/**
  * @covers ::addDependencyRetriever
  *
  * @dataProvider provideInstantiateWithSuggestedDependencies
  *
  * @param string $className
  *   The fully qualified name of the class to instantiate.
  * @param array[] $suggestedDependencyIds
  *   Keys are constructor argument names, and values are arrays of which
  *   keys are dependency retriever names, and values are dependency IDs.
  */
 public function testInstantiateWithSuggestedConstructorDependenciesAndSetterRetrieverInjection($className, array $suggestedDependencyIds = [])
 {
     $this->sut = new SimpleFactory($this->suggestedDependencyFinder->reveal());
     $this->sut->addDependencyRetriever($this->goldenDependencyRetriever->reveal());
     $this->sut->addDependencyRetriever($this->labradorDependencyRetriever->reveal());
     $this->suggestedDependencyFinder->findSuggestedDependencyIds($className)->willReturn($suggestedDependencyIds);
     $overriddenDependencies = ['qux' => new \stdClass()];
     $this->assertInstanceOf($className, $this->sut->instantiate($className, $overriddenDependencies));
 }
 public function instantiate($className, array $overrideDependencies = [])
 {
     $class = new \ReflectionClass($className);
     // Instantiate quickly if there is no constructor.
     if (!$class->hasMethod('__construct')) {
         return new $className();
     }
     $method = $class->getMethod('__construct');
     // Get the argument names and set default values.
     $arguments = [];
     $defaultDependencies = [];
     foreach ($method->getParameters() as $argument) {
         $arguments[$argument->getName()] = $argument;
         if ($argument->isDefaultValueAvailable()) {
             $defaultDependencies[$argument->getName()] = $argument->getDefaultValue();
         }
     }
     // Instantiate quickly if the constructor has no arguments.
     if (!$arguments) {
         return new $className();
     }
     // Retrieve dependencies that aren't overridden.
     $dependencySuggestions = $this->suggestedDependencyFinder->findSuggestedDependencyIds($className);
     $suggestedDependencies = [];
     foreach (array_diff_key($dependencySuggestions, $overrideDependencies) as $argumentName => $argumentDependencySuggestions) {
         /**
          * @var \BartFeenstra\DependencyRetriever\DependencySuggestion\Suggestion[] $argumentDependencySuggestions
          */
         foreach ($this->dependencyRetrievers as $dependencyRetriever) {
             $retrieverName = $dependencyRetriever->getName();
             foreach ($argumentDependencySuggestions as $argumentDependencySuggestion) {
                 if ($retrieverName == $argumentDependencySuggestion->getDependencyRetrieverName() && $dependencyRetriever->knowsDependency($argumentDependencySuggestion->getDependencyId())) {
                     $suggestedDependencies[$argumentName] = $dependencyRetriever->retrieveDependency($argumentDependencySuggestion->getDependencyId());
                 }
             }
         }
     }
     // Merge dependencies.
     $dependencies = array_merge($defaultDependencies, $suggestedDependencies, $overrideDependencies);
     // Check if we have values for all arguments.
     $namesOfArgumentsWithoutValues = array_keys(array_diff_key($arguments, $dependencies));
     if ($namesOfArgumentsWithoutValues) {
         throw new MissingDependencyException($className, $namesOfArgumentsWithoutValues);
     }
     // We now have dependencies for all arguments. Put them in the correct
     // order.
     uksort($dependencies, function ($argumentAName, $argumentBName) use($arguments) {
         /**
          * @var \ReflectionParameter[] $arguments
          */
         $argumentAPosition = $arguments[$argumentAName]->getPosition();
         $argumentBPosition = $arguments[$argumentBName]->getPosition();
         if ($argumentAPosition == $argumentBPosition) {
             return 0;
         } elseif ($argumentAPosition > $argumentBPosition) {
             return 1;
         } else {
             return -1;
         }
     });
     return new $className(...array_values($dependencies));
 }