Packages

  • 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

  • ConfigCompiler
  • ConfigCompilerExtension
  • ConfigHelpers
  • ConfigLoader
  • Configurator

Interfaces

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