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