/** * @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)); }