Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • 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

Classes

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

Exceptions

  • MissingServiceException
  • ServiceCreationException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://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:  * @author     David Grudl
 18:  *
 19:  * @property-read CompilerExtension[] $extensions
 20:  * @property-read ContainerBuilder $containerBuilder
 21:  * @property-read array $config
 22:  */
 23: class Compiler extends Nette\Object
 24: {
 25:     /** @var CompilerExtension[] */
 26:     private $extensions = array();
 27: 
 28:     /** @var ContainerBuilder */
 29:     private $builder;
 30: 
 31:     /** @var array */
 32:     private $config;
 33: 
 34:     /** @var array reserved section names */
 35:     private static $reserved = array('services' => 1, 'factories' => 1, 'parameters' => 1);
 36: 
 37: 
 38:     public function __construct(ContainerBuilder $builder = NULL)
 39:     {
 40:         $this->builder = $builder ?: new ContainerBuilder;
 41:     }
 42: 
 43: 
 44:     /**
 45:      * Add custom configurator extension.
 46:      * @return self
 47:      */
 48:     public function addExtension($name, CompilerExtension $extension)
 49:     {
 50:         if (isset(self::$reserved[$name])) {
 51:             throw new Nette\InvalidArgumentException("Name '$name' is reserved.");
 52:         }
 53:         $this->extensions[$name] = $extension->setCompiler($this, $name);
 54:         return $this;
 55:     }
 56: 
 57: 
 58:     /**
 59:      * @return array
 60:      */
 61:     public function getExtensions($type = NULL)
 62:     {
 63:         return $type
 64:             ? array_filter($this->extensions, function ($item) use ($type) { return $item instanceof $type; })
 65:             : $this->extensions;
 66:     }
 67: 
 68: 
 69:     /**
 70:      * @return ContainerBuilder
 71:      */
 72:     public function getContainerBuilder()
 73:     {
 74:         return $this->builder;
 75:     }
 76: 
 77: 
 78:     /**
 79:      * Returns configuration.
 80:      * @return array
 81:      */
 82:     public function getConfig()
 83:     {
 84:         return $this->config;
 85:     }
 86: 
 87: 
 88:     /**
 89:      * @return string
 90:      */
 91:     public function compile(array $config, $className, $parentName)
 92:     {
 93:         $this->config = $config;
 94:         $this->processParameters();
 95:         $this->processExtensions();
 96:         $this->processServices();
 97:         return $this->generateCode($className, $parentName);
 98:     }
 99: 
100: 
101:     public function processParameters()
102:     {
103:         if (isset($this->config['parameters'])) {
104:             $this->builder->parameters = Helpers::expand($this->config['parameters'], $this->config['parameters'], TRUE);
105:         }
106:     }
107: 
108: 
109:     public function processExtensions()
110:     {
111:         for ($i = 0; $slice = array_slice($this->extensions, $i, 1, TRUE); $i++) {
112:             $name = key($slice);
113:             if (isset($this->config[$name])) {
114:                 $this->config[$name] = $this->builder->expand($this->config[$name]);
115:             }
116:             $this->extensions[$name]->loadConfiguration();
117:         }
118: 
119:         if ($extra = array_diff_key($this->config, self::$reserved, $this->extensions)) {
120:             $extra = implode("', '", array_keys($extra));
121:             throw new Nette\InvalidStateException("Found sections '$extra' in configuration, but corresponding extensions are missing.");
122:         }
123:     }
124: 
125: 
126:     public function processServices()
127:     {
128:         $this->parseServices($this->builder, $this->config);
129: 
130:         foreach ($this->extensions as $name => $extension) {
131:             if (isset($this->config[$name])) {
132:                 $this->parseServices($this->builder, $this->config[$name], $name);
133:             }
134:         }
135:     }
136: 
137: 
138:     public function generateCode($className, $parentName)
139:     {
140:         foreach ($this->extensions as $extension) {
141:             $extension->beforeCompile();
142:             $this->builder->addDependency(Nette\Reflection\ClassType::from($extension)->getFileName());
143:         }
144: 
145:         $classes = $this->builder->generateClasses($className, $parentName);
146:         $classes[0]->addMethod('initialize');
147: 
148:         foreach ($this->extensions as $extension) {
149:             $extension->afterCompile($classes[0]);
150:         }
151:         return implode("\n\n\n", $classes);
152:     }
153: 
154: 
155:     /********************* tools ****************d*g**/
156: 
157: 
158:     /**
159:      * Parses section 'services' from (unexpanded) configuration file.
160:      * @return void
161:      */
162:     public static function parseServices(ContainerBuilder $builder, array $config, $namespace = NULL)
163:     {
164:         if (!empty($config['factories'])) {
165:             trigger_error("Section 'factories' is deprecated, move definitions to section 'services' and append key 'autowired: no'.", E_USER_DEPRECATED);
166:         }
167: 
168:         $services = isset($config['services']) ? $config['services'] : array();
169:         $factories = isset($config['factories']) ? $config['factories'] : array();
170:         $all = array_merge($services, $factories);
171: 
172:         $depths = array();
173:         foreach ($all as $name => $def) {
174:             $path = array();
175:             while (Config\Helpers::isInheriting($def)) {
176:                 $path[] = $def;
177:                 $def = isset($all[$def[Config\Helpers::EXTENDS_KEY]]) ? $all[$def[Config\Helpers::EXTENDS_KEY]] : array();
178:                 if (in_array($def, $path, TRUE)) {
179:                     throw new ServiceCreationException("Circular reference detected for service '$name'.");
180:                 }
181:             }
182:             $depths[$name] = count($path);
183:         }
184:         array_multisort($depths, $all);
185: 
186:         foreach ($all as $origName => $def) {
187:             if ((string) (int) $origName === (string) $origName) {
188:                 $name = (count($builder->getDefinitions()) + 1)
189:                     . preg_replace('#\W+#', '_', $def instanceof \stdClass ? ".$def->value" : (is_scalar($def) ? ".$def" : ''));
190:             } elseif (array_key_exists($origName, $services) && array_key_exists($origName, $factories)) {
191:                 throw new ServiceCreationException("It is not allowed to use services and factories with the same name: '$origName'.");
192:             } else {
193:                 $name = ($namespace ? $namespace . '.' : '') . strtr($origName, '\\', '_');
194:             }
195: 
196:             $params = $builder->parameters;
197:             if (is_array($def) && isset($def['parameters'])) {
198:                 foreach ((array) $def['parameters'] as $k => $v) {
199:                     $v = explode(' ', is_int($k) ? $v : $k);
200:                     $params[end($v)] = $builder::literal('$' . end($v));
201:                 }
202:             }
203:             $def = Helpers::expand($def, $params);
204: 
205:             if (($parent = Config\Helpers::takeParent($def)) && $parent !== $name) {
206:                 $builder->removeDefinition($name);
207:                 $definition = $builder->addDefinition(
208:                     $name,
209:                     $parent === Config\Helpers::OVERWRITE ? NULL : unserialize(serialize($builder->getDefinition($parent))) // deep clone
210:                 );
211:             } elseif ($builder->hasDefinition($name)) {
212:                 $definition = $builder->getDefinition($name);
213:             } else {
214:                 $definition = $builder->addDefinition($name);
215:             }
216: 
217:             try {
218:                 static::parseService($definition, $def);
219:             } catch (\Exception $e) {
220:                 throw new ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e);
221:             }
222: 
223:             if ($definition->class === 'self') {
224:                 $definition->class = $origName;
225:                 trigger_error("Replace service definition '$origName: self' with '- $origName'.", E_USER_DEPRECATED);
226:             }
227:             if ($definition->factory && $definition->factory->entity === 'self') {
228:                 $definition->factory->entity = $origName;
229:                 trigger_error("Replace service definition '$origName: self' with '- $origName'.", E_USER_DEPRECATED);
230:             }
231:         }
232:     }
233: 
234: 
235:     /**
236:      * Parses single service from configuration file.
237:      * @return void
238:      */
239:     public static function parseService(ServiceDefinition $definition, $config)
240:     {
241:         if ($config === NULL) {
242:             return;
243: 
244:         } elseif (is_string($config) && interface_exists($config)) {
245:             $config = array('class' => NULL, 'implement' => $config);
246: 
247:         } elseif ($config instanceof \stdClass && interface_exists($config->value)) {
248:             $config = array('class' => NULL, 'implement' => $config->value, 'factory' => array_shift($config->attributes));
249: 
250:         } elseif (!is_array($config)) {
251:             $config = array('class' => NULL, 'create' => $config);
252:         }
253: 
254:         if (array_key_exists('factory', $config)) {
255:             $config['create'] = $config['factory'];
256:             unset($config['factory']);
257:         };
258: 
259:         $known = array('class', 'create', 'arguments', 'setup', 'autowired', 'inject', 'parameters', 'implement', 'run', 'tags');
260:         if ($error = array_diff(array_keys($config), $known)) {
261:             throw new Nette\InvalidStateException(sprintf("Unknown or deprecated key '%s' in definition of service.", implode("', '", $error)));
262:         }
263: 
264:         $arguments = array();
265:         if (array_key_exists('arguments', $config)) {
266:             Validators::assertField($config, 'arguments', 'array');
267:             $arguments = self::filterArguments($config['arguments']);
268:             $definition->setArguments($arguments);
269:         }
270: 
271:         if (array_key_exists('class', $config) || array_key_exists('create', $config)) {
272:             $definition->class = NULL;
273:             $definition->factory = NULL;
274:         }
275: 
276:         if (array_key_exists('class', $config)) {
277:             Validators::assertField($config, 'class', 'string|stdClass|null');
278:             if ($config['class'] instanceof \stdClass) {
279:                 $definition->setClass($config['class']->value, self::filterArguments($config['class']->attributes));
280:             } else {
281:                 $definition->setClass($config['class'], $arguments);
282:             }
283:         }
284: 
285:         if (array_key_exists('create', $config)) {
286:             Validators::assertField($config, 'create', 'callable|stdClass|null');
287:             if ($config['create'] instanceof \stdClass) {
288:                 $definition->setFactory($config['create']->value, self::filterArguments($config['create']->attributes));
289:             } else {
290:                 $definition->setFactory($config['create'], $arguments);
291:             }
292:         }
293: 
294:         if (isset($config['setup'])) {
295:             if (Config\Helpers::takeParent($config['setup'])) {
296:                 $definition->setup = array();
297:             }
298:             Validators::assertField($config, 'setup', 'list');
299:             foreach ($config['setup'] as $id => $setup) {
300:                 Validators::assert($setup, 'callable|stdClass', "setup item #$id");
301:                 if ($setup instanceof \stdClass) {
302:                     Validators::assert($setup->value, 'callable', "setup item #$id");
303:                     $definition->addSetup($setup->value, self::filterArguments($setup->attributes));
304:                 } else {
305:                     $definition->addSetup($setup);
306:                 }
307:             }
308:         }
309: 
310:         if (isset($config['parameters'])) {
311:             Validators::assertField($config, 'parameters', 'array');
312:             $definition->setParameters($config['parameters']);
313:         }
314: 
315:         if (isset($config['implement'])) {
316:             Validators::assertField($config, 'implement', 'string');
317:             $definition->setImplement($config['implement']);
318:             $definition->setAutowired(TRUE);
319:         }
320: 
321:         if (isset($config['autowired'])) {
322:             Validators::assertField($config, 'autowired', 'bool');
323:             $definition->setAutowired($config['autowired']);
324:         }
325: 
326:         if (isset($config['inject'])) {
327:             Validators::assertField($config, 'inject', 'bool');
328:             $definition->setInject($config['inject']);
329:         }
330: 
331:         if (isset($config['run'])) {
332:             $config['tags']['run'] = (bool) $config['run'];
333:         }
334: 
335:         if (isset($config['tags'])) {
336:             Validators::assertField($config, 'tags', 'array');
337:             if (Config\Helpers::takeParent($config['tags'])) {
338:                 $definition->tags = array();
339:             }
340:             foreach ($config['tags'] as $tag => $attrs) {
341:                 if (is_int($tag) && is_string($attrs)) {
342:                     $definition->addTag($attrs);
343:                 } else {
344:                     $definition->addTag($tag, $attrs);
345:                 }
346:             }
347:         }
348:     }
349: 
350: 
351:     /**
352:      * Removes ... and replaces entities with Statement.
353:      * @return array
354:      */
355:     public static function filterArguments(array $args)
356:     {
357:         foreach ($args as $k => $v) {
358:             if ($v === '...') {
359:                 unset($args[$k]);
360:             } elseif (is_array($v)) {
361:                 $args[$k] = self::filterArguments($v);
362:             } elseif ($v instanceof \stdClass && isset($v->value, $v->attributes)) {
363:                 $args[$k] = new Statement($v->value, self::filterArguments($v->attributes));
364:             }
365:         }
366:         return $args;
367:     }
368: 
369: }
370: 
Nette 2.2 API documentation generated by ApiGen 2.8.0