-
Notifications
You must be signed in to change notification settings - Fork 91
New LazyControllerFactory #165
New LazyControllerFactory #165
Conversation
Inspired by http://circlical.com/blog/2016/3/9/preparing-for-zend-f, this abstract factory/factory can be used to create controller instances for controllers defining constructor dependencies, using the following rules: - 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. As it implements zend-servicemanager's v3 AbstractFactoryInterface, it may be used as either an abstract factory, or by mapping controller class names to the factory.
* @var string[] | ||
*/ | ||
private $aliases = [ | ||
'Zend\Console\Adapter\AdapterInterface' => 'ConsoleAdapter', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
::class
all these, plz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
::class
need to install all repositories, because need this classes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, ::class
does not cause autoloading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@snapshotpl Not true; PHP will resolve the class name using ::class
even if it cannot autoload the class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use ::class
even with non-existing classes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. So Zend\Console\Adapter\AdapterInterface::class
will be Zend\Console\Adapter\AdapterInterface
string even I autoload it or not?
* | ||
* @var string[] | ||
*/ | ||
private $aliases = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be nice to provide custom aliases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll make it protected so this factory can be extended, then.
$parameters[] = $container->get($type); | ||
} | ||
|
||
return $reflectionClass->newInstanceArgs($parameters); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use http://php.net/manual/en/migration56.new-features.php#migration56.new-features.splat if we support php ^5.6
?
cannot happen with version 3 servicemanager.
} | ||
|
||
$type = $parameter->getClass()->getName(); | ||
$type = array_key_exists($type, $this->aliases) ? $this->aliases[$type] : $type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isset
is faster, right?
- Allows extending in order to add more aliases.
* required for those services with no entry based on the class/interface | ||
* name. | ||
* | ||
* Extend the class if you wish to add to the list. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we get this list from configuration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not add a configuration point for it. This currently lists all "special case" service names, and we recommend that users utilize fully qualified interface and/or class names for their service names.
If a user really wants to add more, they should have to think twice before doing so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I see that https://github.com/zendframework/zend-mvc/pull/165/files#diff-898a3a94db9fcef93eeeb26a23397ecaR139 will resolve many cases. Anyway this feature it's for really lazy developers, and never will be cover all cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Precisely. If you need something more robust, or with more features, you should likely create a custom factory at that point, not extend this one. 😄
- Updated instantiation to use splat operator instead of reflection - switch from array_key_exists to isset Both per @snapshotpl
@snapshotpl Thanks for reminding me about the splat operator; still not used to being able to use PHP 5.6 features! |
That's why we goes into 5.6!, right? |
Extracted parameter resolution to a new method, and modified factory to use array_map with that method to create the list of parameters for instantiating the controller.
return new $requestedName(); | ||
} | ||
|
||
$parameters = array_map(function (ReflectionParameter $parameter) use ($container, $requestedName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read about performance array_map
and foreach
and looks a little bit slower. use
also decrease readability for me. Maybe foreach
will be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Micro-optimizations with the size of the arrays we have here. But I agree on the readability part; really wish you could do callables using private methods if invoked within the context of the class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've refactored this now. resolveParameter()
now returns a closure over the container and requested name, leaving:
$parameters = array_map(
$this->resolveParameter($container, $requestedName),
$reflectionParameters
);
`resolveParameter()` now accepts the container and requested name, and returns a callback for resolving a parameter to a value, closing over the two provided values. This simplifies the array_map call, making it more readable.
@weierophinney I just realized that this PR has nothing to do with laziness, but is instead centered on "automatic di". Should I open an issue about it, or can you still work on it? |
@Ocramius — What's your idea? A rename? Or a more generic offering in
|
A rename is sufficient. Just want to avoid messy situations with
|
Inspired by http://circlical.com/blog/2016/3/9/preparing-for-zend-f, this abstract factory/factory can be used to create controller instances for controllers defining constructor dependencies, using the following rules:
$config
typehinted as an array will receive the application "config" service (i.e., the merged configuration).$config
will be injected with an empty array.$options
passed to the factory are ignored in all cases, as we cannot make assumptions about which argument(s) they might replace.As it implements zend-servicemanager's v3 AbstractFactoryInterface, it may be used as either an abstract factory, or by mapping controller class names to the factory.