Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • Compiler
  • CompilerExtension
  • Container
  • ContainerBuilder
  • ContainerFactory
  • Helpers
  • ServiceDefinition
  • Statement

Exceptions

  • MissingServiceException
  • ServiceCreationException
  • 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\DI;
  9: 
 10: use Nette;
 11: use Nette\Utils\Validators;
 12: use Nette\Utils\Strings;
 13: use Nette\Reflection;
 14: use Nette\PhpGenerator\Helpers as PhpHelpers;
 15: 
 16: 
 17: /**
 18:  * Basic container builder.
 19:  *
 20:  * @author     David Grudl
 21:  * @property-read ServiceDefinition[] $definitions
 22:  * @property-read array $dependencies
 23:  */
 24: class ContainerBuilder extends Nette\Object
 25: {
 26:     const THIS_SERVICE = 'self',
 27:         THIS_CONTAINER = 'container';
 28: 
 29:     /** @var array */
 30:     public $parameters = array();
 31: 
 32:     /** @var ServiceDefinition[] */
 33:     private $definitions = array();
 34: 
 35:     /** @var array for auto-wiring */
 36:     private $classes;
 37: 
 38:     /** @var string[] of classes excluded from auto-wiring */
 39:     private $excludedClasses = array();
 40: 
 41:     /** @var array of file names */
 42:     private $dependencies = array();
 43: 
 44:     /** @var Nette\PhpGenerator\ClassType[] */
 45:     private $generatedClasses = array();
 46: 
 47:     /** @var string */
 48:     /*private in 5.4*/public $currentService;
 49: 
 50: 
 51:     /**
 52:      * Adds new service definition.
 53:      * @param  string
 54:      * @return ServiceDefinition
 55:      */
 56:     public function addDefinition($name, ServiceDefinition $definition = NULL)
 57:     {
 58:         if (!is_string($name) || !$name) { // builder is not ready for falsy names such as '0'
 59:             throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($name)));
 60: 
 61:         } elseif (isset($this->definitions[$name])) {
 62:             throw new Nette\InvalidStateException("Service '$name' has already been added.");
 63:         }
 64:         return $this->definitions[$name] = $definition ?: new ServiceDefinition;
 65:     }
 66: 
 67: 
 68:     /**
 69:      * Removes the specified service definition.
 70:      * @param  string
 71:      * @return void
 72:      */
 73:     public function removeDefinition($name)
 74:     {
 75:         unset($this->definitions[$name]);
 76:     }
 77: 
 78: 
 79:     /**
 80:      * Gets the service definition.
 81:      * @param  string
 82:      * @return ServiceDefinition
 83:      */
 84:     public function getDefinition($name)
 85:     {
 86:         if (!isset($this->definitions[$name])) {
 87:             throw new MissingServiceException("Service '$name' not found.");
 88:         }
 89:         return $this->definitions[$name];
 90:     }
 91: 
 92: 
 93:     /**
 94:      * Gets all service definitions.
 95:      * @return ServiceDefinition[]
 96:      */
 97:     public function getDefinitions()
 98:     {
 99:         return $this->definitions;
100:     }
101: 
102: 
103:     /**
104:      * Does the service definition exist?
105:      * @param  string
106:      * @return bool
107:      */
108:     public function hasDefinition($name)
109:     {
110:         return isset($this->definitions[$name]);
111:     }
112: 
113: 
114:     /********************* class resolving ****************d*g**/
115: 
116: 
117:     /**
118:      * Resolves service name by type.
119:      * @param  string  class or interface
120:      * @return string  service name or NULL
121:      * @throws ServiceCreationException
122:      */
123:     public function getByType($class)
124:     {
125:         if ($this->currentService !== NULL && Reflection\ClassType::from($this->definitions[$this->currentService]->class)->is($class)) {
126:             return $this->currentService;
127:         }
128: 
129:         $lower = ltrim(strtolower($class), '\\');
130:         if (!isset($this->classes[$lower])) {
131:             return;
132: 
133:         } elseif (count($this->classes[$lower]) === 1) {
134:             return $this->classes[$lower][0];
135: 
136:         } else {
137:             throw new ServiceCreationException("Multiple services of type $class found: " . implode(', ', $this->classes[$lower]));
138:         }
139:     }
140: 
141: 
142:     /**
143:      * Gets the service objects of the specified tag.
144:      * @param  string
145:      * @return array of [service name => tag attributes]
146:      */
147:     public function findByTag($tag)
148:     {
149:         $found = array();
150:         foreach ($this->definitions as $name => $def) {
151:             if (isset($def->tags[$tag])) {
152:                 $found[$name] = $def->tags[$tag];
153:             }
154:         }
155:         return $found;
156:     }
157: 
158: 
159:     /**
160:      * Creates a list of arguments using autowiring.
161:      * @return array
162:      */
163:     public function autowireArguments($class, $method, array $arguments)
164:     {
165:         $rc = Reflection\ClassType::from($class);
166:         if (!$rc->hasMethod($method)) {
167:             if (!Nette\Utils\Arrays::isList($arguments)) {
168:                 throw new ServiceCreationException("Unable to pass specified arguments to $class::$method().");
169:             }
170:             return $arguments;
171:         }
172: 
173:         $rm = $rc->getMethod($method);
174:         if (!$rm->isPublic()) {
175:             throw new ServiceCreationException("$rm is not callable.");
176:         }
177:         $this->addDependency($rm->getFileName());
178:         return Helpers::autowireArguments($rm, $arguments, $this);
179:     }
180: 
181: 
182:     /**
183:      * Generates $dependencies, $classes and normalizes class names.
184:      * @return array
185:      */
186:     public function prepareClassList()
187:     {
188:         unset($this->definitions[self::THIS_CONTAINER]);
189:         $this->addDefinition(self::THIS_CONTAINER)->setClass('Nette\DI\Container');
190: 
191:         $this->classes = FALSE;
192: 
193:         // prepare generated factories
194:         foreach ($this->definitions as $name => $def) {
195:             if (!$def->implement) {
196:                 continue;
197:             }
198: 
199:             if (!interface_exists($def->implement)) {
200:                 throw new ServiceCreationException("Interface $def->implement used in service '$name' not found.");
201:             }
202:             $rc = Reflection\ClassType::from($def->implement);
203:             $method = $rc->hasMethod('create') ? $rc->getMethod('create') : ($rc->hasMethod('get') ? $rc->getMethod('get') : NULL);
204:             if (count($rc->getMethods()) !== 1 || !$method || $method->isStatic()) {
205:                 throw new ServiceCreationException("Interface $def->implement used in service '$name' must have just one non-static method create() or get().");
206:             }
207:             $def->implement = $rc->getName();
208:             $def->implementType = $rc->hasMethod('create') ? 'create' : 'get';
209: 
210:             if (!$def->class && empty($def->factory->entity)) {
211:                 $returnType = $method->getAnnotation('return');
212:                 if (!$returnType) {
213:                     throw new ServiceCreationException("Method $method used in service '$name' has no @return annotation.");
214:                 }
215: 
216:                 $returnType = Reflection\AnnotationsParser::expandClassName(preg_replace('#[|\s].*#', '', $returnType), $rc);
217:                 if (!class_exists($returnType)) {
218:                     throw new ServiceCreationException("Please check a @return annotation of the $method method. Class '$returnType' cannot be found.");
219:                 }
220:                 $def->setClass($returnType);
221:             }
222: 
223:             if ($method->getName() === 'get') {
224:                 if ($method->getParameters()) {
225:                     throw new ServiceCreationException("Method $method used in service '$name' must have no arguments.");
226:                 }
227:                 if (empty($def->factory->entity)) {
228:                     $def->setFactory('@\\' . ltrim($def->class, '\\'));
229:                 } elseif (!$this->getServiceName($def->factory->entity)) {
230:                     throw new ServiceCreationException("Invalid factory in service '$name' definition.");
231:                 }
232:             }
233: 
234:             if (!$def->parameters) {
235:                 foreach ($method->getParameters() as $param) {
236:                     $paramDef = ($param->isArray() ? 'array' : $param->getClassName()) . ' ' . $param->getName();
237:                     if ($param->isOptional()) {
238:                         $def->parameters[$paramDef] = $param->getDefaultValue();
239:                     } else {
240:                         $def->parameters[] = $paramDef;
241:                     }
242:                 }
243:             }
244:         }
245: 
246:         // complete class-factory pairs
247:         foreach ($this->definitions as $name => $def) {
248:             if (!$def->factory || !$def->factory->entity) {
249:                 if (!$def->class) {
250:                     throw new ServiceCreationException("Class and factory are missing in service '$name' definition.");
251:                 }
252:                 if ($def->factory) {
253:                     $def->factory->entity = $def->class;
254:                 } else {
255:                     $def->factory = new Statement($def->class);
256:                 }
257:             }
258:         }
259: 
260:         // check if services are instantiable
261:         foreach ($this->definitions as $name => $def) {
262:             $factory = $def->factory->entity = $this->normalizeEntity($def->factory->entity);
263: 
264:             if (is_string($factory) && preg_match('#^[\w\\\\]+\z#', $factory) && $factory !== self::THIS_SERVICE) {
265:                 if (!class_exists($factory) || !Reflection\ClassType::from($factory)->isInstantiable()) {
266:                     throw new ServiceCreationException("Class $factory used in service '$name' not found or is not instantiable.");
267:                 }
268:             }
269:         }
270: 
271:         // complete classes
272:         foreach ($this->definitions as $name => $def) {
273:             $this->resolveClass($name);
274: 
275:             if (!$def->class) {
276:                 continue;
277:             } elseif (!class_exists($def->class) && !interface_exists($def->class)) {
278:                 throw new ServiceCreationException("Class or interface $def->class used in service '$name' not found.");
279:             } else {
280:                 $def->class = Reflection\ClassType::from($def->class)->getName();
281:             }
282:         }
283: 
284:         //  build auto-wiring list
285:         $this->classes = array();
286:         foreach ($this->definitions as $name => $def) {
287:             $class = $def->implement ?: $def->class;
288:             if ($def->autowired && $class) {
289:                 foreach (class_parents($class) + class_implements($class) + array($class) as $parent) {
290:                     $this->classes[strtolower($parent)][] = (string) $name;
291:                 }
292:             }
293:         }
294: 
295:         foreach ($this->excludedClasses as $class) {
296:             foreach (class_parents($class) + class_implements($class) + array($class) as $parent) {
297:                 unset($this->classes[strtolower($parent)]);
298:             }
299:         }
300: 
301:         foreach ($this->classes as $class => $foo) {
302:             $this->addDependency(Reflection\ClassType::from($class)->getFileName());
303:         }
304:     }
305: 
306: 
307:     private function resolveClass($name, $recursive = array())
308:     {
309:         if (isset($recursive[$name])) {
310:             throw new ServiceCreationException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($recursive))));
311:         }
312:         $recursive[$name] = TRUE;
313: 
314:         $def = $this->definitions[$name];
315:         $factory = $def->factory->entity;
316: 
317:         if ($def->class) {
318:             return $def->class;
319: 
320:         } elseif (is_array($factory)) { // method calling
321:             if ($service = $this->getServiceName($factory[0])) {
322:                 if (Strings::contains($service, '\\')) { // @\Class
323:                     $factory[0] = $service;
324:                 } else {
325:                     $factory[0] = $this->resolveClass($service, $recursive);
326:                     if (!$factory[0]) {
327:                         return;
328:                     }
329:                     if ($this->definitions[$service]->implement && $factory[1] === 'create') {
330:                         return $def->class = $factory[0];
331:                     }
332:                 }
333:             }
334:             try {
335:                 $reflection = Nette\Utils\Callback::toReflection($factory);
336:             } catch (\ReflectionException $e) {
337:             }
338:             if (isset($e) || !is_callable($factory)) {
339:                 throw new ServiceCreationException(sprintf("Factory '%s' used in service '%s' is not callable.", Nette\Utils\Callback::toString($factory), $name));
340:             }
341:             $def->class = preg_replace('#[|\s].*#', '', $reflection->getAnnotation('return'));
342:             if ($def->class && $reflection instanceof \ReflectionMethod) {
343:                 $def->class = Reflection\AnnotationsParser::expandClassName($def->class, $reflection->getDeclaringClass());
344:             }
345: 
346:         } elseif ($service = $this->getServiceName($factory)) { // alias or factory
347:             if (!$def->implement) {
348:                 $def->autowired = FALSE;
349:             }
350:             if (Strings::contains($service, '\\')) { // @\Class
351:                 return $def->class = $service;
352:             }
353:             if ($this->definitions[$service]->implement) {
354:                 $def->autowired = FALSE;
355:             }
356:             return $def->class = $this->definitions[$service]->implement ?: $this->resolveClass($service, $recursive);
357: 
358:         } else {
359:             return $def->class = $factory; // class name
360:         }
361:     }
362: 
363: 
364:     /**
365:      * @param string[]
366:      * @return self
367:      */
368:     public function addExcludedClasses(array $classes)
369:     {
370:         $this->excludedClasses = array_merge($this->excludedClasses, $classes);
371:         return $this;
372:     }
373: 
374: 
375:     /**
376:      * Adds a file to the list of dependencies.
377:      * @return self
378:      */
379:     public function addDependency($file)
380:     {
381:         $this->dependencies[$file] = TRUE;
382:         return $this;
383:     }
384: 
385: 
386:     /**
387:      * Returns the list of dependent files.
388:      * @return array
389:      */
390:     public function getDependencies()
391:     {
392:         unset($this->dependencies[FALSE]);
393:         return array_keys($this->dependencies);
394:     }
395: 
396: 
397:     /********************* code generator ****************d*g**/
398: 
399: 
400:     /**
401:      * Generates PHP classes. First class is the container.
402:      * @return Nette\PhpGenerator\ClassType[]
403:      */
404:     public function generateClasses($className = 'Container', $parentName = 'Nette\DI\Container')
405:     {
406:         $this->generatedClasses = array();
407:         $this->prepareClassList();
408: 
409:         $containerClass = $this->generatedClasses[] = new Nette\PhpGenerator\ClassType($className);
410:         $containerClass->setExtends($parentName);
411:         $containerClass->addMethod('__construct')
412:             ->addBody('parent::__construct(?);', array($this->parameters));
413: 
414:         $definitions = $this->definitions;
415:         ksort($definitions);
416: 
417:         $meta = $containerClass->addProperty('meta', array())
418:             ->setVisibility('protected')
419:             ->setValue(array(Container::TYPES => $this->classes));
420: 
421:         foreach ($definitions as $name => $def) {
422:             foreach ($def->tags as $tag => $value) {
423:                 $meta->value[Container::TAGS][$tag][$name] = $value;
424:             }
425:         }
426: 
427:         foreach ($definitions as $name => $def) {
428:             try {
429:                 $name = (string) $name;
430:                 $methodName = Container::getMethodName($name);
431:                 if (!PhpHelpers::isIdentifier($methodName)) {
432:                     throw new ServiceCreationException('Name contains invalid characters.');
433:                 }
434:                 $containerClass->addMethod($methodName)
435:                     ->addDocument('@return ' . ($def->implement ?: $def->class))
436:                     ->setBody($name === self::THIS_CONTAINER ? 'return $this;' : $this->generateService($name))
437:                     ->setParameters($def->implement ? array() : $this->convertParameters($def->parameters));
438:             } catch (\Exception $e) {
439:                 throw new ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e);
440:             }
441:         }
442: 
443:         return $this->generatedClasses;
444:     }
445: 
446: 
447:     /**
448:      * Generates body of service method.
449:      * @return string
450:      */
451:     private function generateService($name)
452:     {
453:         $this->currentService = NULL;
454:         $def = $this->definitions[$name];
455: 
456:         $entity = $def->factory->entity;
457:         $serviceRef = $this->getServiceName($entity);
458:         $factory = $serviceRef && !$def->factory->arguments && !$def->setup && $def->implementType !== 'create'
459:             ? new Statement(array('@' . self::THIS_CONTAINER, 'getService'), array($serviceRef))
460:             : $def->factory;
461: 
462:         $code = '$service = ' . $this->formatStatement($factory) . ";\n";
463:         $this->currentService = $name;
464: 
465:         if ($def->class && !$serviceRef && $def->class !== $entity
466:             && !(is_string($entity) && preg_match('#^[\w\\\\]+\z#', $entity) && is_subclass_of($entity, $def->class))
467:         ) {
468:             $code .= PhpHelpers::formatArgs("if (!\$service instanceof $def->class) {\n"
469:                 . "\tthrow new Nette\\UnexpectedValueException(?);\n}\n",
470:                 array("Unable to create service '$name', value returned by factory is not $def->class type.")
471:             );
472:         }
473: 
474:         $setups = (array) $def->setup;
475:         if ($def->inject && $def->class) {
476:             $injects = array();
477:             foreach (Helpers::getInjectProperties(Reflection\ClassType::from($def->class), $this) as $property => $type) {
478:                 $injects[] = new Statement('$' . $property, array('@\\' . ltrim($type, '\\')));
479:             }
480: 
481:             foreach (get_class_methods($def->class) as $method) {
482:                 if (substr($method, 0, 6) === 'inject') {
483:                     $injects[] = new Statement($method);
484:                 }
485:             }
486: 
487:             foreach ($injects as $inject) {
488:                 foreach ($setups as $key => $setup) {
489:                     if ($setup->entity === $inject->entity) {
490:                         $inject = $setup;
491:                         unset($setups[$key]);
492:                     }
493:                 }
494:                 array_unshift($setups, $inject);
495:             }
496:         }
497: 
498:         foreach ($setups as $setup) {
499:             if (is_string($setup->entity) && strpbrk($setup->entity, ':@?') === FALSE) { // auto-prepend @self
500:                 $setup->entity = array('@self', $setup->entity);
501:             }
502:             $code .= $this->formatStatement($setup) . ";\n";
503:         }
504:         $this->currentService = NULL;
505: 
506:         $code .= 'return $service;';
507: 
508:         if (!$def->implement) {
509:             return $code;
510:         }
511: 
512:         $factoryClass = $this->generatedClasses[] = new Nette\PhpGenerator\ClassType;
513:         $factoryClass->setName(str_replace(array('\\', '.'), '_', "{$this->generatedClasses[0]->name}_{$def->implement}Impl_{$name}"))
514:             ->addImplement($def->implement)
515:             ->setFinal(TRUE);
516: 
517:         $factoryClass->addProperty('container')
518:             ->setVisibility('private');
519: 
520:         $factoryClass->addMethod('__construct')
521:             ->addBody('$this->container = $container;')
522:             ->addParameter('container')
523:                 ->setTypeHint($this->generatedClasses[0]->name);
524: 
525:         $factoryClass->addMethod($def->implementType)
526:             ->setParameters($this->convertParameters($def->parameters))
527:             ->setBody(str_replace('$this', '$this->container', $code));
528: 
529:         return "return new {$factoryClass->name}(\$this);";
530:     }
531: 
532: 
533:     /**
534:      * Converts parameters from ServiceDefinition to PhpGenerator.
535:      * @return Nette\PhpGenerator\Parameter[]
536:      */
537:     private function convertParameters(array $parameters)
538:     {
539:         $res = array();
540:         foreach ($parameters as $k => $v) {
541:             $tmp = explode(' ', is_int($k) ? $v : $k);
542:             $param = $res[] = new Nette\PhpGenerator\Parameter;
543:             $param->setName(end($tmp));
544:             if (!is_int($k)) {
545:                 $param = $param->setOptional(TRUE)->setDefaultValue($v);
546:             }
547:             if (isset($tmp[1])) {
548:                 $param->setTypeHint($tmp[0]);
549:             }
550:         }
551:         return $res;
552:     }
553: 
554: 
555:     /**
556:      * Formats PHP code for class instantiating, function calling or property setting in PHP.
557:      * @return string
558:      * @internal
559:      */
560:     public function formatStatement(Statement $statement)
561:     {
562:         $entity = $this->normalizeEntity($statement->entity);
563:         $arguments = $statement->arguments;
564: 
565:         if (is_string($entity) && Strings::contains($entity, '?')) { // PHP literal
566:             return $this->formatPhp($entity, $arguments);
567: 
568:         } elseif ($service = $this->getServiceName($entity)) { // factory calling
569:             $params = array();
570:             foreach ($this->definitions[$service]->parameters as $k => $v) {
571:                 $params[] = preg_replace('#\w+\z#', '\$$0', (is_int($k) ? $v : $k)) . (is_int($k) ? '' : ' = ' . PhpHelpers::dump($v));
572:             }
573:             $rm = new Reflection\GlobalFunction(create_function(implode(', ', $params), ''));
574:             $arguments = Helpers::autowireArguments($rm, $arguments, $this);
575:             return $this->formatPhp('$this->?(?*)', array(Container::getMethodName($service), $arguments));
576: 
577:         } elseif ($entity === 'not') { // operator
578:             return $this->formatPhp('!?', array($arguments[0]));
579: 
580:         } elseif (is_string($entity)) { // class name
581:             if ($constructor = Reflection\ClassType::from($entity)->getConstructor()) {
582:                 $this->addDependency($constructor->getFileName());
583:                 $arguments = Helpers::autowireArguments($constructor, $arguments, $this);
584:             } elseif ($arguments) {
585:                 throw new ServiceCreationException("Unable to pass arguments, class $entity has no constructor.");
586:             }
587:             return $this->formatPhp("new $entity" . ($arguments ? '(?*)' : ''), array($arguments));
588: 
589:         } elseif (!Nette\Utils\Arrays::isList($entity) || count($entity) !== 2) {
590:             throw new ServiceCreationException(sprintf('Expected class, method or property, %s given.', PhpHelpers::dump($entity)));
591: 
592:         } elseif ($entity[0] === '') { // globalFunc
593:             return $this->formatPhp("$entity[1](?*)", array($arguments));
594: 
595:         } elseif (Strings::contains($entity[1], '$')) { // property setter
596:             Validators::assert($arguments, 'list:1', "setup arguments for '" . Nette\Utils\Callback::toString($entity) . "'");
597:             if ($this->getServiceName($entity[0])) {
598:                 return $this->formatPhp('?->? = ?', array($entity[0], substr($entity[1], 1), $arguments[0]));
599:             } else {
600:                 return $this->formatPhp($entity[0] . '::$? = ?', array(substr($entity[1], 1), $arguments[0]));
601:             }
602: 
603:         } elseif ($service = $this->getServiceName($entity[0])) { // service method
604:             $class = $this->definitions[$service]->implement;
605:             if (!$class || !method_exists($class, $entity[1])) {
606:                 $class = $this->definitions[$service]->class;
607:             }
608:             if ($class) {
609:                 $arguments = $this->autowireArguments($class, $entity[1], $arguments);
610:             }
611:             return $this->formatPhp('?->?(?*)', array($entity[0], $entity[1], $arguments));
612: 
613:         } else { // static method
614:             $arguments = $this->autowireArguments($entity[0], $entity[1], $arguments);
615:             return $this->formatPhp("$entity[0]::$entity[1](?*)", array($arguments));
616:         }
617:     }
618: 
619: 
620:     /**
621:      * Formats PHP statement.
622:      * @return string
623:      */
624:     public function formatPhp($statement, $args)
625:     {
626:         $that = $this;
627:         array_walk_recursive($args, function (& $val) use ($that) {
628:             if ($val instanceof Statement) {
629:                 $val = ContainerBuilder::literal($that->formatStatement($val));
630: 
631:             } elseif ($val === $that) {
632:                 $val = ContainerBuilder::literal('$this');
633: 
634:             } elseif ($val instanceof ServiceDefinition) {
635:                 $val = '@' . current(array_keys($that->definitions, $val, TRUE));
636: 
637:             } elseif (is_string($val) && preg_match('#^[\w\\\\]*::[A-Z][A-Z0-9_]*\z#', $val, $m)) {
638:                 $val = ContainerBuilder::literal(ltrim($val, ':'));
639:             }
640: 
641:             if (is_string($val) && substr($val, 0, 1) === '@') {
642:                 $pair = explode('::', $val, 2);
643:                 $name = $that->getServiceName($pair[0]);
644:                 if (isset($pair[1]) && preg_match('#^[A-Z][A-Z0-9_]*\z#', $pair[1], $m)) {
645:                     $val = $that->definitions[$name]->class . '::' . $pair[1];
646:                 } else {
647:                     if ($name === ContainerBuilder::THIS_CONTAINER) {
648:                         $val = '$this';
649:                     } elseif ($name === $that->currentService) {
650:                         $val = '$service';
651:                     } else {
652:                         $val = $that->formatStatement(new Statement(array('@' . ContainerBuilder::THIS_CONTAINER, 'getService'), array($name)));
653:                     }
654:                     $val .= (isset($pair[1]) ? PhpHelpers::formatArgs('->?', array($pair[1])) : '');
655:                 }
656:                 $val = ContainerBuilder::literal($val);
657:             }
658:         });
659:         return PhpHelpers::formatArgs($statement, $args);
660:     }
661: 
662: 
663:     /**
664:      * Expands %placeholders% in strings.
665:      * @return mixed
666:      */
667:     public function expand($value)
668:     {
669:         return Helpers::expand($value, $this->parameters);
670:     }
671: 
672: 
673:     /**
674:      * @return Nette\PhpGenerator\PhpLiteral
675:      */
676:     public static function literal($phpCode)
677:     {
678:         return new Nette\PhpGenerator\PhpLiteral($phpCode);
679:     }
680: 
681: 
682:     /** @internal */
683:     public function normalizeEntity($entity)
684:     {
685:         if (is_string($entity) && Strings::contains($entity, '::') && !Strings::contains($entity, '?')) { // Class::method -> [Class, method]
686:             $entity = explode('::', $entity);
687:         }
688: 
689:         if (is_array($entity) && $entity[0] instanceof ServiceDefinition) { // [ServiceDefinition, ...] -> [@serviceName, ...]
690:             $entity[0] = '@' . current(array_keys($this->definitions, $entity[0], TRUE));
691: 
692:         } elseif ($entity instanceof ServiceDefinition) { // ServiceDefinition -> @serviceName
693:             $entity = '@' . current(array_keys($this->definitions, $entity, TRUE));
694: 
695:         } elseif (is_array($entity) && $entity[0] === $this) { // [$this, ...] -> [@container, ...]
696:             $entity[0] = '@' . self::THIS_CONTAINER;
697:         }
698:         return $entity; // Class, @service, [Class, member], [@service, member], [, globalFunc]
699:     }
700: 
701: 
702:     /**
703:      * Converts @service or @\Class -> service name and checks its existence.
704:      * @return string  of FALSE, if argument is not service name
705:      */
706:     public function getServiceName($arg)
707:     {
708:         if (!is_string($arg) || !preg_match('#^@[\w\\\\.].*\z#', $arg)) {
709:             return FALSE;
710:         }
711:         $service = substr($arg, 1);
712:         if ($service === self::THIS_SERVICE) {
713:             $service = $this->currentService;
714:         }
715:         if (Strings::contains($service, '\\')) {
716:             if ($this->classes === FALSE) { // may be disabled by prepareClassList
717:                 return $service;
718:             }
719:             $res = $this->getByType($service);
720:             if (!$res) {
721:                 throw new ServiceCreationException("Reference to missing service of type $service.");
722:             }
723:             return $res;
724:         }
725:         if (!isset($this->definitions[$service])) {
726:             throw new ServiceCreationException("Reference to missing service '$service'.");
727:         }
728:         return $service;
729:     }
730: 
731: }
732: 
Nette 2.2 API documentation generated by ApiGen 2.8.0