-
Notifications
You must be signed in to change notification settings - Fork 823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use callable as well as creator #10321
use callable as well as creator #10321
Conversation
Looks like a reasonable addition that could be pretty powerful. I think we'd need to see some decent tests here to cover off the use-cases and that injector does indeed create instances as expected this way as well as additions to the documentation. |
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.
Does seems like a good addition
As Dan mentioned, we need tests + docs
The example given in the original description refers to https://github.com/guzzle/guzzle/blob/master/src/HandlerStack.php#L47
OK, I will add some. |
d159f0f
to
cbe62c9
Compare
Done. |
I'm in two minds about this one. Apologies in advance for any incorrect assumptions I've made here, this is a fairly complex topic I sort of like this idea here, though I am quite worried this will lead to making the yml config even more confusing than it is The comment about It's kind of messy having It seems like this would be far cleaner that if instead of essentially high-jacking the keys 'class' and 'constructor', there were additional keys available 'method' and 'arguments' (likely we'd use different key names if we were to do this) I think this work better if we didn't do this in InjectionCreator as it implements the Factory interface, which we cannot update without breaking API, so would need to happen in CMS5. However it might work if this was instead done directly in if (isset($spec['method'])) {
$method = $spec['method'];
if (!is_callable($method)) {
throw new InjectorNotFoundException("Method '{$method}' is not callable");
}
$arguments = $spec['arguments'] ?? [];
$object = call_user_func_array($method, $arguments);
if (!is_object($object)) {
throw new InjectorNotFoundException("Method '{$method}' does not return an object");
}
} else {
$factory = isset($spec['factory']) ? $this->get($spec['factory']) : $this->getObjectCreator();
$object = $factory->create($class, $constructorParams);
} At the same time though, I'm also not keen on this change because it's changing what SilverStripe\Core\Injector\Injector:
LogMiddleware:
class: 'GuzzleHttp\Middleware::log'
constructor: ['%$Psr\Log\LoggerInterface', '%$GuzzleHttp\MessageFormatter', 'info']
GuzzleHttp\HandlerStack:
class: 'GuzzleHttp\HandlerStack::create'
calls:
- [push, ['%$LogMiddleware']]
GuzzleHttp\Client:
constructor:
-
handler: '%$GuzzleHttp\HandlerStack' GuzzleHttp\Middleware::log returns a callable, not a class Therefore I'm not really sure where to go with this one. @dhensby @maxime-rainville @GuySartorelli do you have any views on this one? |
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.
Need to decide if this is something we want to proceed with at this stage
This seems like a really useful and flexible feature on the surface, but it does feel a bit too flexible. I would expect the result of |
Nope, object always: |
Yes, a bit, I think that factory setting is a better place for it. I just didn't want to bring new things and concepts just wanted to use what I found here. IMHO a
Not exactly, IMHO in this exactly case GuzzleHttp\Middleware went unnecessary too far generalizing return type here. In fact those helper methods always return a Closure instance. So from Injector point of view everything is correct, still right. |
OK, if we're validating that things are Objects I'm less concerned because it means that the meaning of There isn't currently any type checking within Injector.php that Injector::instantiate() $factory = isset($spec['factory']) ? $this->get($spec['factory']) : $this->getObjectCreator();
if (($factory instanceof Factory) || !isset($spec['factory_method']) || !method_exists($spec['factory'], $spec['factory_method'])) {
// retain existing functionality
$object = $factory->create($class, $constructorParams);
} else {
// new functionality
$method = $spec['factory_method'];
$object = $factory->$method($class, $constructorParams);
if (!is_object($object)) {
$class = get_class($factory);
throw new InjectorNotFoundException("Method '{$method}' on '{$class}' does not return an object");
}
} How does this sound? |
cbe62c9
to
6a09208
Compare
Quite well. I added support for static factory method, not every factory class allows instantiation. So here it is. |
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's looking good, a few more changes to make
b113826
to
6b83a87
Compare
6b83a87
to
5a22b33
Compare
Done. |
Just double checking my understanding of the use case here. The idea is that occasionally you will use a library that expect you to call a "factory method" on class to create an instance rather than a regular constructor. If you want to instantiate that class with Injector, your only option right now is to create a proper Factory class. The point of this PR is to update the Injector so that we can simply tell Injector to use the factory method and avoid creating a full factory class. If my understanding, this seems like a useful approach and something I would support. |
That's right. A lot of libs use factory design patterns having it's own ready to use factory methods (like a GuzzleHttp from the example) so factory (class) is already there just method name differs than the one imposed by Factory interface. |
Just noting for posterity that symfony simply allows setting the https://symfony.com/doc/current/service_container/factories.html#static-factories In a later version of framework, it might be good to align our approach with symfony's, remove the php It would also be good to rename |
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.
Great work.
Have tested locally to confirm everything works as expected
This makes core object creator a more universal factory and its a natural consequence of:
silverstripe-framework/src/Core/Injector/Injector.php
Lines 430 to 432 in cb37869
Example of extended syntax usage: