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
  • PHP

Classes

  • Compiler
  • CompilerExtension
  • Configurator
  • Helpers
  • Loader

Interfaces

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