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

Commit 1728a75

Browse files
committed
No invokable configuration
It's better to define the services properly up front, and not use the `invokables` configuration for the plugin manager. One issue that arises with the `invokables` configuration is that it can override existing factories and aliases, which may not be what is intended. Defining them explicitly ensures that incoming configuration at instantiation can properly override the default services.
1 parent 16a873d commit 1728a75

File tree

3 files changed

+197
-133
lines changed

3 files changed

+197
-133
lines changed

src/Helper/Navigation/PluginManager.php

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace Zend\View\Helper\Navigation;
1111

1212
use Interop\Container\ContainerInterface;
13+
use Zend\ServiceManager\Factory\InvokableFactory;
1314
use Zend\View\HelperPluginManager;
1415

1516
/**
@@ -27,17 +28,27 @@ class PluginManager extends HelperPluginManager
2728
protected $instanceOf = AbstractHelper::class;
2829

2930
/**
30-
* Default configuration.
31+
* Default aliases
3132
*
32-
* @var array
33+
* @var string[]
3334
*/
34-
protected $config = [
35-
'invokables' => [
36-
'breadcrumbs' => Breadcrumbs::class,
37-
'links' => Links::class,
38-
'menu' => Menu::class,
39-
'sitemap' => Sitemap::class,
40-
],
35+
protected $aliases = [
36+
'breadcrumbs' => Breadcrumbs::class,
37+
'links' => Links::class,
38+
'menu' => Menu::class,
39+
'sitemap' => Sitemap::class,
40+
];
41+
42+
/**
43+
* Default factories
44+
*
45+
* @var string[]
46+
*/
47+
protected $factories = [
48+
Breadcrumbs::class => InvokableFactory::class,
49+
Links::class => InvokableFactory::class,
50+
Menu::class => InvokableFactory::class,
51+
Sitemap::class => InvokableFactory::class,
4152
];
4253

4354
/**
@@ -46,15 +57,13 @@ class PluginManager extends HelperPluginManager
4657
*/
4758
public function __construct(ContainerInterface $container, array $config = [])
4859
{
49-
$this->config['initializers'] = [
50-
function ($container, $instance) {
51-
if (! $instance instanceof AbstractHelper) {
52-
continue;
53-
}
54-
55-
$instance->setServiceLocator($container);
56-
},
57-
];
60+
$this->initializers[] = function ($container, $instance) {
61+
if (! $instance instanceof AbstractHelper) {
62+
continue;
63+
}
64+
65+
$instance->setServiceLocator($container);
66+
};
5867

5968
parent::__construct($container, $config);
6069
}

src/HelperPluginManager.php

Lines changed: 157 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use Interop\Container\ContainerInterface;
1313
use Zend\I18n\Translator\TranslatorAwareInterface;
1414
use Zend\ServiceManager\AbstractPluginManager;
15-
use Zend\Stdlib\ArrayUtils;
15+
use Zend\ServiceManager\Factory\InvokableFactory;
1616

1717
/**
1818
* Plugin manager implementation for view helpers
@@ -25,115 +25,159 @@ class HelperPluginManager extends AbstractPluginManager
2525
{
2626
protected $instanceOf = Helper\HelperInterface::class;
2727

28-
protected $config = [
29-
'aliases' => [
30-
'basePath' => 'basepath',
31-
'BasePath' => 'basepath',
32-
'Cycle' => 'cycle',
33-
'declareVars' => 'declarevars',
34-
'DeclareVars' => 'declarevars',
35-
'Doctype' => 'doctype',
36-
'flashMessenger' => 'flashmessenger',
37-
'FlashMessenger' => 'flashmessenger',
38-
'escapeHtml' => 'escapehtml',
39-
'EscapeHtml' => 'escapehtml',
40-
'escapeHtmlAttr' => 'escapehtmlattr',
41-
'EscapeHtmlAttr' => 'escapehtmlattr',
42-
'escapeJs' => 'escapejs',
43-
'EscapeJs' => 'escapejs',
44-
'escapeCss' => 'escapecss',
45-
'EscapeCss' => 'escapecss',
46-
'escapeUrl' => 'escapeurl',
47-
'EscapeUrl' => 'escapeurl',
48-
'Gravatar' => 'gravatar',
49-
'htmlTag' => 'htmltag',
50-
'HtmlTag' => 'htmltag',
51-
'headLink' => 'headlink',
52-
'HeadLink' => 'headlink',
53-
'headMeta' => 'headmeta',
54-
'HeadMeta' => 'headmeta',
55-
'headScript' => 'headscript',
56-
'HeadScript' => 'headscript',
57-
'headStyle' => 'headstyle',
58-
'HeadStyle' => 'headstyle',
59-
'headTitle' => 'headtitle',
60-
'HeadTitle' => 'headtitle',
61-
'htmlFlash' => 'htmlflash',
62-
'HtmlFlash' => 'htmlflash',
63-
'htmlList' => 'htmllist',
64-
'HtmlList' => 'htmllist',
65-
'htmlObject' => 'htmlobject',
66-
'HtmlObject' => 'htmlobject',
67-
'htmlPage' => 'htmlpage',
68-
'HtmlPage' => 'htmlpage',
69-
'htmlQuicktime' => 'htmlquicktime',
70-
'HtmlQuicktime' => 'htmlquicktime',
71-
'Identity' => 'identity',
72-
'inlineScript' => 'inlinescript',
73-
'InlineScript' => 'inlinescript',
74-
'Json' => 'json',
75-
'Layout' => 'layout',
76-
'paginationControl' => 'paginationcontrol',
77-
'PaginationControl' => 'paginationcontrol',
78-
'Partial' => 'partial',
79-
'partialLoop' => 'partialloop',
80-
'PartialLoop' => 'partialloop',
81-
'Placeholder' => 'placeholder',
82-
'renderChildModel' => 'renderchildmodel',
83-
'RenderChildModel' => 'renderchildmodel',
84-
'render_child_model' => 'renderchildmodel',
85-
'renderToPlaceholder' => 'rendertoplaceholder',
86-
'RenderToPlaceholder' => 'rendertoplaceholder',
87-
'serverUrl' => 'serverurl',
88-
'ServerUrl' => 'serverurl',
89-
'Url' => 'url',
90-
'viewModel' => 'viewmodel',
91-
'ViewModel' => 'viewmodel',
92-
'view_model' => 'viewmodel',
93-
],
94-
'factories' => [
95-
'flashmessenger' => Helper\Service\FlashMessengerFactory::class,
96-
'identity' => Helper\Service\IdentityFactory::class,
97-
],
98-
'invokables' => [
99-
// basepath, doctype, and url are set up as factories in the ViewHelperManagerFactory.
100-
// basepath and url are not very useful without their factories, however the doctype
101-
// helper works fine as an invokable. The factory for doctype simply checks for the
102-
// config value from the merged config.
103-
'basepath' => Helper\BasePath::class,
104-
'cycle' => Helper\Cycle::class,
105-
'declarevars' => Helper\DeclareVars::class,
106-
'doctype' => Helper\Doctype::class, // overridden by a factory in ViewHelperManagerFactory
107-
'escapehtml' => Helper\EscapeHtml::class,
108-
'escapehtmlattr' => Helper\EscapeHtmlAttr::class,
109-
'escapejs' => Helper\EscapeJs::class,
110-
'escapecss' => Helper\EscapeCss::class,
111-
'escapeurl' => Helper\EscapeUrl::class,
112-
'gravatar' => Helper\Gravatar::class,
113-
'htmltag' => Helper\HtmlTag::class,
114-
'headlink' => Helper\HeadLink::class,
115-
'headmeta' => Helper\HeadMeta::class,
116-
'headscript' => Helper\HeadScript::class,
117-
'headstyle' => Helper\HeadStyle::class,
118-
'headtitle' => Helper\HeadTitle::class,
119-
'htmlflash' => Helper\HtmlFlash::class,
120-
'htmllist' => Helper\HtmlList::class,
121-
'htmlobject' => Helper\HtmlObject::class,
122-
'htmlpage' => Helper\HtmlPage::class,
123-
'htmlquicktime' => Helper\HtmlQuicktime::class,
124-
'inlinescript' => Helper\InlineScript::class,
125-
'json' => Helper\Json::class,
126-
'layout' => Helper\Layout::class,
127-
'paginationcontrol' => Helper\PaginationControl::class,
128-
'partialloop' => Helper\PartialLoop::class,
129-
'partial' => Helper\Partial::class,
130-
'placeholder' => Helper\Placeholder::class,
131-
'renderchildmodel' => Helper\RenderChildModel::class,
132-
'rendertoplaceholder' => Helper\RenderToPlaceholder::class,
133-
'serverurl' => Helper\ServerUrl::class,
134-
'url' => Helper\Url::class,
135-
'viewmodel' => Helper\ViewModel::class,
136-
],
28+
/**
29+
* Default helper aliases
30+
*
31+
* Most of these are present for legacy purposes, as v2 of the service
32+
* manager normalized names when fetching services.
33+
*
34+
* @var string[]
35+
*/
36+
protected $aliases = [
37+
'basePath' => 'basepath',
38+
'BasePath' => 'basepath',
39+
'basepath' => Helper\BasePath::class,
40+
'Cycle' => 'cycle',
41+
'cycle' => Helper\Cycle::class,
42+
'declareVars' => 'declarevars',
43+
'DeclareVars' => 'declarevars',
44+
'declarevars' => Helper\DeclareVars::class,
45+
'Doctype' => 'doctype',
46+
'doctype' => Helper\Doctype::class, // overridden by a factory in ViewHelperManagerFactory
47+
'escapeCss' => 'escapecss',
48+
'EscapeCss' => 'escapecss',
49+
'escapecss' => Helper\EscapeCss::class,
50+
'escapeHtmlAttr' => 'escapehtmlattr',
51+
'EscapeHtmlAttr' => 'escapehtmlattr',
52+
'escapehtmlattr' => Helper\EscapeHtmlAttr::class,
53+
'escapeHtml' => 'escapehtml',
54+
'EscapeHtml' => 'escapehtml',
55+
'escapehtml' => Helper\EscapeHtml::class,
56+
'escapeJs' => 'escapejs',
57+
'EscapeJs' => 'escapejs',
58+
'escapejs' => Helper\EscapeJs::class,
59+
'escapeUrl' => 'escapeurl',
60+
'EscapeUrl' => 'escapeurl',
61+
'escapeurl' => Helper\EscapeUrl::class,
62+
'flashMessenger' => 'flashmessenger',
63+
'FlashMessenger' => 'flashmessenger',
64+
'Gravatar' => 'gravatar',
65+
'gravatar' => Helper\Gravatar::class,
66+
'headLink' => 'headlink',
67+
'HeadLink' => 'headlink',
68+
'headlink' => Helper\HeadLink::class,
69+
'headMeta' => 'headmeta',
70+
'HeadMeta' => 'headmeta',
71+
'headmeta' => Helper\HeadMeta::class,
72+
'headScript' => 'headscript',
73+
'HeadScript' => 'headscript',
74+
'headscript' => Helper\HeadScript::class,
75+
'headStyle' => 'headstyle',
76+
'HeadStyle' => 'headstyle',
77+
'headstyle' => Helper\HeadStyle::class,
78+
'headTitle' => 'headtitle',
79+
'HeadTitle' => 'headtitle',
80+
'headtitle' => Helper\HeadTitle::class,
81+
'htmlflash' => Helper\HtmlFlash::class,
82+
'htmlFlash' => 'htmlflash',
83+
'HtmlFlash' => 'htmlflash',
84+
'htmllist' => Helper\HtmlList::class,
85+
'htmlList' => 'htmllist',
86+
'HtmlList' => 'htmllist',
87+
'htmlobject' => Helper\HtmlObject::class,
88+
'htmlObject' => 'htmlobject',
89+
'HtmlObject' => 'htmlobject',
90+
'htmlpage' => Helper\HtmlPage::class,
91+
'htmlPage' => 'htmlpage',
92+
'HtmlPage' => 'htmlpage',
93+
'htmlquicktime' => Helper\HtmlQuicktime::class,
94+
'htmlQuicktime' => 'htmlquicktime',
95+
'HtmlQuicktime' => 'htmlquicktime',
96+
'htmltag' => Helper\HtmlTag::class,
97+
'htmlTag' => 'htmltag',
98+
'HtmlTag' => 'htmltag',
99+
'Identity' => 'identity',
100+
'inlinescript' => Helper\InlineScript::class,
101+
'inlineScript' => 'inlinescript',
102+
'InlineScript' => 'inlinescript',
103+
'json' => Helper\Json::class,
104+
'Json' => 'json',
105+
'layout' => Helper\Layout::class,
106+
'Layout' => 'layout',
107+
'paginationcontrol' => Helper\PaginationControl::class,
108+
'paginationControl' => 'paginationcontrol',
109+
'PaginationControl' => 'paginationcontrol',
110+
'partial' => Helper\Partial::class,
111+
'partialloop' => Helper\PartialLoop::class,
112+
'partialLoop' => 'partialloop',
113+
'PartialLoop' => 'partialloop',
114+
'Partial' => 'partial',
115+
'placeholder' => Helper\Placeholder::class,
116+
'Placeholder' => 'placeholder',
117+
'renderchildmodel' => Helper\RenderChildModel::class,
118+
'render_child_model' => 'renderchildmodel',
119+
'renderChildModel' => 'renderchildmodel',
120+
'RenderChildModel' => 'renderchildmodel',
121+
'rendertoplaceholder' => Helper\RenderToPlaceholder::class,
122+
'renderToPlaceholder' => 'rendertoplaceholder',
123+
'RenderToPlaceholder' => 'rendertoplaceholder',
124+
'serverurl' => Helper\ServerUrl::class,
125+
'serverUrl' => 'serverurl',
126+
'ServerUrl' => 'serverurl',
127+
'url' => Helper\Url::class,
128+
'Url' => 'url',
129+
'viewmodel' => Helper\ViewModel::class,
130+
'view_model' => 'viewmodel',
131+
'viewModel' => 'viewmodel',
132+
'ViewModel' => 'viewmodel',
133+
];
134+
135+
/**
136+
* Default factories
137+
*
138+
* basepath, doctype, and url are set up as factories in the ViewHelperManagerFactory.
139+
* basepath and url are not very useful without their factories, however the doctype
140+
* helper works fine as an invokable. The factory for doctype simply checks for the
141+
* config value from the merged config.
142+
*
143+
* @var array
144+
*/
145+
protected $factories = [
146+
'flashmessenger' => Helper\Service\FlashMessengerFactory::class,
147+
'identity' => Helper\Service\IdentityFactory::class,
148+
Helper\BasePath::class => InvokableFactory::class,
149+
Helper\Cycle::class => InvokableFactory::class,
150+
Helper\DeclareVars::class => InvokableFactory::class,
151+
Helper\Doctype::class => InvokableFactory::class, // overridden by a factory in ViewHelperManagerFactory
152+
Helper\EscapeHtml::class => InvokableFactory::class,
153+
Helper\EscapeHtmlAttr::class => InvokableFactory::class,
154+
Helper\EscapeJs::class => InvokableFactory::class,
155+
Helper\EscapeCss::class => InvokableFactory::class,
156+
Helper\EscapeUrl::class => InvokableFactory::class,
157+
Helper\Gravatar::class => InvokableFactory::class,
158+
Helper\HtmlTag::class => InvokableFactory::class,
159+
Helper\HeadLink::class => InvokableFactory::class,
160+
Helper\HeadMeta::class => InvokableFactory::class,
161+
Helper\HeadScript::class => InvokableFactory::class,
162+
Helper\HeadStyle::class => InvokableFactory::class,
163+
Helper\HeadTitle::class => InvokableFactory::class,
164+
Helper\HtmlFlash::class => InvokableFactory::class,
165+
Helper\HtmlList::class => InvokableFactory::class,
166+
Helper\HtmlObject::class => InvokableFactory::class,
167+
Helper\HtmlPage::class => InvokableFactory::class,
168+
Helper\HtmlQuicktime::class => InvokableFactory::class,
169+
Helper\InlineScript::class => InvokableFactory::class,
170+
Helper\Json::class => InvokableFactory::class,
171+
Helper\Layout::class => InvokableFactory::class,
172+
Helper\PaginationControl::class => InvokableFactory::class,
173+
Helper\PartialLoop::class => InvokableFactory::class,
174+
Helper\Partial::class => InvokableFactory::class,
175+
Helper\Placeholder::class => InvokableFactory::class,
176+
Helper\RenderChildModel::class => InvokableFactory::class,
177+
Helper\RenderToPlaceholder::class => InvokableFactory::class,
178+
Helper\ServerUrl::class => InvokableFactory::class,
179+
Helper\Url::class => InvokableFactory::class,
180+
Helper\ViewModel::class => InvokableFactory::class,
137181
];
138182

139183
/**
@@ -154,11 +198,9 @@ class HelperPluginManager extends AbstractPluginManager
154198
*/
155199
public function __construct(ContainerInterface $container, array $config = [])
156200
{
157-
$this->config['initializers'] = [
158-
[$this, 'injectRenderer'],
159-
[$this, 'injectTranslator'],
160-
];
161-
$config = ArrayUtils::merge($this->config, $config);
201+
$this->initializers[] = [$this, 'injectRenderer'];
202+
$this->initializers[] = [$this, 'injectTranslator'];
203+
162204
parent::__construct($container, $config);
163205
}
164206

test/HelperPluginManagerTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
use Zend\Mvc\I18n\Translator as MvcTranslator;
1414
use Zend\ServiceManager\ServiceManager;
1515
use Zend\View\HelperPluginManager;
16+
use Zend\View\Helper\HelperInterface;
17+
use Zend\View\Helper\Url;
1618
use Zend\View\Renderer\PhpRenderer;
1719

1820
/**
@@ -117,4 +119,15 @@ public function testIfHelperIsTranslatorAwareAndBothMvcTranslatorAndTranslatorAr
117119
$helper = $helpers->get('HeadTitle');
118120
$this->assertSame($translator, $helper->getTranslator());
119121
}
122+
123+
public function testCanOverrideAFactoryViaConfigurationPassedToConstructor()
124+
{
125+
$helper = $this->prophesize(HelperInterface::class)->reveal();
126+
$helpers = new HelperPluginManager(new ServiceManager(), ['factories' => [
127+
Url::class => function ($container, $name, array $options = null) use ($helper) {
128+
return $helper;
129+
},
130+
]]);
131+
$this->assertSame($helper, $helpers->get('url'));
132+
}
120133
}

0 commit comments

Comments
 (0)