isChildItem && $this->isFactoryNeeded($this)) { $content = <<<'FACTORY' new class implements \oat\oatbox\service\ServiceFactoryInterface { public function __invoke(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator) { return new %s(%s); } } FACTORY; } return sprintf( $content, static::class, implode(",\n", $this->getSerializedDependencies()) ); } /** * @return array * * @throws ReflectionException */ private function getSerializedDependencies(): array { return array_map( [common_Utils::class, 'toPHPVariableString'], $this->getDependencies() ); } /** * @param InjectionAwareService $service * * @return iterable * @throws ReflectionException */ protected function iterateParameters(InjectionAwareService $service): iterable { $class = new ReflectionClass($service); $constructor = $class->getMethod('__construct'); $parameters = $constructor->getParameters(); foreach ($parameters as $parameter) { $parameterName = $parameter->getName(); if (!$class->hasProperty($parameterName)) { $message = sprintf( 'Cannot find property "%s" in class %s. Please name properties exactly like constructor parameters, or overload %s', $parameterName, static::class, __METHOD__ ); throw new RuntimeException($message); } $classProperty = $class->getProperty($parameterName); if ($classProperty->isPrivate() || $classProperty->isProtected()) { $classProperty->setAccessible(true); } $value = $classProperty->getValue($service); $parameter->isVariadic() ? yield from $value : yield $value; } } /** * @return array A list of dependencies to be injected in their order. * @throws ReflectionException */ protected function getDependencies(): array { $dependencies = []; foreach ($this->iterateParameters($this) as $propertyValue) { if ($propertyValue instanceof ConfigurableService) { if ($this->hasConfig($propertyValue)) { $propertyValue = new PhpCode( sprintf('$serviceLocator->get(%s::class)', get_class($propertyValue)) ); } if ($propertyValue instanceof self) { $propertyValue->isChildItem = true; } } $dependencies[] = $propertyValue; } return $dependencies; } private function hasConfig(ConfigurableService $service): bool { $className = get_class($service); return defined("$className::SERVICE_ID"); } /** * @param InjectionAwareService $service * * @return bool * @throws ReflectionException */ protected function isFactoryNeeded(InjectionAwareService $service): bool { foreach ($this->iterateParameters($service) as $propertyValue) { if (!$propertyValue instanceof ConfigurableService) { continue; } if ( !$propertyValue instanceof self || $this->hasConfig($propertyValue) ) { return true; } $result = $this->isFactoryNeeded($propertyValue); if ($result) { return true; } } return false; } }