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