Overview

Namespaces

  • Budovy
  • Kdyby
    • BootstrapFormRenderer
      • DI
      • Latte
  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • Nextras
    • Datagrid
  • None
  • PHP
  • Tester
    • CodeCoverage
    • Runner
      • Output
  • Vodacek
    • Forms
      • Controls
  • WebLoader
    • Filter
    • Nette

Classes

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

Exceptions

  • MissingServiceException
  • ServiceCreationException
  • Overview
  • Namespace
  • Class
  • Tree
  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 (array_key_exists($origName, $factories)) {
219:                 $definition->setAutowired(FALSE);
220:             }
221: 
222:             if ($definition->class === 'self') {
223:                 $definition->class = $origName;
224:                 trigger_error("Replace service definition '$origName: self' with '- $origName'.", E_USER_DEPRECATED);
225:             }
226:             if ($definition->factory && $definition->factory->entity === 'self') {
227:                 $definition->factory->entity = $origName;
228:                 trigger_error("Replace service definition '$origName: self' with '- $origName'.", E_USER_DEPRECATED);
229:             }
230:         }
231:     }
232: 
233: 
234:     /**
235:      * Parses single service from configuration file.
236:      * @return void
237:      */
238:     public static function parseService(ServiceDefinition $definition, $config)
239:     {
240:         if ($config === NULL) {
241:             return;
242: 
243:         } elseif (is_string($config) && interface_exists($config)) {
244:             $config = array('class' => NULL, 'implement' => $config);
245: 
246:         } elseif ($config instanceof \stdClass && interface_exists($config->value)) {
247:             $config = array('class' => NULL, 'implement' => $config->value, 'factory' => array_shift($config->attributes));
248: 
249:         } elseif (!is_array($config)) {
250:             $config = array('class' => NULL, 'create' => $config);
251:         }
252: 
253:         if (array_key_exists('factory', $config)) {
254:             $config['create'] = $config['factory'];
255:             unset($config['factory']);
256:         };
257: 
258:         $known = array('class', 'create', 'arguments', 'setup', 'autowired', 'inject', 'parameters', 'implement', 'run', 'tags');
259:         if ($error = array_diff(array_keys($config), $known)) {
260:             throw new Nette\InvalidStateException("Unknown or deprecated key '" . implode("', '", $error) . "' in definition of service.");
261:         }
262: 
263:         $arguments = array();
264:         if (array_key_exists('arguments', $config)) {
265:             Validators::assertField($config, 'arguments', 'array');
266:             $arguments = self::filterArguments($config['arguments']);
267:             $definition->setArguments($arguments);
268:         }
269: 
270:         if (array_key_exists('class', $config) || array_key_exists('create', $config)) {
271:             $definition->class = NULL;
272:             $definition->factory = NULL;
273:         }
274: 
275:         if (array_key_exists('class', $config)) {
276:             Validators::assertField($config, 'class', 'string|stdClass|null');
277:             if ($config['class'] instanceof \stdClass) {
278:                 $definition->setClass($config['class']->value, self::filterArguments($config['class']->attributes));
279:             } else {
280:                 $definition->setClass($config['class'], $arguments);
281:             }
282:         }
283: 
284:         if (array_key_exists('create', $config)) {
285:             Validators::assertField($config, 'create', 'callable|stdClass|null');
286:             if ($config['create'] instanceof \stdClass) {
287:                 $definition->setFactory($config['create']->value, self::filterArguments($config['create']->attributes));
288:             } else {
289:                 $definition->setFactory($config['create'], $arguments);
290:             }
291:         }
292: 
293:         if (isset($config['setup'])) {
294:             if (Config\Helpers::takeParent($config['setup'])) {
295:                 $definition->setup = array();
296:             }
297:             Validators::assertField($config, 'setup', 'list');
298:             foreach ($config['setup'] as $id => $setup) {
299:                 Validators::assert($setup, 'callable|stdClass', "setup item #$id");
300:                 if ($setup instanceof \stdClass) {
301:                     Validators::assert($setup->value, 'callable', "setup item #$id");
302:                     $definition->addSetup($setup->value, self::filterArguments($setup->attributes));
303:                 } else {
304:                     $definition->addSetup($setup);
305:                 }
306:             }
307:         }
308: 
309:         if (isset($config['parameters'])) {
310:             Validators::assertField($config, 'parameters', 'array');
311:             $definition->setParameters($config['parameters']);
312:         }
313: 
314:         if (isset($config['implement'])) {
315:             Validators::assertField($config, 'implement', 'string');
316:             $definition->setImplement($config['implement']);
317:             $definition->setAutowired(TRUE);
318:         }
319: 
320:         if (isset($config['autowired'])) {
321:             Validators::assertField($config, 'autowired', 'bool');
322:             $definition->setAutowired($config['autowired']);
323:         }
324: 
325:         if (isset($config['inject'])) {
326:             Validators::assertField($config, 'inject', 'bool');
327:             $definition->setInject($config['inject']);
328:         }
329: 
330:         if (isset($config['run'])) {
331:             $config['tags']['run'] = (bool) $config['run'];
332:         }
333: 
334:         if (isset($config['tags'])) {
335:             Validators::assertField($config, 'tags', 'array');
336:             if (Config\Helpers::takeParent($config['tags'])) {
337:                 $definition->tags = array();
338:             }
339:             foreach ($config['tags'] as $tag => $attrs) {
340:                 if (is_int($tag) && is_string($attrs)) {
341:                     $definition->addTag($attrs);
342:                 } else {
343:                     $definition->addTag($tag, $attrs);
344:                 }
345:             }
346:         }
347:     }
348: 
349: 
350:     /**
351:      * Removes ... and replaces entities with Statement.
352:      * @return array
353:      */
354:     public static function filterArguments(array $args)
355:     {
356:         foreach ($args as $k => $v) {
357:             if ($v === '...') {
358:                 unset($args[$k]);
359:             } elseif ($v instanceof \stdClass && isset($v->value, $v->attributes)) {
360:                 $args[$k] = new Statement($v->value, self::filterArguments($v->attributes));
361:             }
362:         }
363:         return $args;
364:     }
365: 
366: }
367: 
API documentation generated by ApiGen 2.8.0