contributing.md 0000666 00000002317 13436754664 0007626 0 ustar 00 How to contribute & use the issue tracker ========================================= The issue tracker is the preferred channel for bug reports, features requests and submitting pull requests, but please respect the following restrictions: * Please **do not** use the issue tracker for personal support requests (use [Nette forum](http://forum.nette.org) or [Stack Overflow](http://stackoverflow.com)). * Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others. * Use the GitHub **issue search** — check if the issue has already been reported. A good **bug report** shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. **Feature requests** are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Nette welcomes **pull requests**. If you'd like to contribute, please take a moment to [read the guidelines](http://nette.org/en/contributing) in order to make the contribution process easy and effective for everyone involved. Thanks! src/DI/CompilerExtension.php 0000666 00000005217 13436754664 0012042 0 ustar 00 compiler = $compiler; $this->name = $name; return $this; } public function setConfig(array $config) { $this->config = $config; return $this; } /** * Returns extension configuration. * @return array */ public function getConfig() { if (func_num_args()) { // deprecated return Config\Helpers::merge($this->config, $this->getContainerBuilder()->expand(func_get_arg(0))); } return $this->config; } /** * Checks whether $config contains only $expected items and returns combined array. * @return array * @throws Nette\InvalidStateException */ public function validateConfig(array $expected, array $config = NULL, $name = NULL) { if (func_num_args() === 1) { return $this->config = $this->validateConfig($expected, $this->config); } if ($extra = array_diff_key((array) $config, $expected)) { $name = $name ?: $this->name; $extra = implode(", $name.", array_keys($extra)); throw new Nette\InvalidStateException("Unknown configuration option $name.$extra."); } return Config\Helpers::merge($config, $expected); } /** * @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); $this->compiler->addDependencies($loader->getDependencies()); 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/DecoratorExtension.php 0000666 00000002733 13436754664 0014351 0 ustar 00 array(), 'tags' => array(), 'inject' => NULL, ); public function beforeCompile() { foreach ($this->getConfig() as $class => $info) { $info = $this->validateConfig($this->defaults, $info, $this->prefix($class)); if ($info['inject'] !== NULL) { $info['tags'][InjectExtension::TAG_INJECT] = $info['inject']; } $this->addSetups($class, (array) $info['setup']); $this->addTags($class, (array) $info['tags']); } } public function addSetups($type, array $setups) { foreach ($this->findByType($type) as $def) { foreach ($setups as $setup) { $def->addSetup($setup); } } } public function addTags($type, array $tags) { $tags = Nette\Utils\Arrays::normalize($tags, TRUE); foreach ($this->findByType($type) as $def) { $def->setTags($def->getTags() + $tags); } } private function findByType($type) { $type = ltrim($type, '\\'); return array_filter($this->getContainerBuilder()->getDefinitions(), function ($def) use ($type) { return $def->getClass() === $type || is_subclass_of($def->getClass(), $type) || (PHP_VERSION_ID < 50307 && array_key_exists($type, class_implements($def->getClass()))); }); } } src/DI/Extensions/InjectExtension.php 0000666 00000007007 13436754664 0013642 0 ustar 00 getContainerBuilder()->getDefinitions() as $def) { if ($def->getTag(self::TAG_INJECT) && $def->getClass()) { $this->updateDefinition($def); } } } private function updateDefinition($def) { $class = $def->getClass(); $builder = $this->getContainerBuilder(); $injects = array(); foreach (self::getInjectProperties($class) as $property => $type) { self::checkType($class, $property, $type, $builder); $injects[] = new DI\Statement('$' . $property, array('@\\' . ltrim($type, '\\'))); } foreach (self::getInjectMethods($def->getClass()) as $method) { $injects[] = new DI\Statement($method); } $setups = $def->getSetup(); foreach ($injects as $inject) { foreach ($setups as $key => $setup) { if ($setup->getEntity() === $inject->getEntity()) { $inject = $setup; unset($setups[$key]); } } array_unshift($setups, $inject); } $def->setSetup($setups); } /** * Generates list of inject methods. * @return array * @internal */ public static function getInjectMethods($class) { return array_values(array_filter(get_class_methods($class), function ($name) { return substr($name, 0, 6) === 'inject'; })); } /** * Generates list of properties with annotation @inject. * @return array * @internal */ public static function getInjectProperties($class) { $res = array(); foreach (get_class_vars($class) as $name => $foo) { $rp = new \ReflectionProperty($class, $name); if (PhpReflection::parseAnnotation($rp, 'inject') !== NULL) { if ($type = PhpReflection::parseAnnotation($rp, 'var')) { $type = PhpReflection::expandClassName($type, PhpReflection::getDeclaringClass($rp)); } $res[$name] = $type; } } return $res; } /** * Calls all methods starting with with "inject" using autowiring. * @return void */ public static function callInjects(DI\Container $container, $service) { if (!is_object($service)) { throw new Nette\InvalidArgumentException(sprintf('Service must be object, %s given.', gettype($service))); } foreach (array_reverse(self::getInjectMethods($service)) as $method) { $container->callMethod(array($service, $method)); } foreach (self::getInjectProperties(get_class($service)) as $property => $type) { self::checkType($service, $property, $type, $container); $service->$property = $container->getByType($type); } } /** @internal */ private static function checkType($class, $name, $type, $container) { $rc = PhpReflection::getDeclaringClass(new \ReflectionProperty($class, $name)); $fullname = $rc->getName() . '::$' . $name; if (!$type) { throw new Nette\InvalidStateException("Property $fullname has no @var annotation."); } elseif (!class_exists($type) && !interface_exists($type)) { throw new Nette\InvalidStateException("Class or interface '$type' used in @var annotation at $fullname not found. Check annotation and 'use' statements."); } elseif (!$container->getByType($type, FALSE)) { throw new Nette\InvalidStateException("Service of type {$type} used in @var annotation at $fullname not found. Did you register it in configuration file?"); } } } src/DI/Extensions/ConstantsExtension.php 0000666 00000000756 13436754664 0014406 0 ustar 00 getConfig() as $name => $value) { $class->getMethod('initialize')->addBody('define(?, ?);', array($name, $value)); } } } src/DI/Extensions/ExtensionsExtension.php 0000666 00000001236 13436754664 0014563 0 ustar 00 getConfig() as $name => $class) { if ($class instanceof Nette\DI\Statement) { $rc = new \ReflectionClass($class->getEntity()); $this->compiler->addExtension($name, $rc->newInstanceArgs($class->arguments)); } else { $this->compiler->addExtension($name, new $class); } } } } src/DI/Extensions/DIExtension.php 0000666 00000003277 13436754664 0012727 0 ustar 00 FALSE, 'accessors' => FALSE, ); /** @var bool */ private $debugMode; /** @var int */ private $time; public function __construct($debugMode = FALSE) { $this->debugMode = $debugMode; $this->time = microtime(TRUE); } public function loadConfiguration() { $config = $this->validateConfig($this->defaults); if ($config['accessors']) { $this->getContainerBuilder()->parameters['container']['accessors'] = TRUE; } } public function afterCompile(Nette\PhpGenerator\ClassType $class) { $initialize = $class->getMethod('initialize'); $container = $this->getContainerBuilder(); if ($this->debugMode && $this->config['debugger']) { Nette\Bridges\DITracy\ContainerPanel::$compilationTime = $this->time; $initialize->addBody($container->formatPhp('?;', array( new Nette\DI\Statement('@Tracy\Bar::addPanel', array(new Nette\DI\Statement('Nette\Bridges\DITracy\ContainerPanel'))), ))); } foreach (array_filter($container->findByTag('run')) as $name => $on) { $initialize->addBody('$this->getService(?);', array($name)); } if (!empty($this->config['accessors'])) { $definitions = $container->getDefinitions(); ksort($definitions); foreach ($definitions as $name => $def) { if (Nette\PhpGenerator\Helpers::isIdentifier($name)) { $type = $def->getImplement() ?: $def->getClass(); $class->addDocument("@property $type \$$name"); } } } } } src/DI/Extensions/PhpExtension.php 0000666 00000002470 13436754664 0013154 0 ustar 00 getMethod('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 00000010206 13436754664 0007767 0 ustar 00 $val) { $res[$key] = self::expand($val, $params, $recursive); } return $res; } elseif ($var instanceof Statement) { return new Statement(self::expand($var->getEntity(), $params, $recursive), self::expand($var->arguments, $params, $recursive)); } 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. * @return array */ public static function autowireArguments(\ReflectionFunctionAbstract $method, array $arguments, $container) { $optCount = 0; $num = -1; $res = array(); $methodName = ($method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' : '') . $method->getName() . '()'; 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 = PhpReflection::getPropertyType($parameter)) { // 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 $methodName not found. Did you register it in configuration file?"); } else { throw new ServiceCreationException("Class {$class} needed by $methodName not found. Check type hint and 'use' statements."); } } else { if ($container instanceof ContainerBuilder) { $res[$num] = '@' . $res[$num]; } $optCount = 0; } } elseif ($parameter->isOptional() || $parameter->isDefaultValueAvailable()) { // !optional + defaultAvailable = func($a = NULL, $b) since 5.3.17 & 5.4.7 // optional + !defaultAvailable = i.e. Exception::__construct, mysqli::mysqli, ... $res[$num] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : NULL; $optCount++; } else { throw new ServiceCreationException("Parameter \${$parameter->getName()} in $methodName 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 $methodName."); } return $optCount ? array_slice($res, 0, -$optCount) : $res; } } src/DI/Config/Helpers.php 0000666 00000003164 13436754664 0011201 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 00000001251 13436754664 0013365 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 00000004733 13436754664 0013545 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) { if ($val->value === Neon\Neon::CHAIN) { $tmp = NULL; foreach ($this->process($val->attributes) as $st) { $tmp = new Statement( $tmp === NULL ? $st->getEntity() : array($tmp, ltrim($st->getEntity(), ':')), $st->arguments ); } $val = $tmp; } else { $tmp = $this->process(array($val->value)); $val = new Statement($tmp[0], $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 00000000722 13436754664 0011265 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 13436754664 0010545 0 ustar 00 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($this->extensions[$name]) || isset(self::$reserved[$name])) { throw new Nette\InvalidArgumentException("Name '$name' is already used or 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; } /** * Adds new configuration. * @return self */ public function addConfig(array $config) { $this->config = Config\Helpers::merge($config, $this->config); return $this; } /** * Adds new configuration from file. * @return self */ public function loadConfig($file) { $loader = new Config\Loader; $this->addConfig($loader->load($file)); $this->addDependencies($loader->getDependencies()); return $this; } /** * Returns configuration. * @return array */ public function getConfig() { return $this->config; } /** * Adds a files to the list of dependencies. * @return self */ public function addDependencies(array $files) { $this->dependencies = array_merge($this->dependencies, $files); return $this; } /** * Returns the unique list of dependent files. * @return array */ public function getDependencies() { return array_values(array_unique(array_filter($this->dependencies))); } /** * @return Nette\PhpGenerator\ClassType[] */ public function compile(array $config = NULL, $className = NULL, $parentName = NULL) { $this->config = $config ?: $this->config; $this->processParameters(); $this->processExtensions(); $this->processServices(); $classes = $this->generateCode($className, $parentName); return func_num_args() ? implode("\n\n\n", $classes) // back compatiblity : $classes; } /** @internal */ public function processParameters() { if (isset($this->config['parameters'])) { $this->builder->parameters = Helpers::expand($this->config['parameters'], $this->config['parameters'], TRUE); } } /** @internal */ public function processExtensions() { $last = $this->getExtensions('Nette\DI\Extensions\InjectExtension'); $this->extensions = array_merge(array_diff_key($this->extensions, $last), $last); $this->config = Helpers::expand(array_diff_key($this->config, self::$reserved), $this->builder->parameters) + array_intersect_key($this->config, self::$reserved); foreach ($first = $this->getExtensions('Nette\DI\Extensions\ExtensionsExtension') as $name => $extension) { $extension->setConfig(isset($this->config[$name]) ? $this->config[$name] : array()); $extension->loadConfiguration(); } $extensions = array_diff_key($this->extensions, $first); foreach (array_intersect_key($extensions, $this->config) as $name => $extension) { if (isset($this->config[$name]['services'])) { trigger_error("Support for inner section 'services' inside extension was removed (used in '$name').", E_USER_DEPRECATED); } $extension->setConfig($this->config[$name] ?: array()); } foreach ($extensions as $extension) { $extension->loadConfiguration(); } if ($extra = array_diff_key($this->extensions, $extensions, $first)) { $extra = implode("', '", array_keys($extra)); throw new Nette\DeprecatedException("Extensions '$extra' were added while container was being compiled."); } elseif ($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."); } } /** @internal */ public function processServices() { $this->parseServices($this->builder, $this->config); } /** @internal */ public function generateCode($className, $parentName = NULL) { $this->builder->prepareClassList(); foreach ($this->extensions as $extension) { $extension->beforeCompile(); $rc = new \ReflectionClass($extension); $this->dependencies[] = $rc->getFileName(); } $classes = $this->builder->generateClasses($className, $parentName); $classes[0]->addMethod('initialize'); $this->addDependencies($this->builder->getDependencies()); foreach ($this->extensions as $extension) { $extension->afterCompile($classes[0]); } return $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'])) { throw new Nette\DeprecatedException("Section 'factories' is deprecated, move definitions to section 'services' and append key 'autowired: no'."); } $services = isset($config['services']) ? $config['services'] : array(); $depths = array(); foreach ($services as $name => $def) { $path = array(); while (Config\Helpers::isInheriting($def)) { $path[] = $def; $def = isset($services[$def[Config\Helpers::EXTENDS_KEY]]) ? $services[$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, $services); foreach ($services as $origName => $def) { if ((string) (int) $origName === (string) $origName) { $postfix = $def instanceof Statement && is_string($def->getEntity()) ? '.' . $def->getEntity() : (is_scalar($def) ? ".$def" : ''); $name = (count($builder->getDefinitions()) + 1) . preg_replace('#\W+#', '_', $postfix); } 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->getClass() === 'self' || ($definition->getFactory() && $definition->getFactory()->getEntity() === 'self')) { throw new Nette\DeprecatedException("Replace service definition '$origName: self' with '- $origName'."); } } } /** * 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 Statement && is_string($config->getEntity()) && interface_exists($config->getEntity())) { $config = array('class' => NULL, 'implement' => $config->getEntity(), 'factory' => array_shift($config->arguments)); } elseif (!is_array($config) || isset($config[0], $config[1])) { $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', 'dynamic', '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))); } $config = self::filterArguments($config); $arguments = array(); if (array_key_exists('arguments', $config)) { Validators::assertField($config, 'arguments', 'array'); $arguments = $config['arguments']; $definition->setArguments($arguments); } if (array_key_exists('class', $config) || array_key_exists('create', $config)) { $definition->setClass(NULL); $definition->setFactory(NULL); } if (array_key_exists('class', $config)) { Validators::assertField($config, 'class', 'string|Nette\DI\Statement|null'); if (!$config['class'] instanceof Statement) { $definition->setClass($config['class']); } $definition->setFactory($config['class'], $arguments); } if (array_key_exists('create', $config)) { Validators::assertField($config, 'create', 'callable|Nette\DI\Statement|null'); $definition->setFactory($config['create'], $arguments); } if (isset($config['setup'])) { if (Config\Helpers::takeParent($config['setup'])) { $definition->setSetup(array()); } Validators::assertField($config, 'setup', 'list'); foreach ($config['setup'] as $id => $setup) { Validators::assert($setup, 'callable|Nette\DI\Statement', "setup item #$id"); $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['dynamic'])) { Validators::assertField($config, 'dynamic', 'bool'); $definition->setDynamic($config['dynamic']); } if (isset($config['inject'])) { Validators::assertField($config, 'inject', 'bool'); $definition->addTag(Extensions\InjectExtension::TAG_INJECT, $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->setTags(array()); } foreach ($config['tags'] as $tag => $attrs) { if (is_int($tag) && is_string($attrs)) { $definition->addTag($attrs); } else { $definition->addTag($tag, $attrs); } } } } /** * Removes ... and process constants recursively. * @return array */ public static function filterArguments(array $args) { foreach ($args as $k => $v) { if ($v === '...') { unset($args[$k]); } elseif (is_string($v) && preg_match('#^[\w\\\\]*::[A-Z][A-Z0-9_]*\z#', $v, $m)) { $args[$k] = ContainerBuilder::literal(ltrim($v, ':')); } elseif (is_array($v)) { $args[$k] = self::filterArguments($v); } elseif ($v instanceof Statement) { $tmp = self::filterArguments(array($v->getEntity())); $args[$k] = new Statement($tmp[0], self::filterArguments($v->arguments)); } } return $args; } } src/DI/PhpReflection.php 0000666 00000010361 13436754664 0011131 0 ustar 00 getDocComment()) { throw new Nette\InvalidStateException('You have to enable phpDoc comments in opcode cache.'); } $ok = TRUE; } if (preg_match("#[\\s*]@$name(?:\\s++([^@]\\S*)?|$)#", trim($ref->getDocComment(), '/*'), $m)) { return isset($m[1]) ? $m[1] : ''; } } /** * Returns declaring class or trait. * @return \ReflectionClass */ public static function getDeclaringClass(\ReflectionProperty $prop) { if (PHP_VERSION_ID >= 50400) { foreach ($prop->getDeclaringClass()->getTraits() as $trait) { if ($trait->hasProperty($prop->getName())) { return self::getDeclaringClass($trait->getProperty($prop->getName())); } } } return $prop->getDeclaringClass(); } /** * @return string */ public static function getPropertyType(\ReflectionParameter $prop) { try { return ($ref = $prop->getClass()) ? $ref->getName() : NULL; } catch (\ReflectionException $e) { if (preg_match('#Class (.+) does not exist#', $e->getMessage(), $m)) { return $m[1]; } throw $e; } } /** * Expands class name into full name. * @param string * @return string full name * @throws Nette\InvalidArgumentException */ public static function expandClassName($name, \ReflectionClass $rc) { if (empty($name)) { throw new Nette\InvalidArgumentException('Class name must not be empty.'); } elseif ($name === 'self') { return $rc->getName(); } elseif ($name[0] === '\\') { // fully qualified name return ltrim($name, '\\'); } $uses = & self::$cache[$rc->getName()]; if ($uses === NULL) { self::$cache = self::parseUseStatemenets(file_get_contents($rc->getFileName()), $rc->getName()) + self::$cache; $uses = & self::$cache[$rc->getName()]; } $parts = explode('\\', $name, 2); if (isset($uses[$parts[0]])) { $parts[0] = $uses[$parts[0]]; return implode('\\', $parts); } elseif ($rc->inNamespace()) { return $rc->getNamespaceName() . '\\' . $name; } else { return $name; } } /** * Parses PHP code. * @param string * @return array */ public static function parseUseStatemenets($code, $forClass = NULL) { $tokens = token_get_all($code); $namespace = $class = $classLevel = $level = NULL; $res = $uses = array(); while (list(, $token) = each($tokens)) { switch (is_array($token) ? $token[0] : $token) { case T_NAMESPACE: $namespace = self::fetch($tokens, array(T_STRING, T_NS_SEPARATOR)) . '\\'; $uses = array(); break; case T_CLASS: case T_INTERFACE: case PHP_VERSION_ID < 50400 ? -1 : T_TRAIT: if ($name = self::fetch($tokens, T_STRING)) { $class = $namespace . $name; $classLevel = $level + 1; $res[$class] = $uses; if ($class === $forClass) { return $res; } } break; case T_USE: while (!$class && ($name = self::fetch($tokens, array(T_STRING, T_NS_SEPARATOR)))) { if (self::fetch($tokens, T_AS)) { $uses[self::fetch($tokens, T_STRING)] = ltrim($name, '\\'); } else { $tmp = explode('\\', $name); $uses[end($tmp)] = $name; } if (!self::fetch($tokens, ',')) { break; } } break; case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: case '{': $level++; break; case '}': if ($level === $classLevel) { $class = $classLevel = NULL; } $level--; } } return $res; } private static function fetch(& $tokens, $take) { $res = NULL; while ($token = current($tokens)) { list($token, $s) = is_array($token) ? $token : array($token, $token); if (in_array($token, (array) $take, TRUE)) { $res .= $s; } elseif (!in_array($token, array(T_DOC_COMMENT, T_WHITESPACE, T_COMMENT), TRUE)) { break; } next($tokens); } return $res; } } src/DI/ContainerLoader.php 0000666 00000005556 13436754664 0011452 0 ustar 00 tempDirectory = $tempDirectory; $this->autoRebuild = $autoRebuild; } /** * @param mixed * @param callable function (Nette\DI\Compiler $compiler): string|NULL * @return string */ public function load($key, $generator) { $class = $this->getClassName($key); if (!class_exists($class, FALSE)) { $this->loadFile($class, $generator); } return $class; } /** * @return string */ public function getClassName($key) { return 'Container_' . substr(md5(serialize($key)), 0, 10); } /** * @return void */ private function loadFile($class, $generator) { $file = "$this->tempDirectory/$class.php"; if (!$this->isExpired($file) && (@include $file) !== FALSE) { return; } if (!is_dir($this->tempDirectory)) { @mkdir($this->tempDirectory); // @ - directory may already exist } $handle = fopen("$file.lock", 'c+'); if (!$handle || !flock($handle, LOCK_EX)) { throw new Nette\IOException("Unable to acquire exclusive lock on '$file.lock'."); } if (!is_file($file) || $this->isExpired($file)) { list($toWrite[$file], $toWrite["$file.meta"]) = $this->generate($class, $generator); foreach ($toWrite as $name => $content) { if (file_put_contents("$name.tmp", $content) !== strlen($content) || !rename("$name.tmp", $name)) { @unlink("$name.tmp"); // @ - file may not exist throw new Nette\IOException("Unable to create file '$name'."); } } } if ((@include $file) === FALSE) { // @ - error escalated to exception throw new Nette\IOException("Unable to include '$file'."); } flock($handle, LOCK_UN); } private function isExpired($file) { if ($this->autoRebuild) { $meta = @unserialize(file_get_contents("$file.meta")); // @ - files may not exist $files = $meta ? array_combine($tmp = array_keys($meta), $tmp) : array(); return $meta !== @array_map('filemtime', $files); // @ - files may not exist } return FALSE; } /** * @return array of (code, file[]) */ protected function generate($class, $generator) { $compiler = new Compiler; $compiler->getContainerBuilder()->setClassName($class); $code = call_user_func_array($generator, array(& $compiler)); $code = $code ?: implode("\n\n\n", $compiler->compile()); $files = $compiler->getDependencies(); $files = $files ? array_combine($files, $files) : array(); // workaround for PHP 5.3 array_combine return array( "setEntity($entity); $this->arguments = $arguments; } /** * @param string|array|ServiceDefinition|NULL * @return self */ public function setEntity($entity) { if (!is_string($entity) && !(is_array($entity) && isset($entity[0], $entity[1])) && !$entity instanceof ServiceDefinition && $entity !== NULL ) { throw new Nette\InvalidArgumentException('Argument is not valid Statement entity.'); } $this->entity = $entity; return $this; } public function getEntity() { return $this->entity; } } src/DI/ContainerBuilder.php 0000666 00000065406 13436754664 0011632 0 ustar 00 service */ private $aliases = array(); /** @var array for auto-wiring */ private $classes; /** @var string[] of classes excluded from auto-wiring */ private $excludedClasses = array(); /** @var array of file names */ private $dependencies = array(); /** @var Nette\PhpGenerator\ClassType[] */ private $generatedClasses = array(); /** @var string */ /*private in 5.4*/public $currentService; /** * Adds new service definition. * @param string * @return ServiceDefinition */ public function addDefinition($name, ServiceDefinition $definition = NULL) { if (!is_string($name) || !$name) { // builder is not ready for falsy names such as '0' throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($name))); } $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name; if (isset($this->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) { $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name; unset($this->definitions[$name]); if ($this->classes) { foreach ($this->classes as & $tmp) { foreach ($tmp as & $names) { $names = array_values(array_diff($names, array($name))); } } } } /** * Gets the service definition. * @param string * @return ServiceDefinition */ public function getDefinition($name) { $service = isset($this->aliases[$name]) ? $this->aliases[$name] : $name; if (!isset($this->definitions[$service])) { throw new MissingServiceException("Service '$name' not found."); } return $this->definitions[$service]; } /** * Gets all service definitions. * @return ServiceDefinition[] */ public function getDefinitions() { return $this->definitions; } /** * Does the service definition or alias exist? * @param string * @return bool */ public function hasDefinition($name) { $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name; return isset($this->definitions[$name]); } /** * @param string * @param string */ public function addAlias($alias, $service) { if (!is_string($alias) || !$alias) { // builder is not ready for falsy names such as '0' throw new Nette\InvalidArgumentException(sprintf('Alias name must be a non-empty string, %s given.', gettype($alias))); } elseif (!is_string($service) || !$service) { // builder is not ready for falsy names such as '0' throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($service))); } elseif (isset($this->aliases[$alias])) { throw new Nette\InvalidStateException("Alias '$alias' has already been added."); } elseif (isset($this->definitions[$alias])) { throw new Nette\InvalidStateException("Service '$alias' has already been added."); } $this->aliases[$alias] = $service; } /** * Gets all service aliases. * @return array */ public function getAliases() { return $this->aliases; } /** * @return self */ public function setClassName($name) { $this->className = (string) $name; return $this; } /** * @return string */ public function getClassName() { return $this->className; } /********************* 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) { $class = ltrim($class, '\\'); if ($this->currentService !== NULL) { $rc = new ReflectionClass($this->definitions[$this->currentService]->getClass()); if ($class === $rc->getName() || $rc->isSubclassOf($class)) { return $this->currentService; } } if (!isset($this->classes[$class][TRUE])) { self::checkCase($class); return; } elseif (count($this->classes[$class][TRUE]) === 1) { return $this->classes[$class][TRUE][0]; } else { throw new ServiceCreationException("Multiple services of type $class found: " . implode(', ', $this->classes[$class][TRUE])); } } /** * Gets the service names and definitions of the specified type. * @param string * @return ServiceDefinition[] */ public function findByType($class) { $class = ltrim($class, '\\'); self::checkCase($class); $found = array(); foreach (array(TRUE, FALSE) as $mode) { if (!empty($this->classes[$class][$mode])) { foreach ($this->classes[$class][$mode] as $name) { $found[$name] = $this->definitions[$name]; } } } return $found; } /** * 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 (($tmp = $def->getTag($tag)) !== NULL) { $found[$name] = $tmp; } } return $found; } /** * Creates a list of arguments using autowiring. * @return array */ public function autowireArguments($class, $method, array $arguments) { $rc = new ReflectionClass($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("$class::$method() is not callable."); } $this->addDependency($rm->getFileName()); return Helpers::autowireArguments($rm, $arguments, $this); } /** * Generates $dependencies, $classes and normalizes class names. * @return array * @internal */ public function prepareClassList() { unset($this->definitions[self::THIS_CONTAINER]); $this->addDefinition(self::THIS_CONTAINER)->setClass('Nette\DI\Container'); $this->classes = FALSE; foreach ($this->definitions as $name => $def) { // prepare generated factories if ($def->getImplement()) { $this->resolveImplement($def, $name); } if ($def->isDynamic()) { if (!$def->getClass()) { throw new ServiceCreationException("Class is missing in definition of service '$name'."); } $def->setFactory(NULL); continue; } // complete class-factory pairs if (!$def->getEntity()) { if (!$def->getClass()) { throw new ServiceCreationException("Class and factory are missing in definition of service '$name'."); } $def->setFactory($def->getClass(), ($factory = $def->getFactory()) ? $factory->arguments : array()); } // auto-disable autowiring for aliases if (($alias = $this->getServiceName($def->getFactory()->getEntity())) && (!$def->getImplement() || (!Strings::contains($alias, '\\') && $this->definitions[$alias]->getImplement())) ) { $def->setAutowired(FALSE); } } // resolve and check classes foreach ($this->definitions as $name => $def) { $this->resolveServiceClass($name); } // build auto-wiring list $excludedClasses = array(); foreach ($this->excludedClasses as $class) { self::checkCase($class); $excludedClasses += class_parents($class) + class_implements($class) + array($class => $class); } $this->classes = array(); foreach ($this->definitions as $name => $def) { if ($class = $def->getImplement() ?: $def->getClass()) { foreach (class_parents($class) + class_implements($class) + array($class) as $parent) { $this->classes[$parent][$def->isAutowired() && empty($excludedClasses[$parent])][] = (string) $name; } } } foreach ($this->classes as $class => $foo) { $rc = new ReflectionClass($class); $this->addDependency($rc->getFileName()); } } private function resolveImplement(ServiceDefinition $def, $name) { $interface = $def->getImplement(); if (!interface_exists($interface)) { throw new ServiceCreationException("Interface $interface used in service '$name' not found."); } self::checkCase($interface); $rc = new ReflectionClass($interface); $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 $interface used in service '$name' must have just one non-static method create() or get()."); } $def->setImplementType($methodName = $rc->hasMethod('create') ? 'create' : 'get'); if (!$def->getClass() && !$def->getEntity()) { $returnType = PhpReflection::parseAnnotation($method, 'return'); if (!$returnType) { throw new ServiceCreationException("Method $interface::$methodName() used in service '$name' has no @return annotation."); } $returnType = PhpReflection::expandClassName(preg_replace('#[|\s].*#', '', $returnType), $rc); if (!class_exists($returnType)) { throw new ServiceCreationException("Please check a @return annotation of the $interface::$methodName() method used in service '$name'. Class '$returnType' cannot be found."); } $def->setClass($returnType); } if ($methodName === 'get') { if ($method->getParameters()) { throw new ServiceCreationException("Method $interface::get() used in service '$name' must have no arguments."); } if (!$def->getEntity()) { $def->setFactory('@\\' . ltrim($def->getClass(), '\\')); } elseif (!$this->getServiceName($def->getFactory()->getEntity())) { throw new ServiceCreationException("Invalid factory in service '$name' definition."); } } if (!$def->parameters) { $ctorParams = array(); if (!$def->getEntity()) { $def->setFactory($def->getClass(), $def->getFactory() ? $def->getFactory()->arguments : array()); } if (($class = $this->resolveEntityClass($def->getFactory(), array($name => 1))) && ($rc = new ReflectionClass($class)) && ($ctor = $rc->getConstructor()) ) { foreach ($ctor->getParameters() as $param) { $ctorParams[$param->getName()] = $param; } } foreach ($method->getParameters() as $param) { $hint = $param->isArray() ? 'array' : PhpReflection::getPropertyType($param); if (isset($ctorParams[$param->getName()])) { $arg = $ctorParams[$param->getName()]; if ($hint !== ($arg->isArray() ? 'array' : PhpReflection::getPropertyType($arg))) { throw new ServiceCreationException("Type hint for \${$param->getName()} in $interface::$methodName() doesn't match type hint in $class constructor."); } $def->getFactory()->arguments[$arg->getPosition()] = self::literal('$' . $arg->getName()); } $paramDef = $hint . ' ' . $param->getName(); if ($param->isOptional()) { $def->parameters[$paramDef] = $param->getDefaultValue(); } else { $def->parameters[] = $paramDef; } } } } /** @return string|NULL */ private function resolveServiceClass($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]; $class = $def->getFactory() ? $this->resolveEntityClass($def->getFactory()->getEntity(), $recursive) : NULL; // call always to check entities if ($class = $def->getClass() ?: $class) { $def->setClass($class); if (!class_exists($class) && !interface_exists($class)) { throw new ServiceCreationException("Class or interface $class used in service '$name' not found."); } self::checkCase($class); } elseif ($def->isAutowired()) { trigger_error("Type of service '$name' is unknown.", E_USER_NOTICE); } return $class; } /** @return string|NULL */ private function resolveEntityClass($entity, $recursive = array()) { $entity = $this->normalizeEntity($entity instanceof Statement ? $entity->getEntity() : $entity); if (is_array($entity)) { if (($service = $this->getServiceName($entity[0])) || $entity[0] instanceof Statement) { $entity[0] = $this->resolveEntityClass($entity[0], $recursive); if (!$entity[0]) { return; } elseif (isset($this->definitions[$service]) && $this->definitions[$service]->getImplement()) { // @Implement::create return $entity[1] === 'create' ? $this->resolveServiceClass($service, $recursive) : NULL; } } try { $reflection = Nette\Utils\Callback::toReflection($entity[0] === '' ? $entity[1] : $entity); $refClass = $reflection instanceof \ReflectionMethod ? $reflection->getDeclaringClass() : NULL; } catch (\ReflectionException $e) { } if (isset($e) || ($refClass && (!$reflection->isPublic() || (PHP_VERSION_ID >= 50400 && $refClass->isTrait() && !$reflection->isStatic()) ))) { $name = array_slice(array_keys($recursive), -1); throw new ServiceCreationException(sprintf("Factory '%s' used in service '%s' is not callable.", Nette\Utils\Callback::toString($entity), $name[0])); } $class = preg_replace('#[|\s].*#', '', PhpReflection::parseAnnotation($reflection, 'return')); if ($class) { $class = $refClass ? PhpReflection::expandClassName($class, $refClass) : ltrim($class, '\\'); } return $class; } elseif ($service = $this->getServiceName($entity)) { // alias or factory if (Strings::contains($service, '\\')) { // @\Class return ltrim($service, '\\'); } return $this->definitions[$service]->getImplement() ?: $this->resolveServiceClass($service, $recursive); } elseif (is_string($entity)) { if (!class_exists($entity) || !($rc = new ReflectionClass($entity)) || !$rc->isInstantiable()) { $name = array_slice(array_keys($recursive), -1); throw new ServiceCreationException("Class $entity used in service '$name[0]' not found or is not instantiable."); } return ltrim($entity, '\\'); } } private function checkCase($class) { if (class_exists($class) && ($rc = new ReflectionClass($class)) && $class !== $rc->getName()) { throw new ServiceCreationException("Case mismatch on class name '$class', correct name is '{$rc->getName()}'."); } } /** * @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 * @internal */ 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 = NULL, $parentName = NULL) { $this->prepareClassList(); $this->generatedClasses = array(); $this->className = $className ?: $this->className; $containerClass = $this->generatedClasses[] = new Nette\PhpGenerator\ClassType($this->className); $containerClass->setExtends($parentName ?: 'Nette\DI\Container'); $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) { $meta->value[Container::SERVICES][$name] = $def->getClass() ?: NULL; foreach ($def->getTags() 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->getImplement() ?: $def->getClass())) ->setBody($name === self::THIS_CONTAINER ? 'return $this;' : $this->generateService($name)) ->setParameters($def->getImplement() ? array() : $this->convertParameters($def->parameters)); } catch (\Exception $e) { throw new ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e); } } $aliases = $this->aliases; ksort($aliases); $meta->value[Container::ALIASES] = $aliases; return $this->generatedClasses; } /** * Generates body of service method. * @return string */ private function generateService($name) { $this->currentService = NULL; $def = $this->definitions[$name]; if ($def->isDynamic()) { return PhpHelpers::formatArgs('throw new Nette\\DI\\ServiceCreationException(?);', array("Unable to create dynamic service '$name', it must be added using addService()") ); } $entity = $def->getFactory()->getEntity(); $serviceRef = $this->getServiceName($entity); $factory = $serviceRef && !$def->getFactory()->arguments && !$def->getSetup() && $def->getImplementType() !== 'create' ? new Statement(array('@' . self::THIS_CONTAINER, 'getService'), array($serviceRef)) : $def->getFactory(); $code = '$service = ' . $this->formatStatement($factory) . ";\n"; $this->currentService = $name; if (($class = $def->getClass()) && !$serviceRef && $class !== $entity && !(is_string($entity) && preg_match('#^[\w\\\\]+\z#', $entity) && is_subclass_of($entity, $class)) ) { $code .= PhpHelpers::formatArgs("if (!\$service instanceof $class) {\n" . "\tthrow new Nette\\UnexpectedValueException(?);\n}\n", array("Unable to create service '$name', value returned by factory is not $class type.") ); } foreach ($def->getSetup() as $setup) { if (is_string($setup->getEntity()) && strpbrk($setup->getEntity(), ':@?\\') === FALSE) { // auto-prepend @self $setup->setEntity(array('@self', $setup->getEntity())); } $code .= $this->formatStatement($setup) . ";\n"; } $this->currentService = NULL; $code .= 'return $service;'; if (!$def->getImplement()) { return $code; } $factoryClass = $this->generatedClasses[] = new Nette\PhpGenerator\ClassType; $factoryClass->setName(str_replace(array('\\', '.'), '_', "{$this->className}_{$def->getImplement()}Impl_{$name}")) ->addImplement($def->getImplement()) ->setFinal(TRUE); $factoryClass->addProperty('container') ->setVisibility('private'); $factoryClass->addMethod('__construct') ->addBody('$this->container = $container;') ->addParameter('container') ->setTypeHint($this->className); $factoryClass->addMethod($def->getImplementType()) ->setParameters($this->convertParameters($def->parameters)) ->setBody(str_replace('$this', '$this->container', $code)); return "return new {$factoryClass->getName()}(\$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->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 = 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 \ReflectionFunction(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 $rc = new ReflectionClass($entity); if ($constructor = $rc->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 (!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](?*)", array($arguments)); } elseif ($entity[0] instanceof Statement) { $inner = $this->formatPhp('?', array($entity[0])); if (substr($inner, 0, 4) === 'new ') { $inner = PHP_VERSION_ID < 50400 ? "current(array($inner))" : "($inner)"; } return $this->formatPhp("$inner->?(?*)", array($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('?->? = ?', 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]->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('?->?(?*)', 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 * @internal */ 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->getDefinitions(), $val, TRUE)); } if (!is_string($val)) { return; } elseif (substr($val, 0, 2) === '@@') { $val = substr($val, 1); } elseif (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->getDefinition($name)->getClass() . '::' . $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 * @deprecated */ 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] = '@' . self::THIS_CONTAINER; } return $entity; // Class, @service, [Class, member], [@service, member], [, globalFunc], Statement } /** * Converts @service or @\Class -> service name and checks its existence. * @return string of FALSE, if argument is not service name * @internal */ public function getServiceName($arg) { $arg = $this->normalizeEntity($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; } $service = isset($this->aliases[$service]) ? $this->aliases[$service] : $service; if (!isset($this->definitions[$service])) { throw new ServiceCreationException("Reference to missing service '$service'."); } return $service; } } src/DI/ServiceDefinition.php 0000666 00000011257 13436754664 0012005 0 ustar 00 class = ltrim($class, '\\'); if ($args) { $this->setFactory($class, $args); } return $this; } /** * @return string */ public function getClass() { return $this->class; } /** * @return self */ public function setFactory($factory, array $args = array()) { $this->factory = $factory instanceof Statement ? $factory : new Statement($factory, $args); return $this; } /** * @return Statement|NULL */ public function getFactory() { return $this->factory; } public function getEntity() { return $this->factory ? $this->factory->getEntity() : NULL; } /** * @return self */ public function setArguments(array $args = array()) { if (!$this->factory) { $this->factory = new Statement($this->class); } $this->factory->arguments = $args; return $this; } /** * @param Statement[] * @return self */ public function setSetup(array $setup) { foreach ($setup as $v) { if (!$v instanceof Statement) { throw new Nette\InvalidArgumentException('Argument must be Nette\DI\Statement[].'); } } $this->setup = $setup; return $this; } /** * @return Statement[] */ public function getSetup() { return $this->setup; } /** * @return self */ public function addSetup($entity, array $args = array()) { $this->setup[] = $entity instanceof Statement ? $entity : new Statement($entity, $args); return $this; } /** * @return self */ public function setParameters(array $params) { $this->parameters = $params; return $this; } /** * @return array */ public function getParameters() { return $this->parameters; } /** * @return self */ public function setTags(array $tags) { $this->tags = $tags; return $this; } /** * @return array */ public function getTags() { return $this->tags; } /** * @return self */ public function addTag($tag, $attr = TRUE) { $this->tags[$tag] = $attr; return $this; } /** * @return mixed */ public function getTag($tag) { return isset($this->tags[$tag]) ? $this->tags[$tag] : NULL; } /** * @param bool * @return self */ public function setAutowired($state = TRUE) { $this->autowired = (bool) $state; return $this; } /** * @return bool */ public function isAutowired() { return $this->autowired; } /** * @param bool * @return self */ public function setDynamic($state = TRUE) { $this->dynamic = (bool) $state; return $this; } /** * @return bool */ public function isDynamic() { return $this->dynamic; } /** * @param string * @return self */ public function setImplement($interface) { $this->implement = ltrim($interface, '\\'); return $this; } /** * @return string */ public function getImplement() { return $this->implement; } /** * @param string * @return self */ public function setImplementType($type) { if (!in_array($type, array('get', 'create'), TRUE)) { throw new Nette\InvalidArgumentException('Argument must be get|create.'); } $this->implementType = $type; return $this; } /** * @return string */ public function getImplementType() { return $this->implementType; } /** @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); } /** @return self */ public function setInject($state = TRUE) { //trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED); return $this->addTag(Extensions\InjectExtension::TAG_INJECT, $state); } /** @return self */ public function getInject() { //trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED); return $this->getTag(Extensions\InjectExtension::TAG_INJECT); } } src/DI/ContainerFactory.php 0000666 00000007650 13436754664 0011650 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))); $file = "$this->tempDirectory/$key.php"; if (!$this->isExpired($file) && (@include $file) !== FALSE) { return; } $handle = fopen("$file.lock", 'c+'); if (!$handle || !flock($handle, LOCK_EX)) { throw new Nette\IOException("Unable to acquire exclusive lock on '$file.lock'."); } if (!is_file($file) || $this->isExpired($file)) { $this->dependencies = array(); $toWrite[$file] = $this->generateCode(); $files = $this->dependencies ? array_combine($this->dependencies, $this->dependencies) : array(); $toWrite["$file.meta"] = serialize(@array_map('filemtime', $files)); // @ - file may not exist foreach ($toWrite as $name => $content) { if (file_put_contents("$name.tmp", $content) !== strlen($content) || !rename("$name.tmp", $name)) { @unlink("$name.tmp"); // @ - file may not exist throw new Nette\IOException("Unable to create file '$name'."); } } } if ((@include $file) === FALSE) { // @ - error escalated to exception throw new Nette\IOException("Unable to include '$file'."); } flock($handle, LOCK_UN); } private function isExpired($file) { if ($this->autoRebuild) { $meta = @unserialize(file_get_contents("$file.meta")); // @ - files may not exist $files = $meta ? array_combine($tmp = array_keys($meta), $tmp) : array(); return $meta !== @array_map('filemtime', $files); // @ - files may not exist } return FALSE; } /** * @return Compiler */ protected function createCompiler() { return new Compiler; } /** * @return Config\Loader */ protected function createLoader() { return new Config\Loader; } } src/DI/Container.php 0000666 00000020246 13436754664 0010314 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 (!is_string($name) || !$name) { throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($name))); } $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name; if (isset($this->registry[$name])) { throw new Nette\InvalidStateException("Service '$name' already exists."); } elseif (!is_object($service)) { throw new Nette\InvalidArgumentException(sprintf("Service '%s' must be a object, %s given.", $name, gettype($service))); } elseif (isset($this->meta[self::SERVICES][$name]) && !$service instanceof $this->meta[self::SERVICES][$name]) { throw new Nette\InvalidArgumentException(sprintf("Service '%s' must be instance of %s, %s given.", $name, $this->meta[self::SERVICES][$name], get_class($service))); } $this->registry[$name] = $service; return $this; } /** * Removes the service from the container. * @param string * @return void */ public function removeService($name) { $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $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])) { if (isset($this->meta[self::ALIASES][$name])) { return $this->getService($this->meta[self::ALIASES][$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) { $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name; return isset($this->registry[$name]) || (method_exists($this, $method = self::getMethodName($name)) && ($rm = new \ReflectionMethod($this, $method)) && $rm->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."); } $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name; 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()) { $name = isset($this->meta[self::ALIASES][$name]) ? $this->meta[self::ALIASES][$name] : $name; $method = self::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) || !($rm = new \ReflectionMethod($this, $method)) || $rm->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) { $class = ltrim($class, '\\'); $names = & $this->meta[self::TYPES][$class][TRUE]; if (count($names) === 1) { return $this->getService($names[0]); } elseif (count($names) > 1) { throw new MissingServiceException("Multiple services of type $class found: " . implode(', ', $names) . '.'); } elseif ($need) { throw new MissingServiceException("Service of type $class not found."); } } /** * Gets the service names of the specified type. * @param string * @return string[] */ public function findByType($class) { $class = ltrim($class, '\\'); $meta = & $this->meta[self::TYPES]; return array_merge( isset($meta[$class][TRUE]) ? $meta[$class][TRUE] : array(), isset($meta[$class][FALSE]) ? $meta[$class][FALSE] : 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 = new \ReflectionClass($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) { Extensions\InjectExtension::callInjects($this, $service); } /** * 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 * @deprecated */ 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 di.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 00000003371 13436754664 0013656 0 ustar 00 container = $container; $this->elapsedTime = self::$compilationTime ? microtime(TRUE) - self::$compilationTime : NULL; } /** * Renders tab. * @return string */ public function getTab() { ob_start(); $elapsedTime = $this->elapsedTime; require __DIR__ . '/templates/ContainerPanel.tab.phtml'; return ob_get_clean(); } /** * Renders panel. * @return string */ public function getPanel() { $container = $this->container; $registry = $this->getContainerProperty('registry'); $rc = new \ReflectionClass($container); $file = $rc->getFileName(); $tags = array(); $meta = $this->getContainerProperty('meta'); $services = $meta[Container::SERVICES]; ksort($services); 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) { $rc = new \ReflectionClass('Nette\DI\Container'); $prop = $rc->getProperty($name); $prop->setAccessible(TRUE); return $prop->getValue($this->container); } } src/Bridges/DITracy/templates/ContainerPanel.panel.phtml 0000666 00000003612 13436754664 0017305 0 ustar 00
Name | Autowired | Service | Tags |
---|---|---|---|
TRUE, Dumper::LIVE => TRUE)); ?>
|
TRUE)); } ?> |
Source: