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