Skip to content
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

Dealing with splat operator in a Factory #802

Open
emulgeator opened this issue Jan 20, 2022 · 5 comments
Open

Dealing with splat operator in a Factory #802

emulgeator opened this issue Jan 20, 2022 · 5 comments

Comments

@emulgeator
Copy link

My question would be if it's possible somehow to tell \DI\FactoryInterface::make how to handle a variadic constructor.

So imagine the following class:

class Test
{
    public function __construct(SomeHelper $someHelper, int ...$extraParameters)
    {
    }
}

class Factory
{
    private FactoryInterface $diFactory;

    public function __construct(FactoryInterface $diFactory)
    {
        $this->diFactory = $diFactory;
    }

    public function getTest(int ...$extraParameters): Test
    {
        return $this->diFactory->make(
            Test::class,
            [
                ...$extraParameters,
            ]
        );
    }
}

My factory method obviously wont work this way, and now I can only instantiate the Test class by hand. Is there a way currently to handle this case or if not, would it be possible to support it somehow?

@mnapoli
Copy link
Member

mnapoli commented Jan 26, 2022

🤔 that's a good question, honestly I'm not 100% sure. I would personally test passing the extraParameters parameter as an array, but there's 50% chances it works 😄

@emulgeator
Copy link
Author

Yeah I gave it a try already with eyes closed. Sadly it didn't work

@ramsey
Copy link

ramsey commented Mar 21, 2023

This issue seems related to #619, and I just ran into this problem.

In the following code and error messages, the names have been changed to protect the guilty. 😉

I have a class defined something like this:

final class AggregateThingService implements ThingServiceProviderInterface
{
    public function __construct(
        private readonly ThingServiceProviderInterface $defaultProvider,
        ThingServiceProviderInterface ...$additionalProviders,
    ) {
    }
}

The idea is that it requires a default provider, but you can also give it additional providers. This "aggregate" class implements the same interface, and can be used in the same way as the ones passed to it, but it has some additional logic about which provider to use when executing the interface's methods.

For PHP-DI, my definition looks something like this. In this case, I'm not passing any additional providers. I don't need them for this case. I just need the default provider, which I provide.

[
    ThingServiceProviderInterface::class => DI\create(AggregateThingService::class)
        ->constructor(
            defaultProvider: DI\get(CompanyProvider::class),
        ),
]

When PHP-DI tries to process this, it gives the following error:

Entry "App\Handler\ProcessThingHandler" cannot be resolved: Entry
"App\Provider\ThingServiceProviderInterface" cannot be resolved: The parameter
"additionalProviders" of __construct() has no type defined or guessable. It has
a default value, but the default value can't be read through Reflection because
it is a PHP internal class.

  Full definition:
  Object (
      class = App\Provider\AggregateThingService
      lazy = false
      __construct(
          $defaultProvider = get(App\Provider\Company\CompanyProvider)
          $additionalProviders = #UNDEFINED#
      )
  )

@mnapoli
Copy link
Member

mnapoli commented Mar 27, 2023

Would it work with

final class AggregateThingService implements ThingServiceProviderInterface
{
    public function __construct(
        private readonly ThingServiceProviderInterface $defaultProvider,
        ThingServiceProviderInterface ...$additionalProviders = null,
    ) {
    }
}

? (i.e. suggesting to PHP-DI that these extra parameters are optional)

But maybe I'm just off… Does the splat operator make the parameter optional already?

@emulgeator
Copy link
Author

But maybe I'm just off… Does the splat operator make the parameter optional already?

Yes it does

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

No branches or pull requests

3 participants