Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: Chris@14: namespace Symfony\Component\DependencyInjection\Compiler; Chris@14: Chris@14: use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; Chris@14: use Symfony\Component\DependencyInjection\ContainerBuilder; Chris@14: use Symfony\Component\DependencyInjection\Definition; Chris@14: use Symfony\Component\DependencyInjection\Exception\RuntimeException; Chris@14: use Symfony\Component\DependencyInjection\Reference; Chris@14: Chris@14: /** Chris@14: * @author Nicolas Grekas
Chris@14: */ Chris@14: abstract class AbstractRecursivePass implements CompilerPassInterface Chris@14: { Chris@14: /** Chris@14: * @var ContainerBuilder Chris@14: */ Chris@14: protected $container; Chris@14: protected $currentId; Chris@14: Chris@14: /** Chris@14: * {@inheritdoc} Chris@14: */ Chris@14: public function process(ContainerBuilder $container) Chris@14: { Chris@14: $this->container = $container; Chris@14: Chris@14: try { Chris@14: $this->processValue($container->getDefinitions(), true); Chris@14: } finally { Chris@14: $this->container = null; Chris@14: } Chris@14: } Chris@14: Chris@14: /** Chris@14: * Processes a value found in a definition tree. Chris@14: * Chris@14: * @param mixed $value Chris@14: * @param bool $isRoot Chris@14: * Chris@14: * @return mixed The processed value Chris@14: */ Chris@14: protected function processValue($value, $isRoot = false) Chris@14: { Chris@14: if (\is_array($value)) { Chris@14: foreach ($value as $k => $v) { Chris@14: if ($isRoot) { Chris@14: $this->currentId = $k; Chris@14: } Chris@14: if ($v !== $processedValue = $this->processValue($v, $isRoot)) { Chris@14: $value[$k] = $processedValue; Chris@14: } Chris@14: } Chris@14: } elseif ($value instanceof ArgumentInterface) { Chris@14: $value->setValues($this->processValue($value->getValues())); Chris@14: } elseif ($value instanceof Definition) { Chris@14: $value->setArguments($this->processValue($value->getArguments())); Chris@14: $value->setProperties($this->processValue($value->getProperties())); Chris@14: $value->setMethodCalls($this->processValue($value->getMethodCalls())); Chris@14: Chris@14: $changes = $value->getChanges(); Chris@14: if (isset($changes['factory'])) { Chris@14: $value->setFactory($this->processValue($value->getFactory())); Chris@14: } Chris@14: if (isset($changes['configurator'])) { Chris@14: $value->setConfigurator($this->processValue($value->getConfigurator())); Chris@14: } Chris@14: } Chris@14: Chris@14: return $value; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param Definition $definition Chris@14: * @param bool $required Chris@14: * Chris@14: * @return \ReflectionFunctionAbstract|null Chris@14: * Chris@14: * @throws RuntimeException Chris@14: */ Chris@14: protected function getConstructor(Definition $definition, $required) Chris@14: { Chris@17: if (\is_string($factory = $definition->getFactory())) { Chris@17: if (!\function_exists($factory)) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory)); Chris@14: } Chris@14: $r = new \ReflectionFunction($factory); Chris@14: if (false !== $r->getFileName() && file_exists($r->getFileName())) { Chris@14: $this->container->fileExists($r->getFileName()); Chris@14: } Chris@14: Chris@14: return $r; Chris@14: } Chris@14: Chris@14: if ($factory) { Chris@14: list($class, $method) = $factory; Chris@14: if ($class instanceof Reference) { Chris@14: $class = $this->container->findDefinition((string) $class)->getClass(); Chris@14: } elseif (null === $class) { Chris@14: $class = $definition->getClass(); Chris@14: } Chris@14: if ('__construct' === $method) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId)); Chris@14: } Chris@14: Chris@14: return $this->getReflectionMethod(new Definition($class), $method); Chris@14: } Chris@14: Chris@14: $class = $definition->getClass(); Chris@14: Chris@16: try { Chris@16: if (!$r = $this->container->getReflectionClass($class)) { Chris@16: throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); Chris@16: } Chris@16: } catch (\ReflectionException $e) { Chris@16: throw new RuntimeException(sprintf('Invalid service "%s": %s.', $this->currentId, lcfirst(rtrim($e->getMessage(), '.')))); Chris@14: } Chris@14: if (!$r = $r->getConstructor()) { Chris@14: if ($required) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class))); Chris@14: } Chris@14: } elseif (!$r->isPublic()) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class))); Chris@14: } Chris@14: Chris@14: return $r; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param Definition $definition Chris@14: * @param string $method Chris@14: * Chris@14: * @throws RuntimeException Chris@14: * Chris@14: * @return \ReflectionFunctionAbstract Chris@14: */ Chris@14: protected function getReflectionMethod(Definition $definition, $method) Chris@14: { Chris@14: if ('__construct' === $method) { Chris@14: return $this->getConstructor($definition, true); Chris@14: } Chris@14: Chris@14: if (!$class = $definition->getClass()) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId)); Chris@14: } Chris@14: Chris@14: if (!$r = $this->container->getReflectionClass($class)) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); Chris@14: } Chris@14: Chris@14: if (!$r->hasMethod($method)) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); Chris@14: } Chris@14: Chris@14: $r = $r->getMethod($method); Chris@14: if (!$r->isPublic()) { Chris@14: throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); Chris@14: } Chris@14: Chris@14: return $r; Chris@14: } Chris@14: }