Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Compiler
  • CompilerExtension
  • Container
  • ContainerBuilder
  • ContainerLoader
  • DependencyChecker
  • Helpers
  • PhpGenerator
  • PhpReflection
  • ServiceDefinition
  • Statement

Exceptions

  • MissingServiceException
  • ServiceCreationException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\DI;
  9: 
 10: use Nette;
 11: use Nette\Utils\Validators;
 12: 
 13: 
 14: /**
 15:  * DI container compiler.
 16:  */
 17: class Compiler
 18: {
 19:     use Nette\SmartObject;
 20: 
 21:     /** @var CompilerExtension[] */
 22:     private $extensions = [];
 23: 
 24:     /** @var ContainerBuilder */
 25:     private $builder;
 26: 
 27:     /** @var array */
 28:     private $config = [];
 29: 
 30:     /** @var DependencyChecker */
 31:     private $dependencies;
 32: 
 33:     /** @var string */
 34:     private $className = 'Container';
 35: 
 36:     /** @var string[] */
 37:     private $dynamicParams = [];
 38: 
 39:     /** @var array reserved section names */
 40:     private static $reserved = ['services' => 1, 'parameters' => 1];
 41: 
 42: 
 43:     public function __construct(ContainerBuilder $builder = null)
 44:     {
 45:         $this->builder = $builder ?: new ContainerBuilder;
 46:         $this->dependencies = new DependencyChecker;
 47:     }
 48: 
 49: 
 50:     /**
 51:      * Add custom configurator extension.
 52:      * @param  string|null
 53:      * @return static
 54:      */
 55:     public function addExtension($name, CompilerExtension $extension)
 56:     {
 57:         if ($name === null) {
 58:             $name = '_' . count($this->extensions);
 59:         } elseif (isset($this->extensions[$name]) || isset(self::$reserved[$name])) {
 60:             throw new Nette\InvalidArgumentException("Name '$name' is already used or reserved.");
 61:         }
 62:         $this->extensions[$name] = $extension->setCompiler($this, $name);
 63:         return $this;
 64:     }
 65: 
 66: 
 67:     /**
 68:      * @return array
 69:      */
 70:     public function getExtensions($type = null)
 71:     {
 72:         return $type
 73:             ? array_filter($this->extensions, function ($item) use ($type) { return $item instanceof $type; })
 74:             : $this->extensions;
 75:     }
 76: 
 77: 
 78:     /**
 79:      * @return ContainerBuilder
 80:      */
 81:     public function getContainerBuilder()
 82:     {
 83:         return $this->builder;
 84:     }
 85: 
 86: 
 87:     /**
 88:      * @return static
 89:      */
 90:     public function setClassName($className)
 91:     {
 92:         $this->className = $className;
 93:         return $this;
 94:     }
 95: 
 96: 
 97:     /**
 98:      * Adds new configuration.
 99:      * @return static
100:      */
101:     public function addConfig(array $config)
102:     {
103:         $this->config = Config\Helpers::merge($config, $this->config);
104:         return $this;
105:     }
106: 
107: 
108:     /**
109:      * Adds new configuration from file.
110:      * @return static
111:      */
112:     public function loadConfig($file)
113:     {
114:         $loader = new Config\Loader;
115:         $this->addConfig($loader->load($file));
116:         $this->dependencies->add($loader->getDependencies());
117:         return $this;
118:     }
119: 
120: 
121:     /**
122:      * Returns configuration.
123:      * @return array
124:      */
125:     public function getConfig()
126:     {
127:         return $this->config;
128:     }
129: 
130: 
131:     /**
132:      * Sets the names of dynamic parameters.
133:      * @return static
134:      */
135:     public function setDynamicParameterNames(array $names)
136:     {
137:         $this->dynamicParams = $names;
138:         return $this;
139:     }
140: 
141: 
142:     /**
143:      * Adds dependencies to the list.
144:      * @param  array of ReflectionClass|\ReflectionFunctionAbstract|string
145:      * @return static
146:      */
147:     public function addDependencies(array $deps)
148:     {
149:         $this->dependencies->add(array_filter($deps));
150:         return $this;
151:     }
152: 
153: 
154:     /**
155:      * Exports dependencies.
156:      * @return array
157:      */
158:     public function exportDependencies()
159:     {
160:         return $this->dependencies->export();
161:     }
162: 
163: 
164:     /**
165:      * @return string
166:      */
167:     public function compile()
168:     {
169:         if (func_num_args()) {
170:             trigger_error(__METHOD__ . ' arguments are deprecated, use Compiler::addConfig() and Compiler::setClassName().', E_USER_DEPRECATED);
171:             $this->config = func_get_arg(0) ?: $this->config;
172:             $this->className = @func_get_arg(1) ?: $this->className;
173:         }
174:         $this->processParameters();
175:         $this->processExtensions();
176:         $this->processServices();
177:         $classes = $this->generateCode();
178:         return implode("\n\n\n", $classes);
179:     }
180: 
181: 
182:     /** @internal */
183:     public function processParameters()
184:     {
185:         $params = isset($this->config['parameters']) ? $this->config['parameters'] : [];
186:         foreach ($this->dynamicParams as $key) {
187:             $params[$key] = array_key_exists($key, $params)
188:                 ? ContainerBuilder::literal('isset($this->parameters[?]) \? $this->parameters[?] : ?', [$key, $key, $params[$key]])
189:                 : ContainerBuilder::literal('$this->parameters[?]', [$key]);
190:         }
191:         $this->builder->parameters = Helpers::expand($params, $params, true);
192:     }
193: 
194: 
195:     /** @internal */
196:     public function processExtensions()
197:     {
198:         $this->config = Helpers::expand(array_diff_key($this->config, self::$reserved), $this->builder->parameters)
199:             + array_intersect_key($this->config, self::$reserved);
200: 
201:         foreach ($first = $this->getExtensions(Extensions\ExtensionsExtension::class) as $name => $extension) {
202:             $extension->setConfig(isset($this->config[$name]) ? $this->config[$name] : []);
203:             $extension->loadConfiguration();
204:         }
205: 
206:         $last = $this->getExtensions(Extensions\InjectExtension::class);
207:         $this->extensions = array_merge(array_diff_key($this->extensions, $last), $last);
208: 
209:         $extensions = array_diff_key($this->extensions, $first);
210:         foreach (array_intersect_key($extensions, $this->config) as $name => $extension) {
211:             $extension->setConfig($this->config[$name] ?: []);
212:         }
213: 
214:         foreach ($extensions as $extension) {
215:             $extension->loadConfiguration();
216:         }
217: 
218:         if ($extra = array_diff_key($this->extensions, $extensions, $first)) {
219:             $extra = implode("', '", array_keys($extra));
220:             throw new Nette\DeprecatedException("Extensions '$extra' were added while container was being compiled.");
221: 
222:         } elseif ($extra = key(array_diff_key($this->config, self::$reserved, $this->extensions))) {
223:             $hint = Nette\Utils\ObjectMixin::getSuggestion(array_keys(self::$reserved + $this->extensions), $extra);
224:             throw new Nette\InvalidStateException(
225:                 "Found section '$extra' in configuration, but corresponding extension is missing"
226:                 . ($hint ? ", did you mean '$hint'?" : '.')
227:             );
228:         }
229:     }
230: 
231: 
232:     /** @internal */
233:     public function processServices()
234:     {
235:         if (isset($this->config['services'])) {
236:             self::loadDefinitions($this->builder, $this->config['services']);
237:         }
238:     }
239: 
240: 
241:     /** @internal */
242:     public function generateCode()
243:     {
244:         if (func_num_args()) {
245:             trigger_error(__METHOD__ . ' arguments are deprecated, use Compiler::setClassName().', E_USER_DEPRECATED);
246:             $this->className = func_get_arg(0) ?: $this->className;
247:         }
248: 
249:         $this->builder->prepareClassList();
250: 
251:         foreach ($this->extensions as $extension) {
252:             $extension->beforeCompile();
253:             $this->dependencies->add([(new \ReflectionClass($extension))->getFileName()]);
254:         }
255: 
256:         $generator = new PhpGenerator($this->builder);
257:         $classes = $generator->generate($this->className);
258:         $classes[0]->addMethod('initialize');
259:         $this->dependencies->add($this->builder->getDependencies());
260: 
261:         foreach ($this->extensions as $extension) {
262:             $extension->afterCompile($classes[0]);
263:         }
264:         return $classes;
265:     }
266: 
267: 
268:     /********************* tools ****************d*g**/
269: 
270: 
271:     /**
272:      * Adds service definitions from configuration.
273:      * @return void
274:      */
275:     public static function loadDefinitions(ContainerBuilder $builder, array $services, $namespace = null)
276:     {
277:         $depths = [];
278:         foreach ($services as $name => $def) {
279:             $path = [];
280:             while (Config\Helpers::isInheriting($def)) {
281:                 $path[] = $def;
282:                 $def = isset($services[$def[Config\Helpers::EXTENDS_KEY]]) ? $services[$def[Config\Helpers::EXTENDS_KEY]] : [];
283:                 if (in_array($def, $path, true)) {
284:                     throw new ServiceCreationException("Circular reference detected for service '$name'.");
285:                 }
286:             }
287:             $depths[$name] = count($path) + preg_match('#^@[\w\\\\]+\z#', $name);
288:         }
289:         @array_multisort($depths, $services); // @ may trigger E_NOTICE: Object of class Nette\DI\Statement could not be converted to int
290: 
291:         foreach ($services as $name => $def) {
292:             if (is_int($name)) {
293:                 $postfix = $def instanceof Statement && is_string($def->getEntity()) ? '.' . $def->getEntity() : (is_scalar($def) ? ".$def" : '');
294:                 $name = (count($builder->getDefinitions()) + 1) . preg_replace('#\W+#', '_', $postfix);
295:             } elseif (preg_match('#^@[\w\\\\]+\z#', $name)) {
296:                 $name = $builder->getByType(substr($name, 1), true);
297:             } elseif ($namespace) {
298:                 $name = $namespace . '.' . $name;
299:             }
300: 
301:             if ($def === false) {
302:                 $builder->removeDefinition($name);
303:                 continue;
304:             }
305:             if ($namespace) {
306:                 $def = Helpers::prefixServiceName($def, $namespace);
307:             }
308: 
309:             $params = $builder->parameters;
310:             if (is_array($def) && isset($def['parameters'])) {
311:                 foreach ((array) $def['parameters'] as $k => $v) {
312:                     $v = explode(' ', is_int($k) ? $v : $k);
313:                     $params[end($v)] = $builder::literal('$' . end($v));
314:                 }
315:             }
316:             $def = Helpers::expand($def, $params);
317: 
318:             if (is_array($def) && !empty($def['alteration']) && !$builder->hasDefinition($name)) {
319:                 throw new ServiceCreationException("Service '$name': missing original definition for alteration.");
320:             }
321: 
322:             if (($parent = Config\Helpers::takeParent($def)) && $parent !== $name) {
323:                 if ($parent !== Config\Helpers::OVERWRITE) {
324:                     trigger_error("Section inheritance $name < $parent is deprecated.", E_USER_DEPRECATED);
325:                 }
326:                 $builder->removeDefinition($name);
327:                 $definition = $builder->addDefinition(
328:                     $name,
329:                     $parent === Config\Helpers::OVERWRITE ? null : clone $builder->getDefinition($parent)
330:                 );
331:             } elseif ($builder->hasDefinition($name)) {
332:                 $definition = $builder->getDefinition($name);
333:             } else {
334:                 $definition = $builder->addDefinition($name);
335:             }
336: 
337:             try {
338:                 static::loadDefinition($definition, $def);
339:             } catch (\Exception $e) {
340:                 throw new ServiceCreationException("Service '$name': " . $e->getMessage(), 0, $e);
341:             }
342:         }
343:     }
344: 
345: 
346:     /**
347:      * Parses single service definition from configuration.
348:      * @return void
349:      */
350:     public static function loadDefinition(ServiceDefinition $definition, $config)
351:     {
352:         if ($config === null) {
353:             return;
354: 
355:         } elseif (is_string($config) && interface_exists($config)) {
356:             $config = ['class' => null, 'implement' => $config];
357: 
358:         } elseif ($config instanceof Statement && is_string($config->getEntity()) && interface_exists($config->getEntity())) {
359:             $config = ['class' => null, 'implement' => $config->getEntity(), 'factory' => array_shift($config->arguments)];
360: 
361:         } elseif (!is_array($config) || isset($config[0], $config[1])) {
362:             $config = ['class' => null, 'factory' => $config];
363:         }
364: 
365:         if (array_key_exists('create', $config)) {
366:             trigger_error("Key 'create' is deprecated, use 'factory' or 'type' in configuration.", E_USER_DEPRECATED);
367:             $config['factory'] = $config['create'];
368:             unset($config['create']);
369:         }
370: 
371:         $known = ['type', 'class', 'factory', 'arguments', 'setup', 'autowired', 'dynamic', 'inject', 'parameters', 'implement', 'run', 'tags', 'alteration'];
372:         if ($error = array_diff(array_keys($config), $known)) {
373:             $hints = array_filter(array_map(function ($error) use ($known) {
374:                 return Nette\Utils\ObjectMixin::getSuggestion($known, $error);
375:             }, $error));
376:             $hint = $hints ? ", did you mean '" . implode("', '", $hints) . "'?" : '.';
377:             throw new Nette\InvalidStateException(sprintf("Unknown key '%s' in definition of service$hint", implode("', '", $error)));
378:         }
379: 
380:         $config = Helpers::filterArguments($config);
381: 
382:         if (array_key_exists('class', $config) || array_key_exists('factory', $config)) {
383:             $definition->setType(null);
384:             $definition->setFactory(null);
385:         }
386: 
387:         if (array_key_exists('type', $config)) {
388:             Validators::assertField($config, 'type', 'string|null');
389:             $definition->setType($config['type']);
390:             if (array_key_exists('class', $config)) {
391:                 throw new Nette\InvalidStateException("Unexpected 'class' when 'type' is used.");
392:             }
393:         }
394: 
395:         if (array_key_exists('class', $config)) {
396:             Validators::assertField($config, 'class', 'string|Nette\DI\Statement|null');
397:             if (!$config['class'] instanceof Statement) {
398:                 $definition->setType($config['class']);
399:             }
400:             $definition->setFactory($config['class']);
401:         }
402: 
403:         if (array_key_exists('factory', $config)) {
404:             Validators::assertField($config, 'factory', 'callable|Nette\DI\Statement|null');
405:             $definition->setFactory($config['factory']);
406:         }
407: 
408:         if (array_key_exists('arguments', $config)) {
409:             Validators::assertField($config, 'arguments', 'array');
410:             $arguments = $config['arguments'];
411:             if (!Config\Helpers::takeParent($arguments) && !Nette\Utils\Arrays::isList($arguments) && $definition->getFactory()) {
412:                 $arguments += $definition->getFactory()->arguments;
413:             }
414:             $definition->setArguments($arguments);
415:         }
416: 
417:         if (isset($config['setup'])) {
418:             if (Config\Helpers::takeParent($config['setup'])) {
419:                 $definition->setSetup([]);
420:             }
421:             Validators::assertField($config, 'setup', 'list');
422:             foreach ($config['setup'] as $id => $setup) {
423:                 Validators::assert($setup, 'callable|Nette\DI\Statement|array:1', "setup item #$id");
424:                 if (is_array($setup)) {
425:                     $setup = new Statement(key($setup), array_values($setup));
426:                 }
427:                 $definition->addSetup($setup);
428:             }
429:         }
430: 
431:         if (isset($config['parameters'])) {
432:             Validators::assertField($config, 'parameters', 'array');
433:             $definition->setParameters($config['parameters']);
434:         }
435: 
436:         if (isset($config['implement'])) {
437:             Validators::assertField($config, 'implement', 'string');
438:             $definition->setImplement($config['implement']);
439:             $definition->setAutowired(true);
440:         }
441: 
442:         if (isset($config['autowired'])) {
443:             Validators::assertField($config, 'autowired', 'bool|string|array');
444:             $definition->setAutowired($config['autowired']);
445:         }
446: 
447:         if (isset($config['dynamic'])) {
448:             Validators::assertField($config, 'dynamic', 'bool');
449:             $definition->setDynamic($config['dynamic']);
450:         }
451: 
452:         if (isset($config['inject'])) {
453:             Validators::assertField($config, 'inject', 'bool');
454:             $definition->addTag(Extensions\InjectExtension::TAG_INJECT, $config['inject']);
455:         }
456: 
457:         if (isset($config['run'])) {
458:             trigger_error("Option 'run' is deprecated, use 'run' as tag.", E_USER_DEPRECATED);
459:             $config['tags']['run'] = (bool) $config['run'];
460:         }
461: 
462:         if (isset($config['tags'])) {
463:             Validators::assertField($config, 'tags', 'array');
464:             if (Config\Helpers::takeParent($config['tags'])) {
465:                 $definition->setTags([]);
466:             }
467:             foreach ($config['tags'] as $tag => $attrs) {
468:                 if (is_int($tag) && is_string($attrs)) {
469:                     $definition->addTag($attrs);
470:                 } else {
471:                     $definition->addTag($tag, $attrs);
472:                 }
473:             }
474:         }
475:     }
476: 
477: 
478:     /** @deprecated */
479:     public static function filterArguments(array $args)
480:     {
481:         return Helpers::filterArguments($args);
482:     }
483: 
484: 
485:     /** @deprecated */
486:     public static function parseServices(ContainerBuilder $builder, array $config, $namespace = null)
487:     {
488:         self::loadDefinitions($builder, isset($config['services']) ? $config['services'] : [], $namespace);
489:     }
490: 
491: 
492:     /** @deprecated */
493:     public static function parseService(ServiceDefinition $definition, $config)
494:     {
495:         self::loadDefinition($definition, $config);
496:     }
497: }
498: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0