From b632ca89126bcae19a90a971a9b5239cb5708949 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Tue, 14 Feb 2017 20:09:41 +0100 Subject: [PATCH 1/2] Replace some container injections by service locators [TwigBundle] Replace container by service locator in ContainerAwareRuntimeLoader [HttpKernel] Replace container by service locator in LazyLoadingFragmentHandler Keep legacy feature working --- UPGRADE-3.3.md | 15 +++++++++++ UPGRADE-4.0.md | 15 +++++++++++ .../EventListener/SessionListener.php | 4 +++ .../EventListener/TestSessionListener.php | 4 +++ .../Resources/config/fragment_renderer.xml | 2 +- .../Resources/config/session.xml | 4 +-- .../FrameworkBundle/Resources/config/test.xml | 4 +-- .../ContainerAwareRuntimeLoader.php | 4 +++ .../Compiler/RuntimeLoaderPass.php | 10 +++---- .../TwigBundle/Resources/config/twig.xml | 6 ++--- .../Tests/ContainerAwareRuntimeLoaderTest.php | 3 +++ .../DependencyInjection/TwigExtensionTest.php | 2 +- src/Symfony/Bundle/TwigBundle/composer.json | 2 +- .../FragmentRendererPass.php | 10 ++++--- .../LazyLoadingFragmentHandler.php | 19 +++++++++++-- .../FragmentRendererPassTest.php | 12 +++------ .../LazyLoadingFragmentHandlerTest.php | 27 ++++++++++++++++++- 17 files changed, 111 insertions(+), 32 deletions(-) diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index 2253bea93b51d..b4ba023169082 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -70,12 +70,21 @@ FrameworkBundle * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass` class has been deprecated and will be removed in 4.0. Use the `Symfony\Component\Form\DependencyInjection\FormPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\SessionListener` class has been deprecated + and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\EventListener\SessionListener` instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener` class has been deprecated + and will be removed in 4.0. Use the `Symfony\Component\HttpKernel\EventListener\TestSessionListener` instead. HttpKernel ----------- * The `Psr6CacheClearer::addPool()` method has been deprecated. Pass an array of pools indexed by name to the constructor instead. + + * The `LazyLoadingFragmentHandler::addRendererService()` method has been deprecated and + will be removed in 4.0. Process ------- @@ -125,6 +134,12 @@ TwigBridge * The `TwigRendererEngine::setEnvironment()` method has been deprecated and will be removed in 4.0. Pass the Twig Environment as second argument of the constructor instead. +TwigBundle +---------- + +* The `ContainerAwareRuntimeLoader` class has been deprecated and will be removed in 4.0. + Use the Twig `Twig_ContainerRuntimeLoader` class instead. + Workflow -------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 1335b0e6e04d3..88e7120afafd0 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -187,6 +187,12 @@ FrameworkBundle * The `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass` class has been removed. Use the `Symfony\Component\Form\DependencyInjection\FormPass` class instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\SessionListener` class has been removed. + Use the `Symfony\Component\HttpKernel\EventListener\SessionListener` instead. + + * The `Symfony\Bundle\FrameworkBundle\EventListener\TestSessionListener` class has been removed. + Use the `Symfony\Component\HttpKernel\EventListener\TestSessionListener` instead. SecurityBundle -------------- @@ -236,6 +242,9 @@ HttpKernel * The `Psr6CacheClearer::addPool()` method has been removed. Pass an array of pools indexed by name to the constructor instead. + + * The `LazyLoadingFragmentHandler::addRendererService()` method has been removed. + Process ------- @@ -276,6 +285,12 @@ Translation * Removed the backup feature from the file dumper classes. +TwigBundle +---------- + +* The `ContainerAwareRuntimeLoader` class has been removed. Use the + Twig `Twig_ContainerRuntimeLoader` class instead. + TwigBridge ---------- diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php index 6c248d8a42880..cb41856b7055c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/SessionListener.php @@ -14,10 +14,14 @@ use Symfony\Component\HttpKernel\EventListener\SessionListener as BaseSessionListener; use Symfony\Component\DependencyInjection\ContainerInterface; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', SessionListener::class, BaseSessionListener::class), E_USER_DEPRECATED); + /** * Sets the session in the request. * * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. */ class SessionListener extends BaseSessionListener { diff --git a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php index b32faa2f05668..e6f5e69ba5993 100644 --- a/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php +++ b/src/Symfony/Bundle/FrameworkBundle/EventListener/TestSessionListener.php @@ -14,10 +14,14 @@ use Symfony\Component\HttpKernel\EventListener\TestSessionListener as BaseTestSessionListener; use Symfony\Component\DependencyInjection\ContainerInterface; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use %s instead.', TestSessionListener::class, BaseTestSessionListener::class), E_USER_DEPRECATED); + /** * TestSessionListener. * * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. */ class TestSessionListener extends BaseTestSessionListener { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml index 3f0d319d9de13..963179c64e99e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml @@ -11,7 +11,7 @@ - + %kernel.debug% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml index 2a4816a2b3bf1..67b10b374e1d7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml @@ -47,9 +47,9 @@ - + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml index ebb311ceb0808..24a3ea37728a8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/test.xml @@ -20,9 +20,9 @@ - - + + diff --git a/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php b/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php index 780454aed986e..eaa4d698bf8c4 100644 --- a/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php +++ b/src/Symfony/Bundle/TwigBundle/ContainerAwareRuntimeLoader.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\TwigBundle; +@trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0. Use the Twig_ContainerRuntimeLoader class instead.'), ContainerAwareRuntimeLoader::class); + use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -18,6 +20,8 @@ * Loads Twig extension runtimes via the service container. * * @author Fabien Potencier + * + * @deprecated since version 3.3, will be removed in 4.0. Use \Twig_ContainerRuntimeLoader instead. */ class ContainerAwareRuntimeLoader implements \Twig_RuntimeLoaderInterface { diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php index 7d0be74fadb78..c7c0bf12a488d 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php @@ -11,9 +11,11 @@ namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; /** * Registers Twig runtime services. @@ -31,17 +33,13 @@ public function process(ContainerBuilder $container) foreach ($container->findTaggedServiceIds('twig.runtime') as $id => $attributes) { $def = $container->getDefinition($id); - if (!$def->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as it can be lazy-loaded.', $id)); - } - if ($def->isAbstract()) { throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as it can be lazy-loaded.', $id)); } - $mapping[$def->getClass()] = $id; + $mapping[$def->getClass()] = new Reference($id); } - $definition->replaceArgument(1, $mapping); + $definition->replaceArgument(0, new ServiceLocatorArgument($mapping)); } } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index f247da8b8aa9f..0a6d03dff68ca 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -138,10 +138,8 @@ - - - - + + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/ContainerAwareRuntimeLoaderTest.php b/src/Symfony/Bundle/TwigBundle/Tests/ContainerAwareRuntimeLoaderTest.php index 1e11e63e48687..93202ca987b16 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/ContainerAwareRuntimeLoaderTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/ContainerAwareRuntimeLoaderTest.php @@ -15,6 +15,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Bundle\TwigBundle\ContainerAwareRuntimeLoader; +/** + * @group legacy + */ class ContainerAwareRuntimeLoaderTest extends TestCase { public function testLoad() diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index 4073653c8b571..ad54b9276716c 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -244,7 +244,7 @@ public function testRuntimeLoader() $container->compile(); $loader = $container->getDefinition('twig.runtime_loader'); - $args = $loader->getArgument(1); + $args = $loader->getArgument(0)->getValues(); $this->assertArrayHasKey('Symfony\Bridge\Twig\Form\TwigRenderer', $args); $this->assertArrayHasKey('FooClass', $args); $this->assertContains('twig.form.renderer', $args); diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 7e33c187878fd..2783ffdde0e07 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -21,7 +21,7 @@ "symfony/twig-bridge": "^3.2.1", "symfony/http-foundation": "~2.8|~3.0", "symfony/http-kernel": "~2.8.16|~3.1.9|^3.2.2", - "twig/twig": "~1.28|~2.0" + "twig/twig": "^1.32|^2.2" }, "require-dev": { "symfony/asset": "~2.8|~3.0", diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php index 6583d0f7d40bc..9479d43e765c4 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/FragmentRendererPass.php @@ -11,9 +11,11 @@ namespace Symfony\Component\HttpKernel\DependencyInjection; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; /** @@ -43,11 +45,9 @@ public function process(ContainerBuilder $container) } $definition = $container->getDefinition($this->handlerService); + $renderers = array(); foreach ($container->findTaggedServiceIds($this->rendererTag) as $id => $tags) { $def = $container->getDefinition($id); - if (!$def->isPublic()) { - throw new InvalidArgumentException(sprintf('The service "%s" must be public as fragment renderer are lazy-loaded.', $id)); - } if ($def->isAbstract()) { throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as fragment renderer are lazy-loaded.', $id)); @@ -63,8 +63,10 @@ public function process(ContainerBuilder $container) } foreach ($tags as $tag) { - $definition->addMethodCall('addRendererService', array($tag['alias'], $id)); + $renderers[$tag['alias']] = new Reference($id); } } + + $definition->replaceArgument(0, new ServiceLocatorArgument($renderers)); } } diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php b/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php index 3559e39e9001d..d6f4dab1418c0 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/LazyLoadingFragmentHandler.php @@ -11,7 +11,7 @@ namespace Symfony\Component\HttpKernel\DependencyInjection; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Psr\Container\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; @@ -23,7 +23,11 @@ class LazyLoadingFragmentHandler extends FragmentHandler { private $container; + /** + * @deprecated since version 3.3, to be removed in 4.0 + */ private $rendererIds = array(); + private $initialized = array(); /** * Constructor. @@ -44,9 +48,13 @@ public function __construct(ContainerInterface $container, RequestStack $request * * @param string $name The service name * @param string $renderer The render service id + * + * @deprecated since version 3.3, to be removed in 4.0 */ public function addRendererService($name, $renderer) { + @trigger_error(sprintf('The %s() method is deprecated since version 3.3 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); + $this->rendererIds[$name] = $renderer; } @@ -55,10 +63,17 @@ public function addRendererService($name, $renderer) */ public function render($uri, $renderer = 'inline', array $options = array()) { + // BC 3.x, to be removed in 4.0 if (isset($this->rendererIds[$renderer])) { $this->addRenderer($this->container->get($this->rendererIds[$renderer])); - unset($this->rendererIds[$renderer]); + + return parent::render($uri, $renderer, $options); + } + + if (!isset($this->initialized[$renderer]) && $this->container->has($renderer)) { + $this->addRenderer($this->container->get($renderer)); + $this->initialized[$renderer] = true; } return parent::render($uri, $renderer, $options); diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/FragmentRendererPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/FragmentRendererPassTest.php index 360f806fea6bf..c33db826ef154 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/FragmentRendererPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/FragmentRendererPassTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; @@ -60,19 +62,13 @@ public function testValidContentRenderer() $renderer = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); $renderer ->expects($this->once()) - ->method('addMethodCall') - ->with('addRendererService', array('foo', 'my_content_renderer')) - ; + ->method('replaceArgument') + ->with(0, $this->equalTo(new ServiceLocatorArgument(array('foo' => new Reference('my_content_renderer'))))); $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); $definition->expects($this->atLeastOnce()) ->method('getClass') ->will($this->returnValue('Symfony\Component\HttpKernel\Tests\DependencyInjection\RendererService')); - $definition - ->expects($this->once()) - ->method('isPublic') - ->will($this->returnValue(true)) - ; $builder = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'getReflectionClass'))->getMock(); $builder->expects($this->any()) diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php index 63090d050340c..0406345d96d68 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php @@ -18,7 +18,11 @@ class LazyLoadingFragmentHandlerTest extends TestCase { - public function test() + /** + * @group legacy + * @expectedDeprecation The Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler::addRendererService() method is deprecated since version 3.3 and will be removed in 4.0. + */ + public function testRenderWithLegacyMapping() { $renderer = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface')->getMock(); $renderer->expects($this->once())->method('getName')->will($this->returnValue('foo')); @@ -38,4 +42,25 @@ public function test() // second call should not lazy-load anymore (see once() above on the get() method) $handler->render('/foo', 'foo'); } + + public function testRender() + { + $renderer = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface')->getMock(); + $renderer->expects($this->once())->method('getName')->will($this->returnValue('foo')); + $renderer->expects($this->any())->method('render')->will($this->returnValue(new Response())); + + $requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock(); + $requestStack->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); + + $container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock(); + $container->expects($this->once())->method('has')->with('foo')->willReturn(true); + $container->expects($this->once())->method('get')->will($this->returnValue($renderer)); + + $handler = new LazyLoadingFragmentHandler($container, $requestStack, false); + + $handler->render('/foo', 'foo'); + + // second call should not lazy-load anymore (see once() above on the get() method) + $handler->render('/foo', 'foo'); + } } From 34eeffbbcf710d1df60eec2f54c96ed26ce2b857 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 17 Feb 2017 20:46:29 +0100 Subject: [PATCH 2/2] [EventDispatcher] Fix abstract event subscribers registration --- .../RegisterListenersPass.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php index ec5e4e8747a02..431ea21a6796a 100644 --- a/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php +++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Compiler pass to register tagged services for an event dispatcher. @@ -105,8 +106,8 @@ public function process(ContainerBuilder $container) } $container->addObjectResource($class); - $r = new \ReflectionClass($class); - $extractingDispatcher->addSubscriber($r->newInstanceWithoutConstructor()); + ExtractingEventDispatcher::$subscriber = $class; + $extractingDispatcher->addSubscriber($extractingDispatcher); foreach ($extractingDispatcher->listeners as $args) { $args[1] = new ClosureProxyArgument($id, $args[1]); $definition->addMethodCall('addListener', $args); @@ -119,12 +120,21 @@ public function process(ContainerBuilder $container) /** * @internal */ -class ExtractingEventDispatcher extends EventDispatcher +class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface { public $listeners = array(); + public static $subscriber; + public function addListener($eventName, $listener, $priority = 0) { $this->listeners[] = array($eventName, $listener[1], $priority); } + + public static function getSubscribedEvents() + { + $callback = array(self::$subscriber, 'getSubscribedEvents'); + + return $callback(); + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy