1313use Zend \View \Model \ViewModel ;
1414use Zend \View \Renderer \RendererInterface ;
1515use Zend \View \Renderer \PhpRenderer ;
16+ use Zend \View \Resolver \AggregateResolver ;
1617use Zend \View \Resolver \ResolverInterface ;
17- use Zend \View \Resolver \TemplatePathStack ;
1818use Zend \Expressive \Exception ;
1919
2020/**
2121 * Template implementation bridging zendframework/zend-view.
22+ *
23+ * This implementation provides additional capabilities.
24+ *
25+ * First, it always ensures the resolver is an AggregateResolver, pushing any
26+ * non-Aggregate into a new AggregateResolver instance. Additionally, it always
27+ * registers a ZendView\NamespacedPathStackResolver at priority 0 (lower than
28+ * default) in the Aggregate to ensure we can add and resolve namespaced paths.
2229 */
2330class ZendView implements TemplateInterface
2431{
@@ -40,7 +47,7 @@ class ZendView implements TemplateInterface
4047 private $ renderer ;
4148
4249 /**
43- * @var ResolverInterface
50+ * @var ZendView\NamespacedPathStackResolver
4451 */
4552 private $ resolver ;
4653
@@ -67,9 +74,17 @@ public function __construct(RendererInterface $renderer = null, $layout = null)
6774 {
6875 if (null === $ renderer ) {
6976 $ renderer = $ this ->createRenderer ();
77+ $ resolver = $ renderer ->resolver ();
78+ } else {
79+ $ resolver = $ renderer ->resolver ();
80+ if (! $ resolver instanceof AggregateResolver) {
81+ $ aggregate = $ this ->getDefaultResolver ();
82+ $ aggregate ->attach ($ resolver );
83+ $ resolver = $ aggregate ;
84+ } elseif (! $ this ->hasNamespacedResolver ($ resolver )) {
85+ $ this ->injectNamespacedResolver ($ resolver );
86+ }
7087 }
71- $ this ->renderer = $ renderer ;
72- $ this ->resolver = $ renderer ->resolver ();
7388
7489 if ($ layout && is_string ($ layout )) {
7590 $ model = new ViewModel ();
@@ -85,29 +100,9 @@ public function __construct(RendererInterface $renderer = null, $layout = null)
85100 ));
86101 }
87102
88- $ this ->layout = $ layout ;
89- }
90-
91- /**
92- * Returns a PhpRenderer object
93- *
94- * @return PhpRenderer
95- */
96- private function createRenderer ()
97- {
98- $ render = new PhpRenderer ();
99- $ render ->setResolver ($ this ->getDefaultResolver ());
100- return $ render ;
101- }
102-
103- /**
104- * Get the default resolver
105- *
106- * @return TemplatePathStack
107- */
108- private function getDefaultResolver ()
109- {
110- return new TemplatePathStack ();
103+ $ this ->renderer = $ renderer ;
104+ $ this ->resolver = $ this ->getNamespacedResolver ($ resolver );
105+ $ this ->layout = $ layout ;
111106 }
112107
113108 /**
@@ -143,10 +138,7 @@ public function render($name, $params = [])
143138 */
144139 public function addPath ($ path , $ namespace = null )
145140 {
146- $ this ->resolver ->addPath ($ path );
147-
148- // Normalize the path to be compliant with the TemplatePathStack
149- $ this ->paths [TemplatePathStack::normalizePath ($ path )] = $ namespace ;
141+ $ this ->resolver ->addPath ($ path , $ namespace );
150142 }
151143
152144 /**
@@ -157,10 +149,20 @@ public function addPath($path, $namespace = null)
157149 public function getPaths ()
158150 {
159151 $ paths = [];
160- foreach ($ this ->resolver ->getPaths () as $ path ) {
161- $ namespace = array_key_exists ($ path , $ this ->paths ) ? $ this ->paths [$ path ] : null ;
162- $ paths [] = new TemplatePath ($ path , $ namespace );
152+
153+ foreach ($ this ->resolver ->getPaths () as $ namespace => $ namespacedPaths ) {
154+ if ($ namespace === ZendView \NamespacedPathStackResolver::DEFAULT_NAMESPACE
155+ || empty ($ namespace )
156+ || is_int ($ namespace )
157+ ) {
158+ $ namespace = null ;
159+ }
160+
161+ foreach ($ namespacedPaths as $ path ) {
162+ $ paths [] = new TemplatePath ($ path , $ namespace );
163+ }
163164 }
165+
164166 return $ paths ;
165167 }
166168
@@ -246,4 +248,68 @@ private function renderModel(ModelInterface $model, RendererInterface $renderer)
246248
247249 return $ renderer ->render ($ model );
248250 }
251+
252+ /**
253+ * Returns a PhpRenderer object
254+ *
255+ * @return PhpRenderer
256+ */
257+ private function createRenderer ()
258+ {
259+ $ renderer = new PhpRenderer ();
260+ $ renderer ->setResolver ($ this ->getDefaultResolver ());
261+ return $ renderer ;
262+ }
263+
264+ /**
265+ * Get the default resolver
266+ *
267+ * @return ZendView\NamespacedPathStackResolver
268+ */
269+ private function getDefaultResolver ()
270+ {
271+ $ resolver = new AggregateResolver ();
272+ $ this ->injectNamespacedResolver ($ resolver );
273+ return $ resolver ;
274+ }
275+
276+ /**
277+ * Attaches a new ZendView\NamespacedPathStackResolver to the AggregateResolver
278+ *
279+ * A priority of 0 is used, to ensure it is the last queried.
280+ *
281+ * @param AggregateResolver $aggregate
282+ */
283+ private function injectNamespacedResolver (AggregateResolver $ aggregate )
284+ {
285+ $ aggregate ->attach (new ZendView \NamespacedPathStackResolver (), 0 );
286+ }
287+
288+ /**
289+ * @param AggregateResolver $aggregate
290+ * @return bool
291+ */
292+ private function hasNamespacedResolver (AggregateResolver $ aggregate )
293+ {
294+ foreach ($ aggregate as $ resolver ) {
295+ if ($ resolver instanceof ZendView \NamespacedPathStackResolver) {
296+ return true ;
297+ }
298+ }
299+
300+ return false ;
301+ }
302+
303+ /**
304+ * @param AggregateResolver $aggregate
305+ * @return null|ZendView\NamespacedPathStackResolver
306+ */
307+ private function getNamespacedResolver (AggregateResolver $ aggregate )
308+ {
309+ foreach ($ aggregate as $ resolver ) {
310+ if ($ resolver instanceof ZendView \NamespacedPathStackResolver) {
311+ return $ resolver ;
312+ }
313+ }
314+ }
249315}
0 commit comments