Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • none

Classes

  • Compiler
  • CompilerExtension
  • Configurator
  • Helpers
  • Loader

Interfaces

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