From baaeee3982d4b296bc87863c72264d9663256e68 Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Thu, 23 May 2019 05:45:24 +1000 Subject: [PATCH 1/6] Make sure renderer is PhpRenderer before getting plugin Plus some typehinting fixes --- src/ZendViewRenderer.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ZendViewRenderer.php b/src/ZendViewRenderer.php index 23e95f2..7d7f77e 100644 --- a/src/ZendViewRenderer.php +++ b/src/ZendViewRenderer.php @@ -1,7 +1,7 @@ terminate()) { throw new Exception\RenderingException('Cannot render; encountered a child marked terminal'); @@ -219,7 +220,8 @@ private function renderModel( $child = $this->mergeViewModel($child->getTemplate(), $child); - if ($child !== $root) { + if ($renderer instanceof PhpRenderer && $child !== $root) { + /** @var Helper\ViewModel $viewModelHelper */ $viewModelHelper = $renderer->plugin(Helper\ViewModel::class); $viewModelHelper->setRoot($root); } From 6d7f4b39bbd93ae675e16dbc513084d1d356e56e Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Tue, 28 May 2019 06:20:22 +1000 Subject: [PATCH 2/6] Drop fallback zend-view renderer setup from template renderer --- src/ZendViewRenderer.php | 91 ++++-------------------------- test/ZendViewRendererTest.php | 103 +++++++++++++--------------------- 2 files changed, 50 insertions(+), 144 deletions(-) diff --git a/src/ZendViewRenderer.php b/src/ZendViewRenderer.php index 7d7f77e..209d896 100644 --- a/src/ZendViewRenderer.php +++ b/src/ZendViewRenderer.php @@ -19,7 +19,6 @@ use Zend\View\Model\ViewModel; use Zend\View\Renderer\PhpRenderer; use Zend\View\Renderer\RendererInterface; -use Zend\View\Resolver\AggregateResolver; use function get_class; use function gettype; @@ -32,11 +31,6 @@ * Template implementation bridging zendframework/zend-view. * * This implementation provides additional capabilities. - * - * First, it always ensures the resolver is an AggregateResolver, pushing any - * non-Aggregate into a new AggregateResolver instance. Additionally, it always - * registers a NamespacedPathStackResolver at priority 0 (lower than - * default) in the Aggregate to ensure we can add and resolve namespaced paths. */ class ZendViewRenderer implements TemplateRendererInterface { @@ -64,35 +58,27 @@ class ZendViewRenderer implements TemplateRendererInterface * Allows specifying the renderer to use (any zend-view renderer is * allowed), and optionally also the layout. * + * Renderer is expected to be already configured with NamespacedPathStackResolver, + * typically in AggregateResolver at priority 0 (lower than default), to + * ensure we can add and resolve namespaced paths. + * * The layout may be: * * - a string layout name * - a ModelInterface instance representing the layout * - * If no renderer is provided, a default PhpRenderer instance is created; - * omitting the layout indicates no layout should be used by default when + * Omitting the layout indicates no layout should be used by default when * rendering. * - * @param null|RendererInterface $renderer + * @param RendererInterface $renderer + * @param NamespacedPathStackResolver $resolver * @param null|string|ModelInterface $layout - * @param null|string $defaultSuffix The default template file suffix, if any * @throws Exception\InvalidArgumentException for invalid $layout types */ - public function __construct(RendererInterface $renderer = null, $layout = null, string $defaultSuffix = null) + public function __construct(RendererInterface $renderer, NamespacedPathStackResolver $resolver, $layout = null) { - if (null === $renderer) { - $renderer = $this->createRenderer(); - $resolver = $renderer->resolver(); - } else { - $resolver = $renderer->resolver(); - if (! $resolver instanceof AggregateResolver) { - $aggregate = $this->getDefaultResolver(); - $aggregate->attach($resolver); - $resolver = $aggregate; - } elseif (! $this->hasNamespacedResolver($resolver)) { - $this->injectNamespacedResolver($resolver); - } - } + $this->renderer = $renderer; + $this->resolver = $resolver; if ($layout && is_string($layout)) { $model = new ViewModel(); @@ -108,11 +94,6 @@ public function __construct(RendererInterface $renderer = null, $layout = null, )); } - $this->renderer = $renderer; - $this->resolver = $this->getNamespacedResolver($resolver); - if (null !== $defaultSuffix) { - $this->resolver->setDefaultSuffix($defaultSuffix); - } $this->layout = $layout; } @@ -240,58 +221,6 @@ private function renderModel( return $renderer->render($model); } - /** - * Returns a PhpRenderer object - */ - private function createRenderer() : PhpRenderer - { - $renderer = new PhpRenderer(); - $renderer->setResolver($this->getDefaultResolver()); - return $renderer; - } - - /** - * Get the default resolver - */ - private function getDefaultResolver() : AggregateResolver - { - $resolver = new AggregateResolver(); - $this->injectNamespacedResolver($resolver); - return $resolver; - } - - /** - * Attaches a new NamespacedPathStackResolver to the AggregateResolver - * - * A priority of 0 is used, to ensure it is the last queried. - */ - private function injectNamespacedResolver(AggregateResolver $aggregate) : void - { - $aggregate->attach(new NamespacedPathStackResolver(), 0); - } - - private function hasNamespacedResolver(AggregateResolver $aggregate) : bool - { - foreach ($aggregate as $resolver) { - if ($resolver instanceof NamespacedPathStackResolver) { - return true; - } - } - - return false; - } - - private function getNamespacedResolver(AggregateResolver $aggregate) : ?NamespacedPathStackResolver - { - foreach ($aggregate as $resolver) { - if ($resolver instanceof NamespacedPathStackResolver) { - return $resolver; - } - } - - return null; - } - /** * Merge global/template parameters with provided view model. * diff --git a/test/ZendViewRendererTest.php b/test/ZendViewRendererTest.php index 049b010..389aa6c 100644 --- a/test/ZendViewRendererTest.php +++ b/test/ZendViewRendererTest.php @@ -1,7 +1,7 @@ resolver = new TemplatePathStack(); - $this->render = new PhpRenderer(); - $this->render->setResolver($this->resolver); + $this->resolver = new NamespacedPathStackResolver(); + $this->renderer = new PhpRenderer(); + $this->renderer->setResolver($this->resolver); } public function assertTemplatePath($path, TemplatePath $templatePath, $message = null) @@ -82,33 +85,19 @@ public function assertEqualTemplatePath(TemplatePath $expected, TemplatePath $re } } - public function testCanPassRendererToConstructor() - { - $renderer = new ZendViewRenderer($this->render); - $this->assertInstanceOf(ZendViewRenderer::class, $renderer); - $this->assertAttributeSame($this->render, 'renderer', $renderer); - } - - public function testInstantiatingWithoutEngineLazyLoadsOne() - { - $renderer = new ZendViewRenderer(); - $this->assertInstanceOf(ZendViewRenderer::class, $renderer); - $this->assertAttributeInstanceOf(PhpRenderer::class, 'renderer', $renderer); - } - public function testInstantiatingWithInvalidLayout() { $this->expectException(InvalidArgumentException::class); - new ZendViewRenderer(null, []); + new ZendViewRenderer($this->renderer, $this->resolver, []); } public function testCanAddPathWithEmptyNamespace() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $paths = $renderer->getPaths(); - $this->assertInternalType('array', $paths); + $this->assertIsArray($paths); $this->assertCount(1, $paths); $this->assertTemplatePath(__DIR__ . '/TestAsset' . DIRECTORY_SEPARATOR, $paths[0]); $this->assertTemplatePathString(__DIR__ . '/TestAsset' . DIRECTORY_SEPARATOR, $paths[0]); @@ -117,10 +106,10 @@ public function testCanAddPathWithEmptyNamespace() public function testCanAddPathWithNamespace() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset', 'test'); $paths = $renderer->getPaths(); - $this->assertInternalType('array', $paths); + $this->assertIsArray($paths); $this->assertCount(1, $paths); $this->assertTemplatePath(__DIR__ . '/TestAsset' . DIRECTORY_SEPARATOR, $paths[0]); $this->assertTemplatePathString(__DIR__ . '/TestAsset' . DIRECTORY_SEPARATOR, $paths[0]); @@ -129,7 +118,7 @@ public function testCanAddPathWithNamespace() public function testDelegatesRenderingToUnderlyingImplementation() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'zendview'; $result = $renderer->render('zendview', [ 'name' => $name ]); @@ -159,7 +148,7 @@ public function invalidParameterValues() */ public function testRenderRaisesExceptionForInvalidParameterTypes($params) { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $this->expectException(InvalidArgumentException::class); $renderer->render('foo', $params); @@ -167,7 +156,7 @@ public function testRenderRaisesExceptionForInvalidParameterTypes($params) public function testCanRenderWithNullParams() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $result = $renderer->render('zendview-null', null); $content = file_get_contents(__DIR__ . '/TestAsset/zendview-null.phtml'); @@ -195,7 +184,7 @@ public function objectParameterValues() */ public function testCanRenderWithParameterObjects($params, $search) { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $result = $renderer->render('zendview', $params); $this->assertContains($search, $result); @@ -209,7 +198,7 @@ public function testCanRenderWithParameterObjects($params, $search) */ public function testWillRenderContentInLayoutPassedToConstructor() { - $renderer = new ZendViewRenderer(null, 'zendview-layout'); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, 'zendview-layout'); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'zendview'; $result = $renderer->render('zendview', [ 'name' => $name ]); @@ -222,7 +211,7 @@ public function testWillRenderContentInLayoutPassedToConstructor() public function testSharedParameterIsAvailableInLayout() { - $renderer = new ZendViewRenderer(null, 'zendview-layout-variable'); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, 'zendview-layout-variable'); $renderer->addPath(__DIR__ . '/TestAsset'); $title = uniqid('ZendViewTitle', true); $renderer->addDefaultParam($renderer::TEMPLATE_ALL, 'title', $title); @@ -241,7 +230,7 @@ public function testSharedParameterIsAvailableInLayout() public function testTemplateDefaultParameterIsNotAvailableInLayout() { - $renderer = new ZendViewRenderer(null, 'zendview-layout-variable'); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, 'zendview-layout-variable'); $renderer->addPath(__DIR__ . '/TestAsset'); $title = uniqid('ZendViewTitle', true); $renderer->addDefaultParam('zendview', 'title', $title); @@ -260,7 +249,7 @@ public function testTemplateDefaultParameterIsNotAvailableInLayout() public function testLayoutTemplateDefaultParameterIsAvailableInLayout() { - $renderer = new ZendViewRenderer(null, 'zendview-layout-variable'); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, 'zendview-layout-variable'); $renderer->addPath(__DIR__ . '/TestAsset'); $title = uniqid('ZendViewTitle', true); $name = uniqid('ZendViewName', true); @@ -282,7 +271,7 @@ public function testLayoutTemplateDefaultParameterIsAvailableInLayout() public function testVariableInProvidedLayoutViewModelOverridesTemplateDefaultParameter() { - $renderer = new ZendViewRenderer(null); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $titleToBeOverriden = uniqid('ZendViewTitleToBeOverriden', true); $title = uniqid('ZendViewTitle', true); @@ -308,7 +297,7 @@ public function testVariableInProvidedLayoutViewModelOverridesTemplateDefaultPar public function testTemplateDefaultParameterIsAvailableInLayoutProvidedWithViewModel() { - $renderer = new ZendViewRenderer(null); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $title = uniqid('ZendViewTitle', true); $name = uniqid('ZendViewName', true); @@ -336,7 +325,7 @@ public function testTemplateDefaultParameterIsAvailableInLayoutProvidedWithViewM */ public function testWillRenderContentInLayoutPassedDuringRendering() { - $renderer = new ZendViewRenderer(null); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'zendview'; $result = $renderer->render('zendview', [ 'name' => $name, 'layout' => 'zendview-layout' ]); @@ -353,7 +342,7 @@ public function testWillRenderContentInLayoutPassedDuringRendering() */ public function testLayoutPassedWhenRenderingOverridesLayoutPassedToConstructor() { - $renderer = new ZendViewRenderer(null, 'zendview-layout'); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, 'zendview-layout'); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'zendview'; $result = $renderer->render('zendview', [ 'name' => $name, 'layout' => 'zendview-layout2' ]); @@ -373,7 +362,7 @@ public function testCanPassViewModelForLayoutToConstructor() $layout = new ViewModel(); $layout->setTemplate('zendview-layout'); - $renderer = new ZendViewRenderer(null, $layout); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, $layout); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'zendview'; $result = $renderer->render('zendview', [ 'name' => $name ]); @@ -392,7 +381,7 @@ public function testCanPassViewModelForLayoutParameterWhenRendering() $layout = new ViewModel(); $layout->setTemplate('zendview-layout2'); - $renderer = new ZendViewRenderer(null, 'zendview-layout'); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, 'zendview-layout'); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'zendview'; $result = $renderer->render('zendview', [ 'name' => $name, 'layout' => $layout ]); @@ -411,7 +400,7 @@ public function testDisableLayoutOnRender() $layout = new ViewModel(); $layout->setTemplate('zendview-layout'); - $renderer = new ZendViewRenderer(null, $layout); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, $layout); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'zendview'; @@ -434,7 +423,7 @@ public function testDisableLayoutViaDefaultParameter() $layout = new ViewModel(); $layout->setTemplate('zendview-layout'); - $renderer = new ZendViewRenderer(null, $layout); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver, $layout); $renderer->addPath(__DIR__ . '/TestAsset'); $renderer->addDefaultParam(TemplateRendererInterface::TEMPLATE_ALL, 'layout', false); @@ -453,7 +442,7 @@ public function testDisableLayoutViaDefaultParameter() */ public function testProperlyResolvesNamespacedTemplate() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset/test', 'test'); $expected = file_get_contents(__DIR__ . '/TestAsset/test/test.phtml'); @@ -464,7 +453,7 @@ public function testProperlyResolvesNamespacedTemplate() public function testAddParameterToOneTemplate() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'ZendView'; $renderer->addDefaultParam('zendview', 'name', $name); @@ -477,7 +466,7 @@ public function testAddParameterToOneTemplate() public function testAddSharedParameters() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'ZendView'; $renderer->addDefaultParam($renderer::TEMPLATE_ALL, 'name', $name); @@ -494,7 +483,7 @@ public function testAddSharedParameters() public function testOverrideSharedParametersPerTemplate() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'Zend'; $name2 = 'View'; @@ -526,7 +515,7 @@ public function useArrayOrViewModel() */ public function testOverrideSharedParametersAtRender($viewAsModel) { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $name = 'Zend'; $name2 = 'View'; @@ -543,7 +532,7 @@ public function testOverrideSharedParametersAtRender($viewAsModel) public function testWillRenderAViewModel() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $viewModel = new ViewModel(['name' => 'Zend']); @@ -557,7 +546,7 @@ public function testWillRenderAViewModel() public function testCanRenderWithChildViewModel() { $path = __DIR__ . '/TestAsset'; - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath($path); $viewModelChild = new ViewModel(); @@ -586,7 +575,7 @@ public function testRenderChildWithDefaultParameter() { $name2 = 'Foo'; - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $renderer->addDefaultParam('zendview-2', 'name', $name2); @@ -607,21 +596,9 @@ public function testRenderChildWithDefaultParameter() static::assertEquals($content, $result); } - public function testCanRenderWithCustomDefaultSuffix() - { - $name = 'zend-custom-suffix'; - $suffix = 'pht'; - $renderer = new ZendViewRenderer(null, null, $suffix); - $renderer->addPath(__DIR__ . '/TestAsset'); - $result = $renderer->render('zendview-custom-suffix', ['name' => $name]); - $content = file_get_contents(__DIR__ . '/TestAsset/zendview-custom-suffix.' . $suffix); - $content = str_replace('', $name, $content); - $this->assertEquals($content, $result); - } - public function testChangeLayoutInTemplate() { - $renderer = new ZendViewRenderer(); + $renderer = new ZendViewRenderer($this->renderer, $this->resolver); $renderer->addPath(__DIR__ . '/TestAsset'); $result = $renderer->render('zendview-change-layout', ['layout' => 'zendview-layout']); From e4b69cdb83ebc30e275987b879eaf7869ddf9fb2 Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Tue, 28 May 2019 07:11:24 +1000 Subject: [PATCH 3/6] Fix template renderer factory --- src/ZendViewRendererFactory.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ZendViewRendererFactory.php b/src/ZendViewRendererFactory.php index 05c8b62..971acbf 100644 --- a/src/ZendViewRendererFactory.php +++ b/src/ZendViewRendererFactory.php @@ -66,6 +66,16 @@ public function __invoke(ContainerInterface $container) : ZendViewRenderer 100 ); + $nsPathResolver = new NamespacedPathStackResolver(); + // Set default suffix + if (isset($config['extension'])) { + $nsPathResolver->setDefaultSuffix($config['extension']); + } + $resolver->attach( + $nsPathResolver, + 0 + ); + // Create or retrieve the renderer from the container $renderer = $container->has(PhpRenderer::class) ? $container->get(PhpRenderer::class) @@ -75,9 +85,8 @@ public function __invoke(ContainerInterface $container) : ZendViewRenderer // Inject helpers $this->injectHelpers($renderer, $container); - $defaultSuffix = $config['extension'] ?? $config['default_suffix'] ?? null; // Inject renderer - $view = new ZendViewRenderer($renderer, $config['layout'] ?? null, $defaultSuffix); + $view = new ZendViewRenderer($renderer, $nsPathResolver, $config['layout'] ?? null); // Add template paths $allPaths = isset($config['paths']) && is_array($config['paths']) ? $config['paths'] : []; From 9a81c853d794dbf02760d44feee583e291a9fe0c Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Tue, 4 Jun 2019 14:32:45 +1000 Subject: [PATCH 4/6] Drop test for removed `default_suffix` parameter --- test/ZendViewRendererFactoryTest.php | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/test/ZendViewRendererFactoryTest.php b/test/ZendViewRendererFactoryTest.php index dcfd561..716137a 100644 --- a/test/ZendViewRendererFactoryTest.php +++ b/test/ZendViewRendererFactoryTest.php @@ -278,34 +278,6 @@ public function testConfiguresCustomDefaultSuffix() $this->assertEquals('php', $resolver->getDefaultSuffix()); } - public function testConfiguresDeprecatedDefaultSuffix() - { - $config = [ - 'templates' => [ - 'default_suffix' => 'php', - ], - ]; - - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); - - $factory = new ZendViewRendererFactory(); - $view = $factory($this->container->reveal()); - - $r = new ReflectionProperty($view, 'resolver'); - $r->setAccessible(true); - $resolver = $r->getValue($view); - - $this->assertInstanceOf( - NamespacedPathStackResolver::class, - $resolver, - 'Expected NamespacedPathStackResolver not found!' - ); - $this->assertEquals('php', $resolver->getDefaultSuffix()); - } - public function testInjectsCustomHelpersIntoHelperManager() { $this->container->has('config')->willReturn(false); From fd65be08557e4dd4cf63c6dc5914c78a82b13423 Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Sun, 9 Jun 2019 04:04:50 +1000 Subject: [PATCH 5/6] Split ZendViewRendererFactory into multiple services --- src/HelperPluginManagerFactory.php | 45 ++++- src/NamespacedPathStackResolverFactory.php | 6 +- src/PhpRendererFactory.php | 44 +++++ src/ZendViewRendererFactory.php | 96 +--------- test/HelperPluginManagerFactoryTest.php | 32 +++- ...NamespacedPathStackResolverFactoryTest.php | 55 ++++++ test/PhpRendererFactoryTest.php | 131 ++++++++++++++ test/ZendViewRendererFactoryTest.php | 170 +++--------------- 8 files changed, 328 insertions(+), 251 deletions(-) create mode 100644 src/PhpRendererFactory.php create mode 100644 test/NamespacedPathStackResolverFactoryTest.php create mode 100644 test/PhpRendererFactoryTest.php diff --git a/src/HelperPluginManagerFactory.php b/src/HelperPluginManagerFactory.php index 9cfea01..a2f3fee 100644 --- a/src/HelperPluginManagerFactory.php +++ b/src/HelperPluginManagerFactory.php @@ -1,7 +1,7 @@ configureServiceManager($manager); } + $this->injectHelpers($manager, $container); return $manager; } + + /** + * Inject helpers into the HelperPhpRenderer instance. + * + * If a HelperPluginManager instance is present in the container, uses that; + * otherwise, instantiates one. + * + * In each case, injects with the custom url/serverurl implementations. + * + * @throws Exception\MissingHelperException + */ + private function injectHelpers(HelperPluginManager $helpers, ContainerInterface $container) : void + { + $helpers->setAlias('url', BaseUrlHelper::class); + $helpers->setAlias('Url', BaseUrlHelper::class); + $helpers->setFactory(BaseUrlHelper::class, static function () use ($container) { + if (! $container->has(BaseUrlHelper::class)) { + throw new Exception\MissingHelperException(sprintf( + 'An instance of %s is required in order to create the "url" view helper; not found', + BaseUrlHelper::class + )); + } + return new UrlHelper($container->get(BaseUrlHelper::class)); + }); + + $helpers->setAlias('serverurl', BaseServerUrlHelper::class); + $helpers->setAlias('serverUrl', BaseServerUrlHelper::class); + $helpers->setAlias('ServerUrl', BaseServerUrlHelper::class); + $helpers->setFactory(BaseServerUrlHelper::class, static function () use ($container) { + if (! $container->has(BaseServerUrlHelper::class)) { + throw new Exception\MissingHelperException(sprintf( + 'An instance of %s is required in order to create the "url" view helper; not found', + BaseServerUrlHelper::class + )); + } + return new ServerUrlHelper($container->get(BaseServerUrlHelper::class)); + }); + } } diff --git a/src/NamespacedPathStackResolverFactory.php b/src/NamespacedPathStackResolverFactory.php index 30cf238..17b2ed9 100644 --- a/src/NamespacedPathStackResolverFactory.php +++ b/src/NamespacedPathStackResolverFactory.php @@ -11,7 +11,7 @@ use Psr\Container\ContainerInterface; -class NamespacedPathStackResolverFactory +final class NamespacedPathStackResolverFactory { public function __invoke(ContainerInterface $container) : NamespacedPathStackResolver { @@ -19,8 +19,8 @@ public function __invoke(ContainerInterface $container) : NamespacedPathStackRes $config = $config['templates'] ?? []; $resolver = new NamespacedPathStackResolver(); - if (! empty($config['default_suffix'])) { - $resolver->setDefaultSuffix($config['default_suffix']); + if (! empty($config['extension'])) { + $resolver->setDefaultSuffix($config['extension']); } return $resolver; diff --git a/src/PhpRendererFactory.php b/src/PhpRendererFactory.php new file mode 100644 index 0000000..f922fd9 --- /dev/null +++ b/src/PhpRendererFactory.php @@ -0,0 +1,44 @@ +has('config') ? $container->get('config') : []; + + $resolver = new AggregateResolver(); + $resolver->attach( + new TemplateMapResolver($config['templates']['map'] ?? []), + 100 + ); + + $nsPathResolver = $container->get(NamespacedPathStackResolver::class); + $resolver->attach( + $nsPathResolver, + 0 + ); + + $renderer = new PhpRenderer(); + $renderer->setResolver($resolver); + + $helpers = $container->get(HelperPluginManager::class); + $renderer->setHelperPluginManager($helpers); + + return $renderer; + } +} diff --git a/src/ZendViewRendererFactory.php b/src/ZendViewRendererFactory.php index 971acbf..26ab619 100644 --- a/src/ZendViewRendererFactory.php +++ b/src/ZendViewRendererFactory.php @@ -10,25 +10,14 @@ namespace Zend\Expressive\ZendView; use Psr\Container\ContainerInterface; -use Zend\Expressive\Helper\ServerUrlHelper as BaseServerUrlHelper; -use Zend\Expressive\Helper\UrlHelper as BaseUrlHelper; -use Zend\View\HelperPluginManager; use Zend\View\Renderer\PhpRenderer; -use Zend\View\Resolver; use function is_array; use function is_numeric; -use function sprintf; /** * Create and return a ZendView template instance. * - * Requires the Zend\Expressive\Router\RouterInterface service (for creating - * the UrlHelper instance). - * - * Optionally requires the Zend\View\HelperPluginManager service; if present, - * will use the service to inject the PhpRenderer instance. - * * Optionally uses the service 'config', which should return an array. This * factory consumes the following structure: * @@ -47,43 +36,16 @@ * ], * ] * - * - * Injects the HelperPluginManager used by the PhpRenderer with zend-expressive - * overrides of the url and serverurl helpers. */ -class ZendViewRendererFactory +final class ZendViewRendererFactory { public function __invoke(ContainerInterface $container) : ZendViewRenderer { $config = $container->has('config') ? $container->get('config') : []; $config = $config['templates'] ?? []; - - // Configuration - $resolver = new Resolver\AggregateResolver(); - $resolver->attach( - new Resolver\TemplateMapResolver($config['map'] ?? []), - 100 - ); - - $nsPathResolver = new NamespacedPathStackResolver(); - // Set default suffix - if (isset($config['extension'])) { - $nsPathResolver->setDefaultSuffix($config['extension']); - } - $resolver->attach( - $nsPathResolver, - 0 - ); - - // Create or retrieve the renderer from the container - $renderer = $container->has(PhpRenderer::class) - ? $container->get(PhpRenderer::class) - : new PhpRenderer(); - $renderer->setResolver($resolver); - - // Inject helpers - $this->injectHelpers($renderer, $container); + $renderer = $container->get(PhpRenderer::class); + $nsPathResolver = $container->get(NamespacedPathStackResolver::class); // Inject renderer $view = new ZendViewRenderer($renderer, $nsPathResolver, $config['layout'] ?? null); @@ -99,56 +61,4 @@ public function __invoke(ContainerInterface $container) : ZendViewRenderer return $view; } - - /** - * Inject helpers into the PhpRenderer instance. - * - * If a HelperPluginManager instance is present in the container, uses that; - * otherwise, instantiates one. - * - * In each case, injects with the custom url/serverurl implementations. - * - * @throws Exception\InvalidContainerException if the $container argument - * does not implement InteropContainerInterface. - * @throws Exception\MissingHelperException - */ - private function injectHelpers(PhpRenderer $renderer, ContainerInterface $container) : void - { - $helpers = $this->retrieveHelperManager($container); - $helpers->setAlias('url', BaseUrlHelper::class); - $helpers->setAlias('Url', BaseUrlHelper::class); - $helpers->setFactory(BaseUrlHelper::class, function () use ($container) { - if (! $container->has(BaseUrlHelper::class)) { - throw new Exception\MissingHelperException(sprintf( - 'An instance of %s is required in order to create the "url" view helper; not found', - BaseUrlHelper::class - )); - } - return new UrlHelper($container->get(BaseUrlHelper::class)); - }); - - $helpers->setAlias('serverurl', BaseServerUrlHelper::class); - $helpers->setAlias('serverUrl', BaseServerUrlHelper::class); - $helpers->setAlias('ServerUrl', BaseServerUrlHelper::class); - $helpers->setFactory(BaseServerUrlHelper::class, function () use ($container) { - if (! $container->has(BaseServerUrlHelper::class)) { - throw new Exception\MissingHelperException(sprintf( - 'An instance of %s is required in order to create the "url" view helper; not found', - BaseServerUrlHelper::class - )); - } - return new ServerUrlHelper($container->get(BaseServerUrlHelper::class)); - }); - - $renderer->setHelperPluginManager($helpers); - } - - private function retrieveHelperManager(ContainerInterface $container) : HelperPluginManager - { - if ($container->has(HelperPluginManager::class)) { - return $container->get(HelperPluginManager::class); - } - - return new HelperPluginManager($container); - } } diff --git a/test/HelperPluginManagerFactoryTest.php b/test/HelperPluginManagerFactoryTest.php index 0d33643..f73df7d 100644 --- a/test/HelperPluginManagerFactoryTest.php +++ b/test/HelperPluginManagerFactoryTest.php @@ -1,7 +1,7 @@ container = $this->prophesize(ServiceManager::class); + $this->container = $this->prophesize(ContainerInterface::class); } public function testCallingFactoryWithNoConfigReturnsHelperPluginManagerInstance() @@ -66,4 +69,25 @@ public function testCallingFactoryWithConfigAllowsAddingHelpers() $this->assertInstanceOf(TestHelper::class, $manager->get('testHelper')); return $manager; } + + public function testInjectsCustomHelpersIntoHelperManager() + { + $this->container->has(ExpressiveHelper\UrlHelper::class)->willReturn(true); + $this->container->get(ExpressiveHelper\UrlHelper::class)->willReturn( + $this->prophesize(ExpressiveHelper\UrlHelper::class)->reveal() + ); + $this->container->has(ExpressiveHelper\ServerUrlHelper::class)->willReturn(true); + $this->container->get(ExpressiveHelper\ServerUrlHelper::class)->willReturn( + $this->prophesize(ExpressiveHelper\ServerUrlHelper::class)->reveal() + ); + + $this->container->has('config')->willReturn(false); + $factory = new HelperPluginManagerFactory(); + $helpers = $factory($this->container->reveal()); + + $this->assertTrue($helpers->has('url')); + $this->assertTrue($helpers->has('serverurl')); + $this->assertInstanceOf(UrlHelper::class, $helpers->get('url')); + $this->assertInstanceOf(ServerUrlHelper::class, $helpers->get('serverurl')); + } } diff --git a/test/NamespacedPathStackResolverFactoryTest.php b/test/NamespacedPathStackResolverFactoryTest.php new file mode 100644 index 0000000..50b18e7 --- /dev/null +++ b/test/NamespacedPathStackResolverFactoryTest.php @@ -0,0 +1,55 @@ +container = $this->prophesize(ContainerInterface::class); + } + + public function canCreateResolver() + { + $factory = new NamespacedPathStackResolverFactory(); + $resolver = $factory($this->container->reveal()); + $this->assertInstanceOf(NamespacedPathStackResolver::class, $resolver); + } + + public function testConfiguresCustomDefaultSuffix() + { + $config = [ + 'templates' => [ + 'extension' => 'php', + ], + ]; + + $this->container->has('config')->willReturn(true); + $this->container->get('config')->willReturn($config); + + $factory = new NamespacedPathStackResolverFactory(); + $resolver = $factory($this->container->reveal()); + $this->assertEquals('php', $resolver->getDefaultSuffix()); + } +} diff --git a/test/PhpRendererFactoryTest.php b/test/PhpRendererFactoryTest.php new file mode 100644 index 0000000..998e36f --- /dev/null +++ b/test/PhpRendererFactoryTest.php @@ -0,0 +1,131 @@ +container = $this->prophesize(ContainerInterface::class); + $this->container->has('config')->willReturn(false); + } + + public function injectContainerService($name, $service) + { + $this->container->has($name)->willReturn(true); + $this->container->get($name)->willReturn( + $service instanceof ObjectProphecy ? $service->reveal() : $service + ); + } + + public function testWillUseHelperPluginManagerFromContainer() + { + $this->injectContainerService( + NamespacedPathStackResolver::class, + new NamespacedPathStackResolver() + ); + $helpers = new HelperPluginManager($this->container->reveal()); + $this->injectContainerService(HelperPluginManager::class, $helpers); + $factory = new PhpRendererFactory(); + $renderer = $factory($this->container->reveal()); + $this->assertSame($helpers, $renderer->getHelperPluginManager()); + } + + public function testConfiguresAggregateResolver() + { + $this->injectContainerService( + HelperPluginManager::class, + new HelperPluginManager($this->container->reveal()) + ); + $nsResolver = new NamespacedPathStackResolver(); + $this->injectContainerService(NamespacedPathStackResolver::class, $nsResolver); + $factory = new PhpRendererFactory(); + $renderer = $factory($this->container->reveal()); + $aggregate = $renderer->resolver(); + $this->assertInstanceOf(AggregateResolver::class, $aggregate); + $this->assertContains($nsResolver, $aggregate, 'Expected NamespacedPathStackResolver not found!'); + $resolver = null; + foreach ($aggregate as $resolver) { + if ($resolver instanceof TemplateMapResolver) { + break; + } + } + $this->assertInstanceOf(TemplateMapResolver::class, $resolver, 'Expected TemplateMapResolver not found!'); + } + + public function testConfiguresTemplateMap() + { + $config = [ + 'templates' => [ + 'map' => [ + 'foo' => 'bar', + 'bar' => 'baz', + ], + ], + ]; + $this->injectContainerService('config', $config); + $this->injectContainerService( + HelperPluginManager::class, + new HelperPluginManager($this->container->reveal()) + ); + $this->injectContainerService( + NamespacedPathStackResolver::class, + new NamespacedPathStackResolver() + ); + $factory = new PhpRendererFactory(); + $renderer = $factory($this->container->reveal()); + $aggregate = $renderer->resolver(); + $this->assertInstanceOf(AggregateResolver::class, $aggregate); + $resolver = false; + foreach ($aggregate as $resolver) { + if ($resolver instanceof TemplateMapResolver) { + break; + } + } + $this->assertInstanceOf(TemplateMapResolver::class, $resolver, 'Expected TemplateMapResolver not found!'); + $this->assertTrue($resolver->has('foo')); + $this->assertEquals('bar', $resolver->get('foo')); + $this->assertTrue($resolver->has('bar')); + $this->assertEquals('baz', $resolver->get('bar')); + } + + public function testWillUseHelperManagerFromContainer() + { + $helpers = new HelperPluginManager($this->container->reveal()); + $this->injectContainerService(HelperPluginManager::class, $helpers); + $this->injectContainerService( + NamespacedPathStackResolver::class, + new NamespacedPathStackResolver() + ); + $factory = new PhpRendererFactory(); + $renderer = $factory($this->container->reveal()); + + $this->assertSame($helpers, $renderer->getHelperPluginManager()); + return $helpers; + } +} diff --git a/test/ZendViewRendererFactoryTest.php b/test/ZendViewRendererFactoryTest.php index 716137a..5468ce7 100644 --- a/test/ZendViewRendererFactoryTest.php +++ b/test/ZendViewRendererFactoryTest.php @@ -14,18 +14,12 @@ use Prophecy\Prophecy\ObjectProphecy; use Prophecy\Prophecy\ProphecyInterface; use ReflectionProperty; -use Zend\Expressive\Helper; use Zend\Expressive\Template\TemplatePath; -use Zend\Expressive\ZendView\ServerUrlHelper; -use Zend\Expressive\ZendView\UrlHelper; use Zend\Expressive\ZendView\ZendViewRenderer; use Zend\Expressive\ZendView\ZendViewRendererFactory; use Zend\Expressive\ZendView\NamespacedPathStackResolver; -use Zend\View\HelperPluginManager; use Zend\View\Model\ModelInterface; use Zend\View\Renderer\PhpRenderer; -use Zend\View\Resolver\AggregateResolver; -use Zend\View\Resolver\TemplateMapResolver; use function sprintf; @@ -41,6 +35,12 @@ class ZendViewRendererFactoryTest extends TestCase public function setUp() { $this->container = $this->prophesize(ContainerInterface::class); + + $nsResolver = new NamespacedPathStackResolver(); + $phpRenderer = new PhpRenderer(); + $phpRenderer->setResolver($nsResolver); + $this->injectContainerService(PhpRenderer::class, $phpRenderer); + $this->injectContainerService(NamespacedPathStackResolver::class, $nsResolver); } public function getConfigurationPaths() @@ -102,13 +102,6 @@ public function assertPathNamespaceContains($expected, $namespace, array $paths, $this->assertContains($expected, $found, $message); } - public function fetchPhpRenderer(ZendViewRenderer $view) - { - $r = new ReflectionProperty($view, 'renderer'); - $r->setAccessible(true); - return $r->getValue($view); - } - public function injectContainerService($name, $service) { $this->container->has($name)->willReturn(true); @@ -117,24 +110,9 @@ public function injectContainerService($name, $service) ); } - public function injectBaseHelpers() - { - $this->injectContainerService( - Helper\UrlHelper::class, - $this->prophesize(Helper\UrlHelper::class) - ); - $this->injectContainerService( - Helper\ServerUrlHelper::class, - $this->prophesize(Helper\ServerUrlHelper::class) - ); - } - public function testCallingFactoryWithNoConfigReturnsZendViewInstance() { $this->container->has('config')->willReturn(false); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); - $this->injectBaseHelpers(); $factory = new ZendViewRendererFactory(); $view = $factory($this->container->reveal()); $this->assertInstanceOf(ZendViewRenderer::class, $view); @@ -160,11 +138,7 @@ public function testConfiguresLayout() 'layout' => 'layout/layout', ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); - $this->injectBaseHelpers(); + $this->injectContainerService('config', $config); $factory = new ZendViewRendererFactory(); $view = $factory($this->container->reveal()); @@ -182,11 +156,7 @@ public function testConfiguresPaths() 'paths' => $this->getConfigurationPaths(), ], ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); - $this->injectBaseHelpers(); + $this->injectContainerService('config', $config); $factory = new ZendViewRendererFactory(); $view = $factory($this->container->reveal()); @@ -214,131 +184,33 @@ public function testConfiguresPaths() $this->assertPathNamespaceContains(__DIR__ . '/TestAsset/three' . $dirSlash, null, $paths); } - public function testConfiguresTemplateMap() + public function testWillUseRendererFromContainer() { - $config = [ - 'templates' => [ - 'map' => [ - 'foo' => 'bar', - 'bar' => 'baz', - ], - ], - ]; - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); - $this->injectBaseHelpers(); + $engine = new PhpRenderer; + $this->container->has('config')->willReturn(false); + $this->injectContainerService(PhpRenderer::class, $engine); + $factory = new ZendViewRendererFactory(); $view = $factory($this->container->reveal()); $r = new ReflectionProperty($view, 'renderer'); $r->setAccessible(true); - $renderer = $r->getValue($view); - $aggregate = $renderer->resolver(); - $this->assertInstanceOf(AggregateResolver::class, $aggregate); - $resolver = false; - foreach ($aggregate as $resolver) { - if ($resolver instanceof TemplateMapResolver) { - break; - } - } - $this->assertInstanceOf(TemplateMapResolver::class, $resolver, 'Expected TemplateMapResolver not found!'); - $this->assertTrue($resolver->has('foo')); - $this->assertEquals('bar', $resolver->get('foo')); - $this->assertTrue($resolver->has('bar')); - $this->assertEquals('baz', $resolver->get('bar')); + $composed = $r->getValue($view); + $this->assertSame($engine, $composed); } - public function testConfiguresCustomDefaultSuffix() + public function testWillUseNamespacedPathStackResolverFromContainer() { - $config = [ - 'templates' => [ - 'extension' => 'php', - ], - ]; - - $this->container->has('config')->willReturn(true); - $this->container->get('config')->willReturn($config); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); + $this->container->has('config')->willReturn(false); + $nsResolver = new NamespacedPathStackResolver(); + $this->injectContainerService(NamespacedPathStackResolver::class, $nsResolver); $factory = new ZendViewRendererFactory(); $view = $factory($this->container->reveal()); $r = new ReflectionProperty($view, 'resolver'); $r->setAccessible(true); - $resolver = $r->getValue($view); - - $this->assertInstanceOf( - NamespacedPathStackResolver::class, - $resolver, - 'Expected NamespacedPathStackResolver not found!' - ); - $this->assertEquals('php', $resolver->getDefaultSuffix()); - } - - public function testInjectsCustomHelpersIntoHelperManager() - { - $this->container->has('config')->willReturn(false); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); - $this->injectBaseHelpers(); - $factory = new ZendViewRendererFactory(); - $view = $factory($this->container->reveal()); - $this->assertInstanceOf(ZendViewRenderer::class, $view); - - $renderer = $this->fetchPhpRenderer($view); - $helpers = $renderer->getHelperPluginManager(); - $this->assertInstanceOf(HelperPluginManager::class, $helpers); - $this->assertTrue($helpers->has('url')); - $this->assertTrue($helpers->has('serverurl')); - $this->assertInstanceOf(UrlHelper::class, $helpers->get('url')); - $this->assertInstanceOf(ServerUrlHelper::class, $helpers->get('serverurl')); - } - - public function testWillUseHelperManagerFromContainer() - { - $this->container->has('config')->willReturn(false); - $this->container->has(PhpRenderer::class)->willReturn(false); - $this->injectBaseHelpers(); - - $helpers = new HelperPluginManager($this->container->reveal()); - $this->container->has(HelperPluginManager::class)->willReturn(true); - $this->container->get(HelperPluginManager::class)->willReturn($helpers); - $factory = new ZendViewRendererFactory(); - $view = $factory($this->container->reveal()); - $this->assertInstanceOf(ZendViewRenderer::class, $view); - - $renderer = $this->fetchPhpRenderer($view); - $this->assertSame($helpers, $renderer->getHelperPluginManager()); - return $helpers; - } - - /** - * @depends testWillUseHelperManagerFromContainer - * - * @param HelperPluginManager $helpers - */ - public function testInjectsCustomHelpersIntoHelperManagerFromContainer(HelperPluginManager $helpers) - { - $this->assertTrue($helpers->has('url')); - $this->assertTrue($helpers->has('serverurl')); - $this->assertInstanceOf(UrlHelper::class, $helpers->get('url')); - $this->assertInstanceOf(ServerUrlHelper::class, $helpers->get('serverurl')); - } - - public function testWillUseRendererFromContainer() - { - $engine = new PhpRenderer; - $this->container->has('config')->willReturn(false); - $this->container->has(HelperPluginManager::class)->willReturn(false); - $this->injectContainerService(PhpRenderer::class, $engine); - - $factory = new ZendViewRendererFactory(); - $view = $factory($this->container->reveal()); - - $composed = $this->fetchPhpRenderer($view); - $this->assertSame($engine, $composed); + $composed = $r->getValue($view); + $this->assertSame($nsResolver, $composed); } } From fab31186541b2104a52ba944ed45fbea64297642 Mon Sep 17 00:00:00 2001 From: Aleksei Khudiakov Date: Sun, 9 Jun 2019 04:06:40 +1000 Subject: [PATCH 6/6] Add new factories to config provider --- src/ConfigProvider.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 69081fa..0b1d786 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -11,6 +11,7 @@ use Zend\Expressive\Template\TemplateRendererInterface; use Zend\View\HelperPluginManager; +use Zend\View\Renderer\PhpRenderer; class ConfigProvider { @@ -30,6 +31,8 @@ public function getDependencies() : array ], 'factories' => [ HelperPluginManager::class => HelperPluginManagerFactory::class, + NamespacedPathStackResolver::class => NamespacedPathStackResolverFactory::class, + PhpRenderer::class => PhpRendererFactory::class, ZendViewRenderer::class => ZendViewRendererFactory::class, ], ];