Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Change ZendViewRenderer to DI only; remove fallback dependency setup #66

Draft
wants to merge 6 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use Zend\Expressive\Template\TemplateRendererInterface;
use Zend\View\HelperPluginManager;
use Zend\View\Renderer\PhpRenderer;

class ConfigProvider
{
Expand All @@ -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,
],
];
Expand Down
45 changes: 43 additions & 2 deletions src/HelperPluginManagerFactory.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-zendviewrenderer for the canonical source repository
* @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (https://www.zend.com)
* @copyright Copyright (c) 2015-2019 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-zendviewrenderer/blob/master/LICENSE.md New BSD License
*/

Expand All @@ -10,10 +10,12 @@
namespace Zend\Expressive\ZendView;

use Psr\Container\ContainerInterface;
use Zend\Expressive\Helper\ServerUrlHelper as BaseServerUrlHelper;
use Zend\Expressive\Helper\UrlHelper as BaseUrlHelper;
use Zend\ServiceManager\Config;
use Zend\View\HelperPluginManager;

class HelperPluginManagerFactory
final class HelperPluginManagerFactory
{
public function __invoke(ContainerInterface $container) : HelperPluginManager
{
Expand All @@ -26,6 +28,45 @@ public function __invoke(ContainerInterface $container) : HelperPluginManager
(new Config($config))->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));
});
}
}
6 changes: 3 additions & 3 deletions src/NamespacedPathStackResolverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@

use Psr\Container\ContainerInterface;

class NamespacedPathStackResolverFactory
final class NamespacedPathStackResolverFactory
{
public function __invoke(ContainerInterface $container) : NamespacedPathStackResolver
{
$config = $container->has('config') ? $container->get('config') : [];
$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;
Expand Down
44 changes: 44 additions & 0 deletions src/PhpRendererFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-zendviewrenderer for the canonical source repository
* @copyright Copyright (c) 2019 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-zendviewrenderer/blob/master/LICENSE.md New BSD License
*/

declare(strict_types=1);

namespace Zend\Expressive\ZendView;

use Psr\Container\ContainerInterface;
use Zend\View\HelperPluginManager;
use Zend\View\Renderer\PhpRenderer;
use Zend\View\Resolver\AggregateResolver;
use Zend\View\Resolver\TemplateMapResolver;

final class PhpRendererFactory
{
public function __invoke(ContainerInterface $container) : PhpRenderer
{
$config = $container->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;
}
}
99 changes: 15 additions & 84 deletions src/ZendViewRenderer.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
/**
* @see https://github.com/zendframework/zend-expressive-zendviewrenderer for the canonical source repository
* @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (https://www.zend.com)
* @copyright Copyright (c) 2015-2019 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-expressive-zendviewrenderer/blob/master/LICENSE.md New BSD License
*/

Expand All @@ -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;
Expand All @@ -32,19 +31,14 @@
* 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
{
use ArrayParametersTrait;
use DefaultParamsTrait;

/**
* @var ViewModel
* @var null|ModelInterface
*/
private $layout;

Expand All @@ -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();
Expand All @@ -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;
}

Expand Down Expand Up @@ -207,6 +188,7 @@ private function renderModel(
$root = $model;
}

/** @var ModelInterface $child */
foreach ($model as $child) {
if ($child->terminate()) {
throw new Exception\RenderingException('Cannot render; encountered a child marked terminal');
Expand All @@ -219,7 +201,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);
}
Expand All @@ -238,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.
*
Expand Down
Loading