src/DI/CompilerExtension.php 0000666 00000004343 13436755707 0012040 0 ustar 00 compiler = $compiler; $this->name = $name; return $this; } /** * Returns extension configuration. * @param array default unexpanded values. * @return array */ public function getConfig(array $defaults = NULL) { $config = $this->compiler->getConfig(); $config = isset($config[$this->name]) ? $config[$this->name] : array(); unset($config['services'], $config['factories']); return Config\Helpers::merge($config, $this->compiler->getContainerBuilder()->expand($defaults)); } /** * @return ContainerBuilder */ public function getContainerBuilder() { return $this->compiler->getContainerBuilder(); } /** * Reads configuration from file. * @param string file name * @return array */ public function loadFromFile($file) { $loader = new Config\Loader; $res = $loader->load($file); $container = $this->compiler->getContainerBuilder(); foreach ($loader->getDependencies() as $file) { $container->addDependency($file); } return $res; } /** * Prepend extension name to identifier or service name. * @param string * @return string */ public function prefix($id) { return substr_replace($id, $this->name . '.', substr($id, 0, 1) === '@' ? 1 : 0, 0); } /** * Processes configuration data. Intended to be overridden by descendant. * @return void */ public function loadConfiguration() { } /** * Adjusts DI container before is compiled to PHP class. Intended to be overridden by descendant. * @return void */ public function beforeCompile() { } /** * Adjusts DI container compiled to PHP class. Intended to be overridden by descendant. * @return void */ public function afterCompile(Nette\PhpGenerator\ClassType $class) { } } src/DI/Extensions/ConstantsExtension.php 0000666 00000001012 13436755707 0014367 0 ustar 00 getConfig() as $name => $value) { $class->methods['initialize']->addBody('define(?, ?);', array($name, $value)); } } } src/DI/Extensions/ExtensionsExtension.php 0000666 00000000762 13436755707 0014565 0 ustar 00 getConfig() as $name => $class) { $this->compiler->addExtension($name, new $class); } } } src/DI/Extensions/PhpExtension.php 0000666 00000002524 13436755707 0013153 0 ustar 00 methods['initialize']; foreach ($this->getConfig() as $name => $value) { if (!is_scalar($value)) { throw new Nette\InvalidStateException("Configuration value for directive '$name' is not scalar."); } elseif ($name === 'include_path') { $initialize->addBody('set_include_path(?);', array(str_replace(';', PATH_SEPARATOR, $value))); } elseif ($name === 'ignore_user_abort') { $initialize->addBody('ignore_user_abort(?);', array($value)); } elseif ($name === 'max_execution_time') { $initialize->addBody('set_time_limit(?);', array($value)); } elseif ($name === 'date.timezone') { $initialize->addBody('date_default_timezone_set(?);', array($value)); } elseif (function_exists('ini_set')) { $initialize->addBody('ini_set(?, ?);', array($name, $value)); } elseif (ini_get($name) != $value) { // intentionally == throw new Nette\NotSupportedException('Required function ini_set() is disabled.'); } } } } src/DI/Helpers.php 0000666 00000012101 13436755707 0007762 0 ustar 00 $val) { $res[$key] = self::expand($val, $params, $recursive); } return $res; } elseif ($var instanceof \stdClass || $var instanceof Statement) { $res = clone $var; foreach ($var as $key => $val) { $res->$key = self::expand($val, $params, $recursive); } return $res; } elseif (!is_string($var)) { return $var; } $parts = preg_split('#%([\w.-]*)%#i', $var, -1, PREG_SPLIT_DELIM_CAPTURE); $res = ''; foreach ($parts as $n => $part) { if ($n % 2 === 0) { $res .= $part; } elseif ($part === '') { $res .= '%'; } elseif (isset($recursive[$part])) { throw new Nette\InvalidArgumentException(sprintf('Circular reference detected for variables: %s.', implode(', ', array_keys($recursive)))); } else { try { $val = Nette\Utils\Arrays::get($params, explode('.', $part)); } catch (Nette\InvalidArgumentException $e) { throw new Nette\InvalidArgumentException("Missing parameter '$part'.", 0, $e); } if ($recursive) { $val = self::expand($val, $params, (is_array($recursive) ? $recursive : array()) + array($part => 1)); } if (strlen($part) + 2 === strlen($var)) { return $val; } if (!is_scalar($val)) { throw new Nette\InvalidArgumentException("Unable to concatenate non-scalar parameter '$part' into '$var'."); } $res .= $val; } } return $res; } /** * Generates list of arguments using autowiring. * @param Nette\Reflection\GlobalFunction|Nette\Reflection\Method * @return array */ public static function autowireArguments(\ReflectionFunctionAbstract $method, array $arguments, $container) { $optCount = 0; $num = -1; $res = array(); foreach ($method->getParameters() as $num => $parameter) { if (array_key_exists($num, $arguments)) { $res[$num] = $arguments[$num]; unset($arguments[$num]); $optCount = 0; } elseif (array_key_exists($parameter->getName(), $arguments)) { $res[$num] = $arguments[$parameter->getName()]; unset($arguments[$parameter->getName()]); $optCount = 0; } elseif ($class = $parameter->getClassName()) { // has object type hint $res[$num] = $container->getByType($class, FALSE); if ($res[$num] === NULL) { if ($parameter->allowsNull()) { $optCount++; } elseif (class_exists($class) || interface_exists($class)) { throw new ServiceCreationException("Service of type {$class} needed by $method not found. Did you register it in configuration file?"); } else { throw new ServiceCreationException("Class {$class} needed by $method not found. Check type hint and 'use' statements."); } } else { if ($container instanceof ContainerBuilder) { $res[$num] = '@' . $res[$num]; } $optCount = 0; } } elseif ($parameter->isOptional()) { // PDO::__construct has optional parameter without default value (and isArray() and allowsNull() returns FALSE) $res[$num] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : NULL; $optCount++; } else { throw new ServiceCreationException("Parameter $parameter has no type hint, so its value must be specified."); } } // extra parameters while (array_key_exists(++$num, $arguments)) { $res[$num] = $arguments[$num]; unset($arguments[$num]); $optCount = 0; } if ($arguments) { throw new ServiceCreationException("Unable to pass specified arguments to $method."); } return $optCount ? array_slice($res, 0, -$optCount) : $res; } /** * Generates list of properties with annotation @inject. * @return array */ public static function getInjectProperties(Nette\Reflection\ClassType $class, $container = NULL) { $res = array(); foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $type = $property->getAnnotation('var'); if (!$property->getAnnotation('inject')) { continue; } elseif (!$type) { throw new Nette\InvalidStateException("Property $property has no @var annotation."); } $type = Nette\Reflection\AnnotationsParser::expandClassName($type, $property->getDeclaringClass()); if (!class_exists($type) && !interface_exists($type)) { throw new Nette\InvalidStateException("Class or interface '$type' used in @var annotation at $property not found. Check annotation and 'use' statements."); } elseif ($container && !$container->getByType($type, FALSE)) { throw new ServiceCreationException("Service of type {$type} used in @var annotation at $property not found. Did you register it in configuration file?"); } $res[$property->getName()] = $type; } return $res; } } src/DI/Config/Helpers.php 0000666 00000003236 13436755707 0011200 0 ustar 00 $val) { if (is_int($key)) { $right[] = $val; } else { if (is_array($val) && isset($val[self::EXTENDS_KEY])) { if ($val[self::EXTENDS_KEY] === self::OVERWRITE) { unset($val[self::EXTENDS_KEY]); } } elseif (isset($right[$key])) { $val = static::merge($val, $right[$key]); } $right[$key] = $val; } } return $right; } elseif ($left === NULL && is_array($right)) { return $right; } else { return $left; } } /** * Finds out and removes information about the parent. * @return mixed */ public static function takeParent(& $data) { if (is_array($data) && isset($data[self::EXTENDS_KEY])) { $parent = $data[self::EXTENDS_KEY]; unset($data[self::EXTENDS_KEY]); return $parent; } } /** * @return bool */ public static function isOverwriting(& $data) { return is_array($data) && isset($data[self::EXTENDS_KEY]) && $data[self::EXTENDS_KEY] === self::OVERWRITE; } /** * @return bool */ public static function isInheriting(& $data) { return is_array($data) && isset($data[self::EXTENDS_KEY]) && $data[self::EXTENDS_KEY] !== self::OVERWRITE; } } src/DI/Config/Adapters/PhpAdapter.php 0000666 00000001307 13436755707 0013366 0 ustar 00 $secData) { if (is_array($secData)) { // is section? if (substr($secName, -1) === self::RAW_SECTION) { $secName = substr($secName, 0, -1); } else { // process key nesting separator (key1.key2.key3) $tmp = array(); foreach ($secData as $key => $val) { $cursor = & $tmp; $key = str_replace(self::ESCAPED_KEY_SEPARATOR, "\xFF", $key); foreach (explode(self::KEY_SEPARATOR, $key) as $part) { $part = str_replace("\xFF", self::KEY_SEPARATOR, $part); if (!isset($cursor[$part]) || is_array($cursor[$part])) { $cursor = & $cursor[$part]; } else { throw new Nette\InvalidStateException("Invalid key '$key' in section [$secName] in file '$file'."); } } $cursor = $val; } $secData = $tmp; } $parts = explode(self::INHERITING_SEPARATOR, $secName); if (count($parts) > 1) { $secName = trim($parts[0]); $secData[Helpers::EXTENDS_KEY] = trim($parts[1]); } } $cursor = & $data; // nesting separator in section name foreach (explode(self::KEY_SEPARATOR, $secName) as $part) { if (!isset($cursor[$part]) || is_array($cursor[$part])) { $cursor = & $cursor[$part]; } else { throw new Nette\InvalidStateException("Invalid section [$secName] in file '$file'."); } } if (is_array($secData) && is_array($cursor)) { $secData = Helpers::merge($secData, $cursor); } $cursor = $secData; } return $data; } /** * Generates configuration in INI format. * @return string */ public function dump(array $data) { $output = array(); foreach ($data as $name => $secData) { if (!is_array($secData)) { $output = array(); self::build($data, $output, ''); break; } if ($parent = Helpers::takeParent($secData)) { $output[] = "[$name " . self::INHERITING_SEPARATOR . " $parent]"; } else { $output[] = "[$name]"; } self::build($secData, $output, ''); $output[] = ''; } return "; generated by Nette\n\n" . implode(PHP_EOL, $output); } /** * Recursive builds INI list. * @return void */ private static function build($input, & $output, $prefix) { foreach ($input as $key => $val) { $key = str_replace(self::KEY_SEPARATOR, self::ESCAPED_KEY_SEPARATOR, $key); if (is_array($val)) { self::build($val, $output, $prefix . $key . self::KEY_SEPARATOR); } elseif (is_bool($val)) { $output[] = "$prefix$key = " . ($val ? 'true' : 'false'); } elseif (is_numeric($val)) { $output[] = "$prefix$key = $val"; } elseif (is_string($val)) { $output[] = "$prefix$key = \"$val\""; } else { throw new Nette\InvalidArgumentException(sprintf("The '%s' item must be scalar or array, %s given.", $prefix . $key, gettype($val))); } } } } src/DI/Config/Adapters/NeonAdapter.php 0000666 00000004226 13436755707 0013541 0 ustar 00 process((array) Neon\Neon::decode(file_get_contents($file))); } private function process(array $arr) { $res = array(); foreach ($arr as $key => $val) { if (substr($key, -1) === self::PREVENT_MERGING) { if (!is_array($val) && $val !== NULL) { throw new Nette\InvalidStateException("Replacing operator is available only for arrays, item '$key' is not array."); } $key = substr($key, 0, -1); $val[Helpers::EXTENDS_KEY] = Helpers::OVERWRITE; } elseif (preg_match('#^(\S+)\s+' . self::INHERITING_SEPARATOR . '\s+(\S+)\z#', $key, $matches)) { if (!is_array($val) && $val !== NULL) { throw new Nette\InvalidStateException("Inheritance operator is available only for arrays, item '$key' is not array."); } list(, $key, $val[Helpers::EXTENDS_KEY]) = $matches; if (isset($res[$key])) { throw new Nette\InvalidStateException("Duplicated key '$key'."); } } if (is_array($val)) { $val = $this->process($val); } elseif ($val instanceof Neon\Entity) { $val = (object) array('value' => $val->value, 'attributes' => $this->process($val->attributes)); } $res[$key] = $val; } return $res; } /** * Generates configuration in NEON format. * @return string */ public function dump(array $data) { $tmp = array(); foreach ($data as $name => $secData) { if ($parent = Helpers::takeParent($secData)) { $name .= ' ' . self::INHERITING_SEPARATOR . ' ' . $parent; } $tmp[$name] = $secData; } return "# generated by Nette\n\n" . Neon\Neon::encode($tmp, Neon\Neon::BLOCK); } } src/DI/Config/IAdapter.php 0000666 00000000774 13436755707 0011273 0 ustar 00 'Nette\DI\Config\Adapters\PhpAdapter', 'ini' => 'Nette\DI\Config\Adapters\IniAdapter', 'neon' => 'Nette\DI\Config\Adapters\NeonAdapter', ); private $dependencies = array(); /** * Reads configuration from file. * @param string file name * @param string optional section to load * @return array */ public function load($file, $section = NULL) { if (!is_file($file) || !is_readable($file)) { throw new Nette\FileNotFoundException("File '$file' is missing or is not readable."); } $this->dependencies[] = realpath($file); $data = $this->getAdapter($file)->load($file); if ($section) { if (isset($data[self::INCLUDES_KEY])) { throw new Nette\InvalidStateException("Section 'includes' must be placed under some top section in file '$file'."); } $data = $this->getSection($data, $section, $file); } // include child files $merged = array(); if (isset($data[self::INCLUDES_KEY])) { Validators::assert($data[self::INCLUDES_KEY], 'list', "section 'includes' in file '$file'"); foreach ($data[self::INCLUDES_KEY] as $include) { $merged = Helpers::merge($this->load(dirname($file) . '/' . $include), $merged); } } unset($data[self::INCLUDES_KEY]); return Helpers::merge($data, $merged); } /** * Save configuration to file. * @param array * @param string file * @return void */ public function save($data, $file) { if (file_put_contents($file, $this->getAdapter($file)->dump($data)) === FALSE) { throw new Nette\IOException("Cannot write file '$file'."); } } /** * Returns configuration files. * @return array */ public function getDependencies() { return array_unique($this->dependencies); } /** * Registers adapter for given file extension. * @param string file extension * @param string|Nette\DI\Config\IAdapter * @return self */ public function addAdapter($extension, $adapter) { $this->adapters[strtolower($extension)] = $adapter; return $this; } /** @return IAdapter */ private function getAdapter($file) { $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); if (!isset($this->adapters[$extension])) { throw new Nette\InvalidArgumentException("Unknown file extension '$file'."); } return is_object($this->adapters[$extension]) ? $this->adapters[$extension] : new $this->adapters[$extension]; } private function getSection(array $data, $key, $file) { Validators::assertField($data, $key, 'array|null', "section '%' in file '$file'"); $item = $data[$key]; if ($parent = Helpers::takeParent($item)) { $item = Helpers::merge($item, $this->getSection($data, $parent, $file)); } return $item; } } src/DI/exceptions.php 0000666 00000000611 13436755707 0010544 0 ustar 00 1, 'factories' => 1, 'parameters' => 1); public function __construct(ContainerBuilder $builder = NULL) { $this->builder = $builder ?: new ContainerBuilder; } /** * Add custom configurator extension. * @return self */ public function addExtension($name, CompilerExtension $extension) { if (isset(self::$reserved[$name])) { throw new Nette\InvalidArgumentException("Name '$name' is reserved."); } $this->extensions[$name] = $extension->setCompiler($this, $name); return $this; } /** * @return array */ public function getExtensions($type = NULL) { return $type ? array_filter($this->extensions, function($item) use ($type) { return $item instanceof $type; }) : $this->extensions; } /** * @return ContainerBuilder */ public function getContainerBuilder() { return $this->builder; } /** * Returns configuration. * @return array */ public function getConfig() { return $this->config; } /** * @return string */ public function compile(array $config, $className, $parentName) { $this->config = $config; $this->processParameters(); $this->processExtensions(); $this->processServices(); return $this->generateCode($className, $parentName); } public function processParameters() { if (isset($this->config['parameters'])) { $this->builder->parameters = Helpers::expand($this->config['parameters'], $this->config['parameters'], TRUE); } } public function processExtensions() { for ($i = 0; $slice = array_slice($this->extensions, $i, 1, TRUE); $i++) { $name = key($slice); if (isset($this->config[$name])) { $this->config[$name] = $this->builder->expand($this->config[$name]); } $this->extensions[$name]->loadConfiguration(); } if ($extra = array_diff_key($this->config, self::$reserved, $this->extensions)) { $extra = implode("', '", array_keys($extra)); throw new Nette\InvalidStateException("Found sections '$extra' in configuration, but corresponding extensions are missing."); } } public function processServices() { $this->parseServices($this->builder, $this->config); foreach ($this->extensions as $name => $extension) { if (isset($this->config[$name])) { $this->parseServices($this->builder, $this->config[$name], $name); } } } public function generateCode($className, $parentName) { foreach ($this->extensions as $extension) { $extension->beforeCompile(); $this->builder->addDependency(Nette\Reflection\ClassType::from($extension)->getFileName()); } $classes = $this->builder->generateClasses($className, $parentName); $classes[0]->addMethod('initialize'); foreach ($this->extensions as $extension) { $extension->afterCompile($classes[0]); } return implode("\n\n\n", $classes); } /********************* tools ****************d*g**/ /** * Parses section 'services' from (unexpanded) configuration file. * @return void */ public static function parseServices(ContainerBuilder $builder, array $config, $namespace = NULL) { if (!empty($config['factories'])) { trigger_error("Section 'factories' is deprecated, move definitions to section 'services' and append key 'autowired: no'.", E_USER_DEPRECATED); } $services = isset($config['services']) ? $config['services'] : array(); $factories = isset($config['factories']) ? $config['factories'] : array(); $all = array_merge($services, $factories); $depths = array(); foreach ($all as $name => $def) { $path = array(); while (Config\Helpers::isInheriting($def)) { $path[] = $def; $def = isset($all[$def[Config\Helpers::EXTENDS_KEY]]) ? $all[$def[Config\Helpers::EXTENDS_KEY]] : array(); if (in_array($def, $path, TRUE)) { throw new ServiceCreationException("Circular reference detected for service '$name'."); } } $depths[$name] = count($path); } array_multisort($depths, $all); foreach ($all as $origName => $def) { if ((string) (int) $origName === (string) $origName) { $name = count($builder->getDefinitions()) . preg_replace('#\W+#', '_', $def instanceof \stdClass ? ".$def->value" : (is_scalar($def) ? ".$def" : '')); } elseif (array_key_exists($origName, $services) && array_key_exists($origName, $factories)) { throw new ServiceCreationException("It is not allowed to use services and factories with the same name: '$origName'."); } else { $name = ($namespace ? $namespace . '.' : '') . strtr($origName, '\\', '_'); } $params = $builder->parameters; if (is_array($def) && isset($def['parameters'])) { foreach ((array) $def['parameters'] as $k => $v) { $v = explode(' ', is_int($k) ? $v : $k); $params[end($v)] = $builder::literal('$' . end($v)); } } $def = Helpers::expand($def, $params); if (($parent = Config\Helpers::takeParent($def)) && $parent !== $name) { $builder->removeDefinition($name); $definition = $builder->addDefinition( $name, $parent === Config\Helpers::OVERWRITE ? NULL : unserialize(serialize($builder->getDefinition($parent))) // deep clone ); } elseif ($builder->hasDefinition($name)) { $definition = $builder->getDefinition($name); } else { $definition = $builder->addDefinition($name); } try { static::parseService($definition, $def); } catch (\Exception $e) { throw new ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e); } if ($definition->class === 'self') { $definition->class = $origName; trigger_error("Replace service definition '$origName: self' with '- $origName'.", E_USER_DEPRECATED); } if ($definition->factory && $definition->factory->entity === 'self') { $definition->factory->entity = $origName; trigger_error("Replace service definition '$origName: self' with '- $origName'.", E_USER_DEPRECATED); } } } /** * Parses single service from configuration file. * @return void */ public static function parseService(ServiceDefinition $definition, $config) { if ($config === NULL) { return; } elseif (is_string($config) && interface_exists($config)) { $config = array('class' => NULL, 'implement' => $config); } elseif ($config instanceof \stdClass && interface_exists($config->value)) { $config = array('class' => NULL, 'implement' => $config->value, 'factory' => array_shift($config->attributes)); } elseif (!is_array($config)) { $config = array('class' => NULL, 'create' => $config); } if (array_key_exists('factory', $config)) { $config['create'] = $config['factory']; unset($config['factory']); }; $known = array('class', 'create', 'arguments', 'setup', 'autowired', 'inject', 'parameters', 'implement', 'run', 'tags'); if ($error = array_diff(array_keys($config), $known)) { throw new Nette\InvalidStateException(sprintf("Unknown or deprecated key '%s' in definition of service.", implode("', '", $error))); } $arguments = array(); if (array_key_exists('arguments', $config)) { Validators::assertField($config, 'arguments', 'array'); $arguments = self::filterArguments($config['arguments']); $definition->setArguments($arguments); } if (array_key_exists('class', $config) || array_key_exists('create', $config)) { $definition->class = NULL; $definition->factory = NULL; } if (array_key_exists('class', $config)) { Validators::assertField($config, 'class', 'string|stdClass|null'); if ($config['class'] instanceof \stdClass) { $definition->setClass($config['class']->value, self::filterArguments($config['class']->attributes)); } else { $definition->setClass($config['class'], $arguments); } } if (array_key_exists('create', $config)) { Validators::assertField($config, 'create', 'callable|stdClass|null'); if ($config['create'] instanceof \stdClass) { $definition->setFactory($config['create']->value, self::filterArguments($config['create']->attributes)); } else { $definition->setFactory($config['create'], $arguments); } } if (isset($config['setup'])) { if (Config\Helpers::takeParent($config['setup'])) { $definition->setup = array(); } Validators::assertField($config, 'setup', 'list'); foreach ($config['setup'] as $id => $setup) { Validators::assert($setup, 'callable|stdClass', "setup item #$id"); if ($setup instanceof \stdClass) { Validators::assert($setup->value, 'callable', "setup item #$id"); $definition->addSetup($setup->value, self::filterArguments($setup->attributes)); } else { $definition->addSetup($setup); } } } if (isset($config['parameters'])) { Validators::assertField($config, 'parameters', 'array'); $definition->setParameters($config['parameters']); } if (isset($config['implement'])) { Validators::assertField($config, 'implement', 'string'); $definition->setImplement($config['implement']); $definition->setAutowired(TRUE); } if (isset($config['autowired'])) { Validators::assertField($config, 'autowired', 'bool'); $definition->setAutowired($config['autowired']); } if (isset($config['inject'])) { Validators::assertField($config, 'inject', 'bool'); $definition->setInject($config['inject']); } if (isset($config['run'])) { $config['tags']['run'] = (bool) $config['run']; } if (isset($config['tags'])) { Validators::assertField($config, 'tags', 'array'); if (Config\Helpers::takeParent($config['tags'])) { $definition->tags = array(); } foreach ($config['tags'] as $tag => $attrs) { if (is_int($tag) && is_string($attrs)) { $definition->addTag($attrs); } else { $definition->addTag($tag, $attrs); } } } } /** * Removes ... and replaces entities with Statement. * @return array */ public static function filterArguments(array $args) { foreach ($args as $k => $v) { if ($v === '...') { unset($args[$k]); } elseif (is_array($v)) { $args[$k] = self::filterArguments($v); } elseif ($v instanceof \stdClass && isset($v->value, $v->attributes)) { $args[$k] = new Statement($v->value, self::filterArguments($v->attributes)); } } return $args; } } src/DI/Statement.php 0000666 00000001131 13436755707 0010325 0 ustar 00 entity = $entity; $this->arguments = $arguments; } } src/DI/ContainerBuilder.php 0000666 00000053623 13436755707 0011627 0 ustar 00 definitions[$name])) { throw new Nette\InvalidStateException("Service '$name' has already been added."); } return $this->definitions[$name] = $definition ?: new ServiceDefinition; } /** * Removes the specified service definition. * @param string * @return void */ public function removeDefinition($name) { unset($this->definitions[$name]); } /** * Gets the service definition. * @param string * @return ServiceDefinition */ public function getDefinition($name) { if (!isset($this->definitions[$name])) { throw new MissingServiceException("Service '$name' not found."); } return $this->definitions[$name]; } /** * Gets all service definitions. * @return array */ public function getDefinitions() { return $this->definitions; } /** * Does the service definition exist? * @param string * @return bool */ public function hasDefinition($name) { return isset($this->definitions[$name]); } /********************* class resolving ****************d*g**/ /** * Resolves service name by type. * @param string class or interface * @return string service name or NULL * @throws ServiceCreationException */ public function getByType($class) { if ($this->currentService !== NULL && Reflection\ClassType::from($this->definitions[$this->currentService]->class)->is($class)) { return $this->currentService; } $lower = ltrim(strtolower($class), '\\'); if (!isset($this->classes[$lower])) { return; } elseif (count($this->classes[$lower]) === 1) { return $this->classes[$lower][0]; } else { throw new ServiceCreationException("Multiple services of type $class found: " . implode(', ', $this->classes[$lower])); } } /** * Gets the service objects of the specified tag. * @param string * @return array of [service name => tag attributes] */ public function findByTag($tag) { $found = array(); foreach ($this->definitions as $name => $def) { if (isset($def->tags[$tag])) { $found[$name] = $def->tags[$tag]; } } return $found; } /** * Creates a list of arguments using autowiring. * @return array */ public function autowireArguments($class, $method, array $arguments) { $rc = Reflection\ClassType::from($class); if (!$rc->hasMethod($method)) { if (!Nette\Utils\Arrays::isList($arguments)) { throw new ServiceCreationException("Unable to pass specified arguments to $class::$method()."); } return $arguments; } $rm = $rc->getMethod($method); if (!$rm->isPublic()) { throw new ServiceCreationException("$rm is not callable."); } $this->addDependency($rm->getFileName()); return Helpers::autowireArguments($rm, $arguments, $this); } /** * Generates $dependencies, $classes and normalizes class names. * @return array */ public function prepareClassList() { unset($this->definitions[self::THIS_CONTAINER]); $this->addDefinition(self::THIS_CONTAINER)->setClass('Nette\DI\Container'); $this->classes = FALSE; // prepare generated factories foreach ($this->definitions as $name => $def) { if (!$def->implement) { continue; } if (!interface_exists($def->implement)) { throw new ServiceCreationException("Interface $def->implement used in service '$name' not found."); } $rc = Reflection\ClassType::from($def->implement); $method = $rc->hasMethod('create') ? $rc->getMethod('create') : ($rc->hasMethod('get') ? $rc->getMethod('get') : NULL); if (count($rc->getMethods()) !== 1 || !$method || $method->isStatic()) { throw new ServiceCreationException("Interface $def->implement used in service '$name' must have just one non-static method create() or get()."); } $def->implement = $rc->getName(); $def->implementType = $rc->hasMethod('create') ? 'create' : 'get'; if (!$def->class && empty($def->factory->entity)) { $returnType = $method->getAnnotation('return'); if (!$returnType) { throw new ServiceCreationException("Method $method used in service '$name' has no @return annotation."); } $returnType = Reflection\AnnotationsParser::expandClassName(preg_replace('#[|\s].*#', '', $returnType), $rc); if (!class_exists($returnType)) { throw new ServiceCreationException("Please check a @return annotation of the $method method. Class '$returnType' cannot be found."); } $def->setClass($returnType); } if ($method->getName() === 'get') { if ($method->getParameters()) { throw new ServiceCreationException("Method $method used in service '$name' must have no arguments."); } if (empty($def->factory->entity)) { $def->setFactory('@\\' . ltrim($def->class, '\\')); } elseif (!$this->getServiceName($def->factory->entity)) { throw new ServiceCreationException("Invalid factory in service '$name' definition."); } } if (!$def->parameters) { foreach ($method->getParameters() as $param) { $paramDef = ($param->isArray() ? 'array' : $param->getClassName()) . ' ' . $param->getName(); if ($param->isOptional()) { $def->parameters[$paramDef] = $param->getDefaultValue(); } else { $def->parameters[] = $paramDef; } } } } // complete class-factory pairs foreach ($this->definitions as $name => $def) { if (!$def->factory || !$def->factory->entity) { if (!$def->class) { throw new ServiceCreationException("Class and factory are missing in service '$name' definition."); } if ($def->factory) { $def->factory->entity = $def->class; } else { $def->factory = new Statement($def->class); } } } // check if services are instantiable foreach ($this->definitions as $name => $def) { $factory = $def->factory->entity = $this->normalizeEntity($def->factory->entity); if (is_string($factory) && preg_match('#^[\w\\\\]+\z#', $factory) && $factory !== self::THIS_SERVICE) { if (!class_exists($factory) || !Reflection\ClassType::from($factory)->isInstantiable()) { throw new ServiceCreationException("Class $factory used in service '$name' not found or is not instantiable."); } } } // complete classes foreach ($this->definitions as $name => $def) { $this->resolveClass($name); if (!$def->class) { continue; } elseif (!class_exists($def->class) && !interface_exists($def->class)) { throw new ServiceCreationException("Class or interface $def->class used in service '$name' not found."); } else { $def->class = Reflection\ClassType::from($def->class)->getName(); } } // build auto-wiring list $this->classes = array(); foreach ($this->definitions as $name => $def) { $class = $def->implement ?: $def->class; if ($def->autowired && $class) { foreach (class_parents($class) + class_implements($class) + array($class) as $parent) { $this->classes[strtolower($parent)][] = (string) $name; } } } foreach ($this->excludedClasses as $class) { foreach (class_parents($class) + class_implements($class) + array($class) as $parent) { unset($this->classes[strtolower($parent)]); } } foreach ($this->classes as $class => $foo) { $this->addDependency(Reflection\ClassType::from($class)->getFileName()); } } private function resolveClass($name, $recursive = array()) { if (isset($recursive[$name])) { throw new ServiceCreationException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($recursive)))); } $recursive[$name] = TRUE; $def = $this->definitions[$name]; $factory = $def->factory->entity; if ($def->class) { return $def->class; } elseif (is_array($factory)) { // method calling if ($service = $this->getServiceName($factory[0])) { if (Strings::contains($service, '\\')) { // @\Class $factory[0] = $service; } else { $factory[0] = $this->resolveClass($service, $recursive); if (!$factory[0]) { return; } if ($this->definitions[$service]->implement && $factory[1] === 'create') { return $def->class = $factory[0]; } } } try { $reflection = Nette\Utils\Callback::toReflection($factory); } catch (\ReflectionException $e) { } if (isset($e) || !is_callable($factory)) { throw new ServiceCreationException(sprintf("Factory '%s' used in service '%s' is not callable.", Nette\Utils\Callback::toString($factory), $name)); } $def->class = preg_replace('#[|\s].*#', '', $reflection->getAnnotation('return')); if ($def->class && $reflection instanceof \ReflectionMethod) { $def->class = Reflection\AnnotationsParser::expandClassName($def->class, $reflection->getDeclaringClass()); } } elseif ($service = $this->getServiceName($factory)) { // alias or factory if (!$def->implement) { $def->autowired = FALSE; } if (Strings::contains($service, '\\')) { // @\Class return $def->class = $service; } if ($this->definitions[$service]->implement) { $def->autowired = FALSE; } return $def->class = $this->definitions[$service]->implement ?: $this->resolveClass($service, $recursive); } else { return $def->class = $factory; // class name } } /** * @param string[] * @return self */ public function addExcludedClasses(array $classes) { $this->excludedClasses = array_merge($this->excludedClasses, $classes); return $this; } /** * Adds a file to the list of dependencies. * @return self */ public function addDependency($file) { $this->dependencies[$file] = TRUE; return $this; } /** * Returns the list of dependent files. * @return array */ public function getDependencies() { unset($this->dependencies[FALSE]); return array_keys($this->dependencies); } /********************* code generator ****************d*g**/ /** * Generates PHP classes. First class is the container. * @return Nette\PhpGenerator\ClassType[] */ public function generateClasses($className = 'Container', $parentName = 'Nette\DI\Container') { $this->generatedClasses = array(); $this->prepareClassList(); $containerClass = $this->generatedClasses[] = new Nette\PhpGenerator\ClassType($className); $containerClass->setExtends($parentName); $containerClass->addMethod('__construct') ->addBody('parent::__construct(?);', array($this->parameters)); $definitions = $this->definitions; ksort($definitions); $meta = $containerClass->addProperty('meta', array()) ->setVisibility('protected') ->setValue(array(Container::TYPES => $this->classes)); foreach ($definitions as $name => $def) { foreach ($def->tags as $tag => $value) { $meta->value[Container::TAGS][$tag][$name] = $value; } } foreach ($definitions as $name => $def) { try { $name = (string) $name; $methodName = Container::getMethodName($name); if (!PhpHelpers::isIdentifier($methodName)) { throw new ServiceCreationException('Name contains invalid characters.'); } $containerClass->addMethod($methodName) ->addDocument('@return ' . ($def->implement ?: $def->class)) ->setBody($name === self::THIS_CONTAINER ? 'return $this;' : $this->generateService($name)) ->setParameters($def->implement ? array() : $this->convertParameters($def->parameters)); } catch (\Exception $e) { throw new ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e); } } return $this->generatedClasses; } /** * Generates body of service method. * @return string */ private function generateService($name) { $this->currentService = NULL; $def = $this->definitions[$name]; $serviceRef = $this->getServiceName($def->factory->entity); $factory = $serviceRef && !$def->factory->arguments && !$def->setup && $def->implementType !== 'create' ? new Statement(array('@' . ContainerBuilder::THIS_CONTAINER, 'getService'), array($serviceRef)) : $def->factory; $code = '$service = ' . $this->formatStatement($factory) . ";\n"; $this->currentService = $name; if ($def->class && $def->class !== $def->factory->entity && !$serviceRef) { $code .= PhpHelpers::formatArgs("if (!\$service instanceof $def->class) {\n" . "\tthrow new Nette\\UnexpectedValueException(?);\n}\n", array("Unable to create service '$name', value returned by factory is not $def->class type.") ); } $setups = (array) $def->setup; if ($def->inject && $def->class) { $injects = array(); foreach (Helpers::getInjectProperties(Reflection\ClassType::from($def->class), $this) as $property => $type) { $injects[] = new Statement('$' . $property, array('@\\' . ltrim($type, '\\'))); } foreach (get_class_methods($def->class) as $method) { if (substr($method, 0, 6) === 'inject') { $injects[] = new Statement($method); } } foreach ($injects as $inject) { foreach ($setups as $key => $setup) { if ($setup->entity === $inject->entity) { $inject = $setup; unset($setups[$key]); } } array_unshift($setups, $inject); } } foreach ($setups as $setup) { if (is_string($setup->entity) && strpbrk($setup->entity, ':@?') === FALSE) { // auto-prepend @self $setup->entity = array('@self', $setup->entity); } $code .= $this->formatStatement($setup) . ";\n"; } $code .= 'return $service;'; if (!$def->implement) { return $code; } $factoryClass = $this->generatedClasses[] = new Nette\PhpGenerator\ClassType; $factoryClass->setName(str_replace(array('\\', '.'), '_', "{$this->generatedClasses[0]->name}_{$def->implement}Impl_{$name}")) ->addImplement($def->implement) ->setFinal(TRUE); $factoryClass->addProperty('container') ->setVisibility('private'); $factoryClass->addMethod('__construct') ->addBody('$this->container = $container;') ->addParameter('container') ->setTypeHint('Nette\DI\Container'); $factoryClass->addMethod($def->implementType) ->setParameters($this->convertParameters($def->parameters)) ->setBody(str_replace('$this', '$this->container', $code)); return "return new {$factoryClass->name}(\$this);"; } /** * Converts parameters from ServiceDefinition to PhpGenerator. * @return Nette\PhpGenerator\Parameter[] */ private function convertParameters(array $parameters) { $res = array(); foreach ($parameters as $k => $v) { $tmp = explode(' ', is_int($k) ? $v : $k); $param = $res[] = new Nette\PhpGenerator\Parameter; $param->setName(end($tmp)); if (!is_int($k)) { $param = $param->setOptional(TRUE)->setDefaultValue($v); } if (isset($tmp[1])) { $param->setTypeHint($tmp[0]); } } return $res; } /** * 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->entity); $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 = array(); 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 Reflection\GlobalFunction(create_function(implode(', ', $params), '')); $arguments = Helpers::autowireArguments($rm, $arguments, $this); return $this->formatPhp('$this->?(?*)', array(Container::getMethodName($service), $arguments)); } elseif ($entity === 'not') { // operator return $this->formatPhp('!?', array($arguments[0])); } elseif (is_string($entity)) { // class name if ($constructor = Reflection\ClassType::from($entity)->getConstructor()) { $this->addDependency($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 ? '(?*)' : ''), array($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 ($entity[0] === '') { // globalFunc return $this->formatPhp("$entity[1](?*)", array($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('?->? = ?', array($entity[0], substr($entity[1], 1), $arguments[0])); } else { return $this->formatPhp($entity[0] . '::$? = ?', array(substr($entity[1], 1), $arguments[0])); } } elseif ($service = $this->getServiceName($entity[0])) { // service method $class = $this->definitions[$service]->implement; if (!$class || !method_exists($class, $entity[1])) { $class = $this->definitions[$service]->class; } if ($class) { $arguments = $this->autowireArguments($class, $entity[1], $arguments); } return $this->formatPhp('?->?(?*)', array($entity[0], $entity[1], $arguments)); } else { // static method $arguments = $this->autowireArguments($entity[0], $entity[1], $arguments); return $this->formatPhp("$entity[0]::$entity[1](?*)", array($arguments)); } } /** * Formats PHP statement. * @return string */ public function formatPhp($statement, $args) { $that = $this; array_walk_recursive($args, function(& $val) use ($that) { if ($val instanceof Statement) { $val = ContainerBuilder::literal($that->formatStatement($val)); } elseif ($val === $that) { $val = ContainerBuilder::literal('$this'); } elseif ($val instanceof ServiceDefinition) { $val = '@' . current(array_keys($that->definitions, $val, TRUE)); } elseif (is_string($val) && preg_match('#^[\w\\\\]*::[A-Z][A-Z0-9_]*\z#', $val, $m)) { $val = ContainerBuilder::literal(ltrim($val, ':')); } if (is_string($val) && substr($val, 0, 1) === '@') { $pair = explode('::', $val, 2); $name = $that->getServiceName($pair[0]); if (isset($pair[1]) && preg_match('#^[A-Z][A-Z0-9_]*\z#', $pair[1], $m)) { $val = $that->definitions[$name]->class . '::' . $pair[1]; } else { if ($name === ContainerBuilder::THIS_CONTAINER) { $val = '$this'; } elseif ($name === $that->currentService) { $val = '$service'; } else { $val = $that->formatStatement(new Statement(array('@' . ContainerBuilder::THIS_CONTAINER, 'getService'), array($name))); } $val .= (isset($pair[1]) ? PhpHelpers::formatArgs('->?', array($pair[1])) : ''); } $val = ContainerBuilder::literal($val); } }); return PhpHelpers::formatArgs($statement, $args); } /** * Expands %placeholders% in strings. * @return mixed */ public function expand($value) { return Helpers::expand($value, $this->parameters); } /** * @return Nette\PhpGenerator\PhpLiteral */ public static function literal($phpCode) { return new Nette\PhpGenerator\PhpLiteral($phpCode); } /** @internal */ public function normalizeEntity($entity) { if (is_string($entity) && Strings::contains($entity, '::') && !Strings::contains($entity, '?')) { // Class::method -> [Class, method] $entity = explode('::', $entity); } if (is_array($entity) && $entity[0] instanceof ServiceDefinition) { // [ServiceDefinition, ...] -> [@serviceName, ...] $entity[0] = '@' . current(array_keys($this->definitions, $entity[0], TRUE)); } elseif ($entity instanceof ServiceDefinition) { // ServiceDefinition -> @serviceName $entity = '@' . current(array_keys($this->definitions, $entity, TRUE)); } elseif (is_array($entity) && $entity[0] === $this) { // [$this, ...] -> [@container, ...] $entity[0] = '@' . ContainerBuilder::THIS_CONTAINER; } return $entity; // Class, @service, [Class, member], [@service, member], [, globalFunc] } /** * Converts @service or @\Class -> service name and checks its existence. * @return string of FALSE, if argument is not service name */ public function getServiceName($arg) { if (!is_string($arg) || !preg_match('#^@[\w\\\\.].*\z#', $arg)) { return FALSE; } $service = substr($arg, 1); if ($service === self::THIS_SERVICE) { $service = $this->currentService; } if (Strings::contains($service, '\\')) { if ($this->classes === FALSE) { // may be disabled by prepareClassList return $service; } $res = $this->getByType($service); if (!$res) { throw new ServiceCreationException("Reference to missing service of type $service."); } return $res; } if (!isset($this->definitions[$service])) { throw new ServiceCreationException("Reference to missing service '$service'."); } return $service; } } src/DI/ServiceDefinition.php 0000666 00000005043 13436755707 0012000 0 ustar 00 class = $class; if ($args) { $this->setFactory($class, $args); } return $this; } public function setFactory($factory, array $args = array()) { $this->factory = $factory instanceof Statement ? $factory : new Statement($factory, $args); return $this; } public function setArguments(array $args = array()) { if ($this->factory) { $this->factory->arguments = $args; } else { $this->setClass($this->class, $args); } return $this; } public function addSetup($target, array $args = array()) { $this->setup[] = new Statement($target, $args); return $this; } public function addTag($tag, $attrs = TRUE) { $this->tags[$tag] = $attrs; return $this; } public function getTag($tag) { return isset($this->tags[$tag]) ? $this->tags[$tag] : NULL; } /** @deprecated */ public function setShared($on) { trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED); $this->autowired = $on ? $this->autowired : FALSE; return $this; } /** @deprecated */ public function isShared() { trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED); } } src/DI/ContainerFactory.php 0000666 00000007007 13436755707 0011643 0 ustar 00 tempDirectory = $tempDirectory; } /** * @return Container */ public function create() { if (!class_exists($this->class)) { $this->loadClass(); } return new $this->class; } /** * @return string */ protected function generateCode() { $compiler = $this->createCompiler(); $config = $this->generateConfig(); $this->onCompile($this, $compiler, $config); $code = "configFiles as $info) { if (is_scalar($info[0])) { $code .= "// source: $info[0] $info[1]\n"; } } $code .= "\n" . $compiler->compile($config, $this->class, $this->parentClass); if ($this->autoRebuild !== 'compat') { // back compatibility $this->dependencies = array_merge($this->dependencies, $compiler->getContainerBuilder()->getDependencies()); } return $code; } /** * @return array */ protected function generateConfig() { $config = array(); $loader = $this->createLoader(); foreach ($this->configFiles as $info) { $info = is_scalar($info[0]) ? $loader->load($info[0], $info[1]) : $info[0]; $config = Config\Helpers::merge($info, $config); } $this->dependencies = array_merge($this->dependencies, $loader->getDependencies()); return Config\Helpers::merge($config, $this->config); } /** * @return void */ private function loadClass() { $key = md5(serialize(array($this->config, $this->configFiles, $this->class, $this->parentClass))); $handle = fopen($file = "$this->tempDirectory/$key.php", 'c+'); if (!$handle) { throw new Nette\IOException("Unable to open or create file '$file'."); } flock($handle, LOCK_SH); $stat = fstat($handle); if ($stat['size']) { if ($this->autoRebuild) { foreach ((array) @unserialize(file_get_contents($file . '.meta')) as $f => $time) { // @ - file may not exist if (@filemtime($f) !== $time) { // @ - stat may fail goto write; } } } } else { write: ftruncate($handle, 0); flock($handle, LOCK_EX); $stat = fstat($handle); if (!$stat['size']) { $this->dependencies = array(); $code = $this->generateCode(); if (fwrite($handle, $code, strlen($code)) !== strlen($code)) { ftruncate($handle, 0); throw new Nette\IOException("Unable to write file '$file'."); } $tmp = array(); foreach ($this->dependencies as $f) { $tmp[$f] = @filemtime($f); // @ - stat may fail } file_put_contents($file . '.meta', serialize($tmp)); } flock($handle, LOCK_SH); } require $file; } /** * @return Compiler */ protected function createCompiler() { return new Compiler; } /** * @return Config\Loader */ protected function createLoader() { return new Config\Loader; } } src/DI/Container.php 0000666 00000020014 13436755707 0010304 0 ustar 00 parameters = $params + $this->parameters; } /** * @return array */ public function getParameters() { return $this->parameters; } /** * Adds the service to the container. * @param string * @param object * @return self */ public function addService($name, $service) { if (func_num_args() > 2) { throw new Nette\DeprecatedException('Parameter $meta has been removed.'); } elseif (!is_string($name) || !$name) { throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($name))); } elseif (isset($this->registry[$name])) { throw new Nette\InvalidStateException("Service '$name' already exists."); } elseif (is_string($service) || is_array($service) || $service instanceof \Closure || $service instanceof Nette\Callback) { trigger_error(sprintf('Passing factories to %s() is deprecated; pass the object itself.', __METHOD__), E_USER_DEPRECATED); $service = is_string($service) && !preg_match('#\x00|:#', $service) ? new $service : call_user_func($service, $this); } if (!is_object($service)) { throw new Nette\InvalidArgumentException(sprintf('Service must be a object, %s given.', gettype($service))); } $this->registry[$name] = $service; return $this; } /** * Removes the service from the container. * @param string * @return void */ public function removeService($name) { unset($this->registry[$name]); } /** * Gets the service object by name. * @param string * @return object * @throws MissingServiceException */ public function getService($name) { if (!isset($this->registry[$name])) { $this->registry[$name] = $this->createService($name); } return $this->registry[$name]; } /** * Does the service exist? * @param string service name * @return bool */ public function hasService($name) { return isset($this->registry[$name]) || method_exists($this, $method = Container::getMethodName($name)) && $this->getReflection()->getMethod($method)->getName() === $method; } /** * Is the service created? * @param string service name * @return bool */ public function isCreated($name) { if (!$this->hasService($name)) { throw new MissingServiceException("Service '$name' not found."); } return isset($this->registry[$name]); } /** * Creates new instance of the service. * @param string service name * @return object * @throws MissingServiceException */ public function createService($name, array $args = array()) { $method = Container::getMethodName($name); if (isset($this->creating[$name])) { throw new Nette\InvalidStateException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($this->creating)))); } elseif (!method_exists($this, $method) || $this->getReflection()->getMethod($method)->getName() !== $method) { throw new MissingServiceException("Service '$name' not found."); } $this->creating[$name] = TRUE; try { $service = call_user_func_array(array($this, $method), $args); } catch (\Exception $e) { unset($this->creating[$name]); throw $e; } unset($this->creating[$name]); if (!is_object($service)) { throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by method $method() is not object."); } return $service; } /** * Resolves service by type. * @param string class or interface * @param bool throw exception if service doesn't exist? * @return object service or NULL * @throws MissingServiceException */ public function getByType($class, $need = TRUE) { $names = $this->findByType($class); if (!$names) { if ($need) { throw new MissingServiceException("Service of type $class not found."); } } elseif (count($names) > 1) { throw new MissingServiceException("Multiple services of type $class found: " . implode(', ', $names) . '.'); } else { return $this->getService($names[0]); } } /** * Gets the service names of the specified type. * @param string * @return string[] */ public function findByType($class) { $class = ltrim(strtolower($class), '\\'); return isset($this->meta[self::TYPES][$class]) ? $this->meta[self::TYPES][$class] : array(); } /** * Gets the service names of the specified tag. * @param string * @return array of [service name => tag attributes] */ public function findByTag($tag) { return isset($this->meta[self::TAGS][$tag]) ? $this->meta[self::TAGS][$tag] : array(); } /********************* autowiring ****************d*g**/ /** * Creates new instance using autowiring. * @param string class * @param array arguments * @return object * @throws Nette\InvalidArgumentException */ public function createInstance($class, array $args = array()) { $rc = Nette\Reflection\ClassType::from($class); if (!$rc->isInstantiable()) { throw new ServiceCreationException("Class $class is not instantiable."); } elseif ($constructor = $rc->getConstructor()) { return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this)); } elseif ($args) { throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor."); } return new $class; } /** * Calls all methods starting with with "inject" using autowiring. * @param object * @return void */ public function callInjects($service) { if (!is_object($service)) { throw new Nette\InvalidArgumentException(sprintf('Service must be object, %s given.', gettype($service))); } foreach (array_reverse(get_class_methods($service)) as $method) { if (substr($method, 0, 6) === 'inject') { $this->callMethod(array($service, $method)); } } foreach (Helpers::getInjectProperties(Nette\Reflection\ClassType::from($service), $this) as $property => $type) { $service->$property = $this->getByType($type); } } /** * Calls method using autowiring. * @param mixed class, object, function, callable * @param array arguments * @return mixed */ public function callMethod($function, array $args = array()) { return call_user_func_array( $function, Helpers::autowireArguments(Nette\Utils\Callback::toReflection($function), $args, $this) ); } /********************* shortcuts ****************d*g**/ /** * Expands %placeholders%. * @param mixed * @return mixed */ public function expand($s) { return Helpers::expand($s, $this->parameters); } /** @deprecated */ public function &__get($name) { $this->error(__METHOD__, 'getService'); $tmp = $this->getService($name); return $tmp; } /** @deprecated */ public function __set($name, $service) { $this->error(__METHOD__, 'addService'); $this->addService($name, $service); } /** @deprecated */ public function __isset($name) { $this->error(__METHOD__, 'hasService'); return $this->hasService($name); } /** @deprecated */ public function __unset($name) { $this->error(__METHOD__, 'removeService'); $this->removeService($name); } private function error($oldName, $newName) { if (empty($this->parameters['container']['accessors'])) { trigger_error("$oldName() is deprecated; use $newName() or enable nette.container.accessors in configuration.", E_USER_DEPRECATED); } } public static function getMethodName($name) { $uname = ucfirst($name); return 'createService' . ((string) $name === $uname ? '__' : '') . str_replace('.', '__', $uname); } } src/Bridges/DITracy/ContainerPanel.php 0000666 00000003374 13436755707 0013660 0 ustar 00 container = $container; } /** * Renders tab. * @return string */ public function getTab() { ob_start(); require __DIR__ . '/templates/ContainerPanel.tab.phtml'; return ob_get_clean(); } /** * Renders panel. * @return string */ public function getPanel() { $services = array(); foreach (Nette\Reflection\ClassType::from($this->container)->getMethods() as $method) { if (preg_match('#^createService_*(.+)\z#', $method->getName(), $m)) { $services[str_replace('__', '.', strtolower(substr($m[1], 0, 1)) . substr($m[1], 1))] = $method->getAnnotation('return'); } } ksort($services); $container = $this->container; $registry = $this->getContainerProperty('registry'); $tags = array(); $meta = $this->getContainerProperty('meta'); if (isset($meta[Container::TAGS])) { foreach ($meta[Container::TAGS] as $tag => $tmp) { foreach ($tmp as $service => $val) { $tags[$service][$tag] = $val; } } } ob_start(); require __DIR__ . '/templates/ContainerPanel.panel.phtml'; return ob_get_clean(); } private function getContainerProperty($name) { $prop = Nette\Reflection\ClassType::from('Nette\DI\Container')->getProperty($name); $prop->setAccessible(TRUE); return $prop->getValue($this->container); } } src/Bridges/DITracy/templates/ContainerPanel.panel.phtml 0000666 00000003341 13436755707 0017303 0 ustar 00
Name | Autowired | Service | Tags |
---|---|---|---|
TRUE)); ?>
|
TRUE)); } ?> |