[7.x] Add container support for variadic constructor arguments#32454
Conversation
|
I guess this won't work for situations like this? public function __construct(Logger $logger, Filter $mainFilter, Filter ...$filters) |
|
Thanks for jumping in, @driesvints!
Gah, you are right. :( That said, I'm not sure how big of an issue it is? As far as I understand it, you cannot contextually bind a class that has more than one instance of the same type. For example, I don't think you can do the following without dropping to a full factory: public function __construct(Logger $logger, Filter $mainFilter, Filter $backupFilter)The solution I had hoped to find already in place was being able to contextually bind based on parameter name instead of type. This seems to currently only work for binding primitives. For example, I'd hoped I'd be able to do this but it does not: interface Logger { }
class MyLogger implements Logger { }
interface Filter { }
class NullFilter implements Filter { }
class ProfanityFilter implements Filter { }
class TooLongFilter implements Filter { }
class Firewall
{
public $logger;
public $filters;
public function __construct(Logger $logger, Filter $mainFilter, Filter $backupFilter)
{
$this->logger = $logger;
$this->filters = [$mainFilter, $backupFilter];
}
}
app()->singleton(Logger::class, MyLogger::class);
app()->when(Firewall::class)->needs('$mainFilter')->give(NullFilter::class);
app()->when(Firewall::class)->needs('$backupFilter')->give(ProfanityFilter::class);
$firewall = app(Firewall::class);My vote: This is a separate issue/limitation with the current Container contextual binding functionality. This PR would enable support for a common variadic case which would be a huge win on its own. If we want to support |
|
@simensen let's see what Taylor says :) |
|
I agree the multiple instances of same type is kind of a separate issue. Do you mind sending in a laravel/docs PR for this? |
|
I'll see if I can get on the docs, sure! |
This PR adds container support for variadic constructor arguments.
See these Twitter discussions for the catalysts for this PR:
The Problem
There is currently no way to support classes that have variadic constructor arguments without breaking out and providing a full factory for a class.
For example:
The only way to do this currently:
It feels heavy handed since we have to manually
makeall four dependencies forFirewallwhen all of them should be able to be autowired. Likewise, add another constructor argument, and the container configuration needs to change, even if the new constructor argument could be autowired.The Solution
My ideal situation is to configure classes like this using contextual binding. I only want to specify the dependencies that cannot be autowired.
To me, that looks like this:
Or... better yet...
This PR makes both of these last two configuration styles possible.
Notable Changes
Container::getContextualConcretecan now return anarray(only a doc change, but changed expectation)Container::resolveClasscan now return anarray(docs already have it returningmixed, but changed expectation)Container::resolveClasswill now return an empty array ([]) if a value is not defined (better fits with how PHP handles variadic functions when no variadic values are actually passed)ContextualBindingBuilder::givecan now accept an array (only a doc change, but changed expectation)Other Thoughts on Implementation
needs/giveis best vs addingneedsVariadic/givesVariadicor something along those lines. This looks cleanest and probably easiest to adopt, but happy to discuss this.Container::resolveClass. I started downresolveVariadicbut quickly scaled back as it was duplicating a lot of code and wasn't sure it was worth it.Related to #26950.