private function updateDefinition(DI\ServiceDefinition $def) { $class = $def->getClass(); $setups = $def->getSetup(); foreach (self::getInjectProperties($class) as $property => $type) { $builder = $this->getContainerBuilder(); $inject = new DI\Statement('$' . $property, ['@\\' . ltrim($type, '\\')]); foreach ($setups as $key => $setup) { if ($setup->getEntity() === $inject->getEntity()) { $inject = $setup; $builder = NULL; unset($setups[$key]); } } self::checkType($class, $property, $type, $builder); array_unshift($setups, $inject); } foreach (array_reverse(self::getInjectMethods($def->getClass())) as $method) { $inject = new DI\Statement($method); foreach ($setups as $key => $setup) { if ($setup->getEntity() === $inject->getEntity()) { $inject = $setup; unset($setups[$key]); } } array_unshift($setups, $inject); } $def->setSetup($setups); }
/** * Formats PHP code for class instantiating, function calling or property setting in PHP. * @return string * @internal */ public function formatStatement(Statement $statement) { $entity = $this->normalizeEntity($statement->getEntity()); $arguments = $statement->arguments; if (is_string($entity) && Strings::contains($entity, '?')) { // PHP literal return $this->formatPhp($entity, $arguments); } elseif ($service = $this->getServiceName($entity)) { // factory calling $params = []; foreach ($this->definitions[$service]->parameters as $k => $v) { $params[] = preg_replace('#\\w+\\z#', '\\$$0', is_int($k) ? $v : $k) . (is_int($k) ? '' : ' = ' . PhpHelpers::dump($v)); } $rm = new \ReflectionFunction(create_function(implode(', ', $params), '')); $arguments = Helpers::autowireArguments($rm, $arguments, $this); return $this->formatPhp('$this->?(?*)', [Container::getMethodName($service), $arguments]); } elseif ($entity === 'not') { // operator return $this->formatPhp('!?', [$arguments[0]]); } elseif (is_string($entity)) { // class name if ($constructor = (new ReflectionClass($entity))->getConstructor()) { $this->addDependency((string) $constructor->getFileName()); $arguments = Helpers::autowireArguments($constructor, $arguments, $this); } elseif ($arguments) { throw new ServiceCreationException("Unable to pass arguments, class {$entity} has no constructor."); } return $this->formatPhp("new {$entity}" . ($arguments ? '(?*)' : ''), [$arguments]); } elseif (!Nette\Utils\Arrays::isList($entity) || count($entity) !== 2) { throw new ServiceCreationException(sprintf('Expected class, method or property, %s given.', PhpHelpers::dump($entity))); } elseif (!preg_match('#^\\$?' . PhpHelpers::PHP_IDENT . '\\z#', $entity[1])) { throw new ServiceCreationException("Expected function, method or property name, '{$entity['1']}' given."); } elseif ($entity[0] === '') { // globalFunc return $this->formatPhp("{$entity['1']}(?*)", [$arguments]); } elseif ($entity[0] instanceof Statement) { $inner = $this->formatPhp('?', [$entity[0]]); if (substr($inner, 0, 4) === 'new ') { $inner = "({$inner})"; } return $this->formatPhp("{$inner}->?(?*)", [$entity[1], $arguments]); } elseif (Strings::contains($entity[1], '$')) { // property setter Validators::assert($arguments, 'list:1', "setup arguments for '" . Nette\Utils\Callback::toString($entity) . "'"); if ($this->getServiceName($entity[0])) { return $this->formatPhp('?->? = ?', [$entity[0], substr($entity[1], 1), $arguments[0]]); } else { return $this->formatPhp($entity[0] . '::$? = ?', [substr($entity[1], 1), $arguments[0]]); } } elseif ($service = $this->getServiceName($entity[0])) { // service method $class = $this->definitions[$service]->getImplement(); if (!$class || !method_exists($class, $entity[1])) { $class = $this->definitions[$service]->getClass(); } if ($class) { $arguments = $this->autowireArguments($class, $entity[1], $arguments); } return $this->formatPhp('?->?(?*)', [$entity[0], $entity[1], $arguments]); } else { // static method $arguments = $this->autowireArguments($entity[0], $entity[1], $arguments); return $this->formatPhp("{$entity['0']}::{$entity['1']}(?*)", [$arguments]); } }
public function getEntity() { return $this->factory ? $this->factory->getEntity() : NULL; }
/** * @param \Nette\DI\ServiceDefinition $metadataDriver * @param string $namespace * @param string|object $driver * @param string $prefix * @throws \Nette\Utils\AssertionException * @return string */ protected function processMetadataDriver(Nette\DI\ServiceDefinition $metadataDriver, $namespace, $driver, $prefix) { if (!is_string($namespace) || !Strings::match($namespace, '#^' . self::PHP_NAMESPACE . '\\z#')) { throw new Nette\Utils\AssertionException("The metadata namespace expects to be valid namespace, {$namespace} given."); } $namespace = ltrim($namespace, '\\'); if (is_string($driver) || is_array($driver)) { $paths = is_array($driver) ? $driver : [$driver]; foreach ($paths as $path) { if (($pos = strrpos($path, '*')) !== FALSE) { $path = substr($path, 0, $pos); } if (!file_exists($path)) { throw new Nette\Utils\AssertionException("The metadata path expects to be an existing directory, {$path} given."); } } $driver = new Statement(self::ANNOTATION_DRIVER, is_array($paths) ? $paths : [$paths]); } $impl = $driver instanceof \stdClass ? $driver->value : ($driver instanceof Statement ? $driver->getEntity() : (string) $driver); list($driver) = CacheHelpers::filterArgs($driver); /** @var Statement $driver */ /** @var string $impl */ if (isset($this->metadataDriverClasses[$impl])) { $driver = new Statement($this->metadataDriverClasses[$impl], $driver->arguments); } if (is_string($driver->getEntity()) && substr($driver->getEntity(), 0, 1) === '@') { $metadataDriver->addSetup('addDriver', [$driver->getEntity(), $namespace]); return $driver->getEntity(); } if ($impl === self::ANNOTATION_DRIVER) { $driver->arguments = [Nette\Utils\Arrays::flatten($driver->arguments), 2 => $this->prefix('@cache.' . $prefix . '.metadata')]; } $serviceName = $this->prefix($prefix . '.driver.' . str_replace('\\', '_', $namespace) . '.' . str_replace('\\', '_', $impl) . 'Impl'); $this->getContainerBuilder()->addDefinition($serviceName)->setClass('Doctrine\\Common\\Persistence\\Mapping\\Driver\\MappingDriver')->setFactory($driver->getEntity(), $driver->arguments)->setAutowired(FALSE)->setInject(FALSE); $metadataDriver->addSetup('addDriver', ['@' . $serviceName, $namespace]); return '@' . $serviceName; }
/** * Formats PHP code for class instantiating, function calling or property setting in PHP. * @return string */ private function formatStatement(Statement $statement) { $entity = $statement->getEntity(); $arguments = $statement->arguments; if (is_string($entity) && Strings::contains($entity, '?')) { // PHP literal return $this->formatPhp($entity, $arguments); } elseif ($service = $this->builder->getServiceName($entity)) { // factory calling return $this->formatPhp('$this->?(?*)', [Container::getMethodName($service), $arguments]); } elseif ($entity === 'not') { // operator return $this->formatPhp('!?', [$arguments[0]]); } elseif (is_string($entity)) { // class name return $this->formatPhp("new {$entity}" . ($arguments ? '(?*)' : ''), [$arguments]); } elseif ($entity[0] === '') { // globalFunc return $this->formatPhp("{$entity['1']}(?*)", [$arguments]); } elseif ($entity[0] instanceof Statement) { $inner = $this->formatPhp('?', [$entity[0]]); if (substr($inner, 0, 4) === 'new ') { $inner = "({$inner})"; } return $this->formatPhp("{$inner}->?(?*)", [$entity[1], $arguments]); } elseif ($entity[1][0] === '$') { // property getter, setter or appender $name = substr($entity[1], 1); if ($append = substr($name, -2) === '[]') { $name = substr($name, 0, -2); } if ($this->builder->getServiceName($entity[0])) { $prop = $this->formatPhp('?->?', [$entity[0], $name]); } else { $prop = $this->formatPhp($entity[0] . '::$?', [$name]); } return $arguments ? $this->formatPhp($prop . ($append ? '[]' : '') . ' = ?', [$arguments[0]]) : $prop; } elseif ($service = $this->builder->getServiceName($entity[0])) { // service method return $this->formatPhp('?->?(?*)', [$entity[0], $entity[1], $arguments]); } else { // static method return $this->formatPhp("{$entity['0']}::{$entity['1']}(?*)", [$arguments]); } }
/** * @return Statement */ public function completeStatement(Statement $statement) { $entity = $this->normalizeEntity($statement->getEntity()); $arguments = $statement->arguments; if (is_string($entity) && Strings::contains($entity, '?')) { // PHP literal } elseif ($service = $this->getServiceName($entity)) { // factory calling $params = []; foreach ($this->definitions[$service]->parameters as $k => $v) { $params[] = preg_replace('#\\w+\\z#', '\\$$0', is_int($k) ? $v : $k) . (is_int($k) ? '' : ' = ' . PhpHelpers::dump($v)); } $rm = new \ReflectionFunction(create_function(implode(', ', $params), '')); $arguments = Helpers::autowireArguments($rm, $arguments, $this); $entity = '@' . $service; } elseif ($entity === 'not') { // operator } elseif (is_string($entity)) { // class name if (!class_exists($entity)) { throw new ServiceCreationException("Class {$entity} not found."); } elseif ((new ReflectionClass($entity))->isAbstract()) { throw new ServiceCreationException("Class {$entity} is abstract."); } elseif (($rm = (new ReflectionClass($entity))->getConstructor()) !== NULL && !$rm->isPublic()) { $visibility = $rm->isProtected() ? 'protected' : 'private'; throw new ServiceCreationException("Class {$entity} has {$visibility} constructor."); } elseif ($constructor = (new ReflectionClass($entity))->getConstructor()) { $this->addDependency($constructor); $arguments = Helpers::autowireArguments($constructor, $arguments, $this); } elseif ($arguments) { throw new ServiceCreationException("Unable to pass arguments, class {$entity} has no constructor."); } } elseif (!Nette\Utils\Arrays::isList($entity) || count($entity) !== 2) { throw new ServiceCreationException(sprintf('Expected class, method or property, %s given.', PhpHelpers::dump($entity))); } elseif (!preg_match('#^\\$?' . PhpHelpers::PHP_IDENT . '(\\[\\])?\\z#', $entity[1])) { throw new ServiceCreationException("Expected function, method or property name, '{$entity['1']}' given."); } elseif ($entity[0] === '') { // globalFunc if (!Nette\Utils\Arrays::isList($arguments)) { throw new ServiceCreationException("Unable to pass specified arguments to {$entity['0']}."); } elseif (!function_exists($entity[1])) { throw new ServiceCreationException("Function {$entity['1']} doesn't exist."); } $rf = new \ReflectionFunction($entity[1]); $this->addDependency($rf); $arguments = Helpers::autowireArguments($rf, $arguments, $this); } else { if ($entity[0] instanceof Statement) { $entity[0] = $this->completeStatement($entity[0]); } elseif ($service = $this->getServiceName($entity[0])) { // service method $entity[0] = '@' . $service; } if ($entity[1][0] === '$') { // property getter, setter or appender Validators::assert($arguments, 'list:0..1', "setup arguments for '" . Nette\Utils\Callback::toString($entity) . "'"); if (!$arguments && substr($entity[1], -2) === '[]') { throw new ServiceCreationException("Missing argument for {$entity['1']}."); } } elseif ($class = empty($service) || $entity[1] === 'create' ? $this->resolveEntityClass($entity[0]) : $this->definitions[$service]->getClass()) { $arguments = $this->autowireArguments($class, $entity[1], $arguments); } } array_walk_recursive($arguments, function (&$val) { if ($val instanceof Statement) { $val = $this->completeStatement($val); } elseif ($val === $this) { trigger_error("Replace object ContainerBuilder in Statement arguments with '@container'.", E_USER_DEPRECATED); $val = self::literal('$this'); } elseif ($val instanceof ServiceDefinition) { $val = '@' . current(array_keys($this->getDefinitions(), $val, TRUE)); } elseif (is_string($val) && strlen($val) > 1 && $val[0] === '@' && $val[1] !== '@') { $pair = explode('::', $val, 2); $name = $this->getServiceName($pair[0]); if (!isset($pair[1])) { // @service $val = '@' . $name; } elseif (preg_match('#^[A-Z][A-Z0-9_]*\\z#', $pair[1], $m)) { // @service::CONSTANT $val = self::literal($this->getDefinition($name)->getClass() . '::' . $pair[1]); } else { // @service::property $val = new Statement(['@' . $name, '$' . $pair[1]]); } } }); return new Statement($entity, $arguments); }