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

Classes

Interfaces

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