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

New LazyControllerFactory #165

Conversation

weierophinney
Copy link
Member

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.

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',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

::class all these, plz

Copy link
Contributor

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

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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

Copy link
Contributor

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 = [
Copy link
Contributor

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

Copy link
Member Author

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

$type = $parameter->getClass()->getName();
$type = array_key_exists($type, $this->aliases) ? $this->aliases[$type] : $type;
Copy link
Contributor

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.
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

@snapshotpl snapshotpl Jun 23, 2016

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

Copy link
Member Author

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
@weierophinney
Copy link
Member Author

@snapshotpl Thanks for reminding me about the splat operator; still not used to being able to use PHP 5.6 features!

@snapshotpl
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member Author

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 weierophinney merged commit 00ba958 into zendframework:master Jun 23, 2016
weierophinney added a commit that referenced this pull request Jun 23, 2016
weierophinney added a commit that referenced this pull request Jun 23, 2016
@weierophinney weierophinney deleted the feature/lazy-service-controller-factory branch June 23, 2016 20:42
@Ocramius
Copy link
Member

@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?

@weierophinney
Copy link
Member Author

@Ocramius — What's your idea? A rename? Or a more generic offering in
zend--servicemanager?
On Jun 23, 2016 7:39 PM, "Marco Pivetta" notifications@github.com wrote:

@weierophinney https://github.com/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?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#165 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AABlV0WWRFtIntt4GKtWu80bvyqAgGISks5qOyddgaJpZM4I9CNX
.

@Ocramius
Copy link
Member

A rename is sufficient. Just want to avoid messy situations with
documentation later on
On Jun 24, 2016 03:49, "weierophinney" notifications@github.com wrote:

@Ocramius — What's your idea? A rename? Or a more generic offering in
zend--servicemanager?
On Jun 23, 2016 7:39 PM, "Marco Pivetta" notifications@github.com wrote:

@weierophinney https://github.com/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?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<
#165 (comment)
,
or mute the thread
<
https://github.com/notifications/unsubscribe/AABlV0WWRFtIntt4GKtWu80bvyqAgGISks5qOyddgaJpZM4I9CNX

.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#165 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AAJakDtk9DVdI5rx6WcKvAaW1Ch_8r7Iks5qOzengaJpZM4I9CNX
.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants