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
    • Reflection
    • Security
    • Utils
  • none
  • Tracy
    • Bridges
      • Nette

Classes

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

Exceptions

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