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
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • Compiler
  • CompilerExtension
  • Container
  • ContainerBuilder
  • ContainerFactory
  • ContainerLoader
  • ServiceDefinition
  • Statement

Exceptions

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