This repository has been archived by the owner on Jan 29, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 92
/
LazyControllerAbstractFactory.php
185 lines (168 loc) · 6.28 KB
/
LazyControllerAbstractFactory.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
<?php
/**
* @link http://github.com/zendframework/zend-mvc for the canonical source repository
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-mvc/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Mvc\Controller;
use Interop\Container\ContainerInterface;
use ReflectionClass;
use ReflectionParameter;
use Zend\Console\Adapter\AdapterInterface as ConsoleAdapterInterface;
use Zend\Filter\FilterPluginManager;
use Zend\Hydrator\HydratorPluginManager;
use Zend\InputFilter\InputFilterPluginManager;
use Zend\Log\FilterPluginManager as LogFilterManager;
use Zend\Log\FormatterPluginManager as LogFormatterManager;
use Zend\Log\ProcessorPluginManager as LogProcessorManager;
use Zend\Log\WriterPluginManager as LogWriterManager;
use Zend\Serializer\AdapterPluginManager as SerializerAdapterManager;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\AbstractFactoryInterface;
use Zend\Stdlib\DispatchableInterface;
use Zend\Validator\ValidatorPluginManager;
/**
* Reflection-based factory for controllers.
*
* To ease development, this factory may be used for controllers with
* type-hinted arguments that resolve to services in the application
* container; this allows omitting the step of writing a factory for
* each controller.
*
* You may use it as either an abstract factory:
*
* <code>
* 'controllers' => [
* 'abstract_factories' => [
* LazyControllerAbstractFactory::class,
* ],
* ],
* </code>
*
* Or as a factory, mapping a controller class name to it:
*
* <code>
* 'controllers' => [
* 'factories' => [
* MyControllerWithDependencies::class => LazyControllerAbstractFactory::class,
* ],
* ],
* </code>
*
* The latter approach is more explicit, and also more performant.
*
* The factory has the following constraints/features:
*
* - A parameter named `$config` typehinted as an array will receive the
* application "config" service (i.e., the merged configuration).
* - Parameters type-hinted against array, but not named `$config` will
* be injected with an empty array.
* - Scalar parameters will be resolved as null values.
* - If a service cannot be found for a given typehint, the factory will
* raise an exception detailing this.
* - Some services provided by Zend Framework components do not have
* entries based on their class name (for historical reasons); the
* factory contains a map of these class/interface names to the
* corresponding service name to allow them to resolve.
*
* `$options` passed to the factory are ignored in all cases, as we cannot
* make assumptions about which argument(s) they might replace.
*/
class LazyControllerAbstractFactory implements AbstractFactoryInterface
{
/**
* Maps known classes/interfaces to the service that provides them; only
* required for those services with no entry based on the class/interface
* name.
*
* Extend the class if you wish to add to the list.
*
* @var string[]
*/
protected $aliases = [
ConsoleAdapterInterface::class => 'ConsoleAdapter',
FilterPluginManager::class => 'FilterManager',
HydratorPluginManager::class => 'HydratorManager',
InputFilterPluginManager::class => 'InputFilterManager',
LogFilterManager::class => 'LogFilterManager',
LogFormatterManager::class => 'LogFormatterManager',
LogProcessorManager::class => 'LogProcessorManager',
LogWriterManager::class => 'LogWriterManager',
SerializerAdapterManager::class => 'SerializerAdapterManager',
ValidatorPluginManager::class => 'ValidatorManager',
];
/**
* {@inheritDoc}
*
* @return DispatchableInterface
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$reflectionClass = new ReflectionClass($requestedName);
if (null === ($constructor = $reflectionClass->getConstructor())) {
return new $requestedName();
}
$reflectionParameters = $constructor->getParameters();
if (empty($reflectionParameters)) {
return new $requestedName();
}
$parameters = array_map(
$this->resolveParameter($container, $requestedName),
$reflectionParameters
);
return new $requestedName(...$parameters);
}
/**
* {@inheritDoc}
*/
public function canCreate(ContainerInterface $container, $requestedName)
{
if (! class_exists($requestedName)) {
return false;
}
return in_array(DispatchableInterface::class, class_implements($requestedName), true);
}
/**
* Resolve a parameter to a value.
*
* Returns a callback for resolving a parameter to a value.
*
* @param ContainerInterface $container
* @param string $requestedName
* @return callable
*/
private function resolveParameter(ContainerInterface $container, $requestedName)
{
/**
* @param ReflectionClass $parameter
* @return mixed
* @throws ServiceNotFoundException If type-hinted parameter cannot be
* resolved to a service in the container.
*/
return function (ReflectionParameter $parameter) use ($container, $requestedName) {
if ($parameter->isArray()
&& $parameter->getName() === 'config'
&& $container->has('config')
) {
return $container->get('config');
}
if ($parameter->isArray()) {
return [];
}
if (! $parameter->getClass()) {
return;
}
$type = $parameter->getClass()->getName();
$type = isset($this->aliases[$type]) ? $this->aliases[$type] : $type;
if (! $container->has($type)) {
throw new ServiceNotFoundException(sprintf(
'Unable to create controller "%s"; unable to resolve parameter "%s" using type hint "%s"',
$requestedName,
$parameter->getName(),
$type
));
}
return $container->get($type);
};
}
}