Namespaces

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

Classes

  • Compiler
  • CompilerExtension
  • Container
  • ContainerBuilder
  • ContainerLoader
  • DependencyChecker
  • PhpGenerator
  • ServiceDefinition
  • Statement

Exceptions

  • MissingServiceException
  • ServiceCreationException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\DI;
  9: 
 10: use Nette;
 11: use Nette\PhpGenerator\Helpers as PhpHelpers;
 12: use Nette\Utils\Strings;
 13: use Nette\Utils\Validators;
 14: use ReflectionClass;
 15: 
 16: 
 17: /**
 18:  * Container builder.
 19:  */
 20: class ContainerBuilder
 21: {
 22:     use Nette\SmartObject;
 23: 
 24:     const THIS_SERVICE = 'self',
 25:         THIS_CONTAINER = 'container';
 26: 
 27:     /** @var array */
 28:     public $parameters = [];
 29: 
 30:     /** @var ServiceDefinition[] */
 31:     private $definitions = [];
 32: 
 33:     /** @var array of alias => service */
 34:     private $aliases = [];
 35: 
 36:     /** @var array for auto-wiring */
 37:     private $classList = [];
 38: 
 39:     /** @var bool */
 40:     private $classListNeedsRefresh = TRUE;
 41: 
 42:     /** @var string[] of classes excluded from auto-wiring */
 43:     private $excludedClasses = [];
 44: 
 45:     /** @var array */
 46:     private $dependencies = [];
 47: 
 48:     /** @var string */
 49:     private $currentService;
 50: 
 51: 
 52:     /**
 53:      * Adds new service definition.
 54:      * @param  string
 55:      * @return ServiceDefinition
 56:      */
 57:     public function addDefinition($name, ServiceDefinition $definition = NULL)
 58:     {
 59:         $this->classListNeedsRefresh = TRUE;
 60:         if (!is_string($name) || !$name) { // builder is not ready for falsy names such as '0'
 61:             throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($name)));
 62:         }
 63:         $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;
 64:         if (isset($this->definitions[$name])) {
 65:             throw new Nette\InvalidStateException("Service '$name' has already been added.");
 66:         }
 67:         if (!$definition) {
 68:             $definition = new ServiceDefinition;
 69:         }
 70:         $definition->setNotifier(function () {
 71:             $this->classListNeedsRefresh = TRUE;
 72:         });
 73:         return $this->definitions[$name] = $definition;
 74:     }
 75: 
 76: 
 77:     /**
 78:      * Removes the specified service definition.
 79:      * @param  string
 80:      * @return void
 81:      */
 82:     public function removeDefinition($name)
 83:     {
 84:         $this->classListNeedsRefresh = TRUE;
 85:         $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;
 86:         unset($this->definitions[$name]);
 87:     }
 88: 
 89: 
 90:     /**
 91:      * Gets the service definition.
 92:      * @param  string
 93:      * @return ServiceDefinition
 94:      */
 95:     public function getDefinition($name)
 96:     {
 97:         $service = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;
 98:         if (!isset($this->definitions[$service])) {
 99:             throw new MissingServiceException("Service '$name' not found.");
100:         }
101:         return $this->definitions[$service];
102:     }
103: 
104: 
105:     /**
106:      * Gets all service definitions.
107:      * @return ServiceDefinition[]
108:      */
109:     public function getDefinitions()
110:     {
111:         return $this->definitions;
112:     }
113: 
114: 
115:     /**
116:      * Does the service definition or alias exist?
117:      * @param  string
118:      * @return bool
119:      */
120:     public function hasDefinition($name)
121:     {
122:         $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name;
123:         return isset($this->definitions[$name]);
124:     }
125: 
126: 
127:     /**
128:      * @param  string
129:      * @param  string
130:      */
131:     public function addAlias($alias, $service)
132:     {
133:         if (!is_string($alias) || !$alias) { // builder is not ready for falsy names such as '0'
134:             throw new Nette\InvalidArgumentException(sprintf('Alias name must be a non-empty string, %s given.', gettype($alias)));
135: 
136:         } elseif (!is_string($service) || !$service) { // builder is not ready for falsy names such as '0'
137:             throw new Nette\InvalidArgumentException(sprintf('Service name must be a non-empty string, %s given.', gettype($service)));
138: 
139:         } elseif (isset($this->aliases[$alias])) {
140:             throw new Nette\InvalidStateException("Alias '$alias' has already been added.");
141: 
142:         } elseif (isset($this->definitions[$alias])) {
143:             throw new Nette\InvalidStateException("Service '$alias' has already been added.");
144: 
145:         }
146:         $this->aliases[$alias] = $service;
147:     }
148: 
149: 
150:     /**
151:      * Removes the specified alias.
152:      * @return void
153:      */
154:     public function removeAlias($alias)
155:     {
156:         unset($this->aliases[$alias]);
157:     }
158: 
159: 
160:     /**
161:      * Gets all service aliases.
162:      * @return array
163:      */
164:     public function getAliases()
165:     {
166:         return $this->aliases;
167:     }
168: 
169: 
170:     /**
171:      * @param  string[]
172:      * @return self
173:      */
174:     public function addExcludedClasses(array $classes)
175:     {
176:         foreach ($classes as $class) {
177:             if (class_exists($class) || interface_exists($class)) {
178:                 self::checkCase($class);
179:                 $this->excludedClasses += class_parents($class) + class_implements($class) + [$class => $class];
180:             }
181:         }
182:         return $this;
183:     }
184: 
185: 
186:     /**
187:      * @deprecated
188:      */
189:     public function setClassName($name)
190:     {
191:         trigger_error(__METHOD__ . ' has been deprecated', E_USER_DEPRECATED);
192:         return $this;
193:     }
194: 
195: 
196:     /**
197:      * @deprecated
198:      */
199:     public function getClassName()
200:     {
201:         trigger_error(__METHOD__ . ' has been deprecated', E_USER_DEPRECATED);
202:     }
203: 
204: 
205:     /********************* class resolving ****************d*g**/
206: 
207: 
208:     /**
209:      * Resolves service name by type.
210:      * @param  string  class or interface
211:      * @return string|NULL  service name or NULL
212:      * @throws ServiceCreationException
213:      */
214:     public function getByType($class)
215:     {
216:         $class = ltrim($class, '\\');
217: 
218:         if ($this->currentService !== NULL
219:             && is_a($this->definitions[$this->currentService]->getClass(), $class, TRUE)
220:         ) {
221:             return $this->currentService;
222:         }
223: 
224:         $classes = $this->getClassList();
225:         if (empty($classes[$class][TRUE])) {
226:             self::checkCase($class);
227:             return;
228: 
229:         } elseif (count($classes[$class][TRUE]) === 1) {
230:             return $classes[$class][TRUE][0];
231: 
232:         } else {
233:             $list = $classes[$class][TRUE];
234:             $hint = count($list) === 2 && ($tmp = strpos($list[0], '.') xor strpos($list[1], '.'))
235:                 ? '. If you want to overwrite service ' . $list[$tmp ? 0 : 1] . ', give it proper name.'
236:                 : '';
237:             throw new ServiceCreationException("Multiple services of type $class found: " . implode(', ', $list) . $hint);
238:         }
239:     }
240: 
241: 
242:     /**
243:      * Gets the service names and definitions of the specified type.
244:      * @param  string
245:      * @return ServiceDefinition[]
246:      */
247:     public function findByType($class)
248:     {
249:         $class = ltrim($class, '\\');
250:         self::checkCase($class);
251:         $found = [];
252:         $classes = $this->getClassList();
253:         if (!empty($classes[$class])) {
254:             foreach (array_merge(...array_values($classes[$class])) as $name) {
255:                 $found[$name] = $this->definitions[$name];
256:             }
257:         }
258:         return $found;
259:     }
260: 
261: 
262:     /**
263:      * Gets the service objects of the specified tag.
264:      * @param  string
265:      * @return array of [service name => tag attributes]
266:      */
267:     public function findByTag($tag)
268:     {
269:         $found = [];
270:         foreach ($this->definitions as $name => $def) {
271:             if (($tmp = $def->getTag($tag)) !== NULL) {
272:                 $found[$name] = $tmp;
273:             }
274:         }
275:         return $found;
276:     }
277: 
278: 
279:     /**
280:      * @internal
281:      */
282:     public function getClassList()
283:     {
284:         if ($this->classList !== FALSE && $this->classListNeedsRefresh) {
285:             $this->prepareClassList();
286:             $this->classListNeedsRefresh = FALSE;
287:         }
288:         return $this->classList ?: [];
289:     }
290: 
291: 
292:     /**
293:      * Generates $dependencies, $classes and normalizes class names.
294:      * @return array
295:      * @internal
296:      */
297:     public function prepareClassList()
298:     {
299:         unset($this->definitions[self::THIS_CONTAINER]);
300:         $this->addDefinition(self::THIS_CONTAINER)->setClass(Container::class);
301: 
302:         $this->classList = FALSE;
303: 
304:         foreach ($this->definitions as $name => $def) {
305:             // prepare generated factories
306:             if ($def->getImplement()) {
307:                 $this->resolveImplement($def, $name);
308:             }
309: 
310:             if ($def->isDynamic()) {
311:                 if (!$def->getClass()) {
312:                     throw new ServiceCreationException("Class is missing in definition of service '$name'.");
313:                 }
314:                 $def->setFactory(NULL);
315:                 continue;
316:             }
317: 
318:             // complete class-factory pairs
319:             if (!$def->getEntity()) {
320:                 if (!$def->getClass()) {
321:                     throw new ServiceCreationException("Class and factory are missing in definition of service '$name'.");
322:                 }
323:                 $def->setFactory($def->getClass(), ($factory = $def->getFactory()) ? $factory->arguments : []);
324:             }
325: 
326:             // auto-disable autowiring for aliases
327:             if (($alias = $this->getServiceName($def->getFactory()->getEntity())) &&
328:                 (!$def->getImplement() || (!Strings::contains($alias, '\\') && $this->definitions[$alias]->getImplement()))
329:             ) {
330:                 $def->setAutowired(FALSE);
331:             }
332:         }
333: 
334:         // resolve and check classes
335:         foreach ($this->definitions as $name => $def) {
336:             $this->resolveServiceClass($name);
337:         }
338: 
339:         //  build auto-wiring list
340:         $this->classList = $preferred = [];
341:         foreach ($this->definitions as $name => $def) {
342:             if ($class = $def->getImplement() ?: $def->getClass()) {
343:                 $defAutowired = $def->getAutowired();
344:                 if (is_array($defAutowired)) {
345:                     foreach ($defAutowired as $k => $aclass) {
346:                         if ($aclass === self::THIS_SERVICE) {
347:                             $defAutowired[$k] = $class;
348:                         } elseif (!is_a($class, $aclass, TRUE)) {
349:                             throw new ServiceCreationException("Incompatible class $aclass in autowiring definition of service '$name'.");
350:                         }
351:                     }
352:                 }
353: 
354:                 foreach (class_parents($class) + class_implements($class) + [$class] as $parent) {
355:                     $autowired = $defAutowired && empty($this->excludedClasses[$parent]);
356:                     if ($autowired && is_array($defAutowired)) {
357:                         $autowired = FALSE;
358:                         foreach ($defAutowired as $aclass) {
359:                             if (is_a($parent, $aclass, TRUE)) {
360:                                 if (empty($preferred[$parent]) && isset($this->classList[$parent][TRUE])) {
361:                                     $this->classList[$parent][FALSE] = array_merge(...$this->classList[$parent]);
362:                                     $this->classList[$parent][TRUE] = [];
363:                                 }
364:                                 $preferred[$parent] = $autowired = TRUE;
365:                                 break;
366:                             }
367:                         }
368:                     } elseif (isset($preferred[$parent])) {
369:                         $autowired = FALSE;
370:                     }
371:                     $this->classList[$parent][$autowired][] = (string) $name;
372:                 }
373:             }
374:         }
375:     }
376: 
377: 
378:     private function resolveImplement(ServiceDefinition $def, $name)
379:     {
380:         $interface = $def->getImplement();
381:         if (!interface_exists($interface)) {
382:             throw new ServiceCreationException("Interface $interface used in service '$name' not found.");
383:         }
384:         self::checkCase($interface);
385:         $rc = new ReflectionClass($interface);
386:         $this->addDependency($rc);
387:         $method = $rc->hasMethod('create')
388:             ? $rc->getMethod('create')
389:             : ($rc->hasMethod('get') ? $rc->getMethod('get') : NULL);
390: 
391:         if (count($rc->getMethods()) !== 1 || !$method || $method->isStatic()) {
392:             throw new ServiceCreationException("Interface $interface used in service '$name' must have just one non-static method create() or get().");
393:         }
394:         $def->setImplementMode($methodName = $rc->hasMethod('create') ? $def::IMPLEMENT_MODE_CREATE : $def::IMPLEMENT_MODE_GET);
395: 
396:         if (!$def->getClass() && !$def->getEntity()) {
397:             $returnType = PhpReflection::getReturnType($method);
398:             if (!$returnType) {
399:                 throw new ServiceCreationException("Method $interface::$methodName() used in service '$name' has no @return annotation.");
400:             } elseif (!class_exists($returnType)) {
401:                 throw new ServiceCreationException("Check a @return annotation of the $interface::$methodName() method used in service '$name', class '$returnType' cannot be found.");
402:             }
403:             $def->setClass($returnType);
404:         }
405: 
406:         if ($methodName === 'get') {
407:             if ($method->getParameters()) {
408:                 throw new ServiceCreationException("Method $interface::get() used in service '$name' must have no arguments.");
409:             }
410:             if (!$def->getEntity()) {
411:                 $def->setFactory('@\\' . ltrim($def->getClass(), '\\'));
412:             } elseif (!$this->getServiceName($def->getFactory()->getEntity())) {
413:                 throw new ServiceCreationException("Invalid factory in service '$name' definition.");
414:             }
415:         }
416: 
417:         if (!$def->parameters) {
418:             $ctorParams = [];
419:             if (!$def->getEntity()) {
420:                 $def->setFactory($def->getClass(), $def->getFactory() ? $def->getFactory()->arguments : []);
421:             }
422:             if (($class = $this->resolveEntityClass($def->getFactory(), [$name => 1]))
423:                 && ($ctor = (new ReflectionClass($class))->getConstructor())
424:             ) {
425:                 foreach ($ctor->getParameters() as $param) {
426:                     $ctorParams[$param->getName()] = $param;
427:                 }
428:             }
429: 
430:             foreach ($method->getParameters() as $param) {
431:                 $hint = PhpReflection::getParameterType($param);
432:                 if (isset($ctorParams[$param->getName()])) {
433:                     $arg = $ctorParams[$param->getName()];
434:                     if ($hint !== PhpReflection::getParameterType($arg)) {
435:                         throw new ServiceCreationException("Type hint for \${$param->getName()} in $interface::$methodName() doesn't match type hint in $class constructor.");
436:                     }
437:                     $def->getFactory()->arguments[$arg->getPosition()] = self::literal('$' . $arg->getName());
438:                 } elseif (!$def->getSetup()) {
439:                     $hint = Nette\Utils\ObjectMixin::getSuggestion(array_keys($ctorParams), $param->getName());
440:                     throw new ServiceCreationException("Unused parameter \${$param->getName()} when implementing method $interface::$methodName()" . ($hint ? ", did you mean \${$hint}?" : '.'));
441:                 }
442:                 $paramDef = $hint . ' ' . $param->getName();
443:                 if ($param->isDefaultValueAvailable()) {
444:                     $def->parameters[$paramDef] = $param->getDefaultValue();
445:                 } else {
446:                     $def->parameters[] = $paramDef;
447:                 }
448:             }
449:         }
450:     }
451: 
452: 
453:     /** @return string|NULL */
454:     private function resolveServiceClass($name, $recursive = [])
455:     {
456:         if (isset($recursive[$name])) {
457:             throw new ServiceCreationException(sprintf('Circular reference detected for services: %s.', implode(', ', array_keys($recursive))));
458:         }
459:         $recursive[$name] = TRUE;
460: 
461:         $def = $this->definitions[$name];
462:         $factoryClass = $def->getFactory() ? $this->resolveEntityClass($def->getFactory()->getEntity(), $recursive) : NULL; // call always to check entities
463:         if ($class = $def->getClass() ?: $factoryClass) {
464:             if (!class_exists($class) && !interface_exists($class)) {
465:                 throw new ServiceCreationException("Class or interface '$class' used in service '$name' not found.");
466:             }
467:             self::checkCase($class);
468:             $def->setClass($class);
469:             if (count($recursive) === 1) {
470:                 $this->addDependency(new ReflectionClass($factoryClass ?: $class));
471:             }
472: 
473:         } elseif ($def->getAutowired()) {
474:             throw new ServiceCreationException("Unknown type of service '$name', declare return type of factory method (for PHP 5 use annotation @return)");
475:         }
476:         return $class;
477:     }
478: 
479: 
480:     /** @return string|NULL */
481:     private function resolveEntityClass($entity, $recursive = [])
482:     {
483:         $entity = $this->normalizeEntity($entity instanceof Statement ? $entity->getEntity() : $entity);
484:         $serviceName = current(array_slice(array_keys($recursive), -1));
485: 
486:         if (is_array($entity)) {
487:             if (($service = $this->getServiceName($entity[0])) || $entity[0] instanceof Statement) {
488:                 $entity[0] = $this->resolveEntityClass($entity[0], $recursive);
489:                 if (!$entity[0]) {
490:                     return;
491:                 } elseif (isset($this->definitions[$service]) && $this->definitions[$service]->getImplement()) { // @Implement::create
492:                     return $entity[1] === 'create' ? $this->resolveServiceClass($service, $recursive) : NULL;
493:                 }
494:             }
495: 
496:             try {
497:                 $reflection = Nette\Utils\Callback::toReflection($entity[0] === '' ? $entity[1] : $entity);
498:                 $refClass = $reflection instanceof \ReflectionMethod ? $reflection->getDeclaringClass() : NULL;
499:             } catch (\ReflectionException $e) {
500:             }
501: 
502:             if (isset($e) || ($refClass && (!$reflection->isPublic()
503:                 || ($refClass->isTrait() && !$reflection->isStatic())
504:             ))) {
505:                 throw new ServiceCreationException(sprintf("Method %s() used in service '%s' is not callable.", Nette\Utils\Callback::toString($entity), $serviceName));
506:             }
507:             $this->addDependency($reflection);
508: 
509:             $type = PhpReflection::getReturnType($reflection);
510:             if ($type && !class_exists($type) && !interface_exists($type)) {
511:                 throw new ServiceCreationException(sprintf("Class or interface '%s' not found. Is return type of %s() used in service '%s' correct?", $type, Nette\Utils\Callback::toString($entity), $serviceName));
512:             }
513:             return $type;
514: 
515:         } elseif ($service = $this->getServiceName($entity)) { // alias or factory
516:             if (Strings::contains($service, '\\')) { // @\Class
517:                 return ltrim($service, '\\');
518:             }
519:             return $this->definitions[$service]->getImplement()
520:                 ?: $this->definitions[$service]->getClass()
521:                 ?: $this->resolveServiceClass($service, $recursive);
522: 
523:         } elseif (is_string($entity)) { // class
524:             if (!class_exists($entity)) {
525:                 throw new ServiceCreationException("Class $entity used in service '$serviceName' not found.");
526:             }
527:             return ltrim($entity, '\\');
528:         }
529:     }
530: 
531: 
532:     /**
533:      * @return void
534:      */
535:     public function complete()
536:     {
537:         $this->prepareClassList();
538: 
539:         foreach ($this->definitions as $name => $def) {
540:             if ($def->isDynamic()) {
541:                 continue;
542:             }
543: 
544:             $this->currentService = NULL;
545:             $entity = $def->getFactory()->getEntity();
546:             $serviceRef = $this->getServiceName($entity);
547:             $factory = $serviceRef && !$def->getFactory()->arguments && !$def->getSetup() && $def->getImplementMode() !== $def::IMPLEMENT_MODE_CREATE
548:                 ? new Statement(['@' . self::THIS_CONTAINER, 'getService'], [$serviceRef])
549:                 : $def->getFactory();
550: 
551:             try {
552:                 $def->setFactory($this->completeStatement($factory));
553:                 $this->classListNeedsRefresh = FALSE;
554: 
555:                 $this->currentService = $name;
556:                 $setups = $def->getSetup();
557:                 foreach ($setups as & $setup) {
558:                     if (is_string($setup->getEntity()) && strpbrk($setup->getEntity(), ':@?\\') === FALSE) { // auto-prepend @self
559:                         $setup = new Statement(['@' . $name, $setup->getEntity()], $setup->arguments);
560:                     }
561:                     $setup = $this->completeStatement($setup);
562:                 }
563:                 $def->setSetup($setups);
564: 
565:             } catch (\Exception $e) {
566:                 throw new ServiceCreationException("Service '$name': " . $e->getMessage(), 0, $e);
567: 
568:             } finally {
569:                 $this->currentService = NULL;
570:             }
571:         }
572:     }
573: 
574: 
575:     /**
576:      * @return Statement
577:      */
578:     public function completeStatement(Statement $statement)
579:     {
580:         $entity = $this->normalizeEntity($statement->getEntity());
581:         $arguments = $statement->arguments;
582: 
583:         if (is_string($entity) && Strings::contains($entity, '?')) { // PHP literal
584: 
585:         } elseif ($service = $this->getServiceName($entity)) { // factory calling
586:             $params = [];
587:             foreach ($this->definitions[$service]->parameters as $k => $v) {
588:                 $params[] = preg_replace('#\w+\z#', '\$$0', (is_int($k) ? $v : $k)) . (is_int($k) ? '' : ' = ' . PhpHelpers::dump($v));
589:             }
590:             $rm = new \ReflectionFunction(create_function(implode(', ', $params), ''));
591:             $arguments = Helpers::autowireArguments($rm, $arguments, $this);
592:             $entity = '@' . $service;
593: 
594:         } elseif ($entity === 'not') { // operator
595: 
596:         } elseif (is_string($entity)) { // class name
597:             if (!class_exists($entity)) {
598:                 throw new ServiceCreationException("Class $entity not found.");
599:             } elseif ((new ReflectionClass($entity))->isAbstract()) {
600:                 throw new ServiceCreationException("Class $entity is abstract.");
601:             } elseif (($rm = (new ReflectionClass($entity))->getConstructor()) !== NULL && !$rm->isPublic()) {
602:                 $visibility = $rm->isProtected() ? 'protected' : 'private';
603:                 throw new ServiceCreationException("Class $entity has $visibility constructor.");
604:             } elseif ($constructor = (new ReflectionClass($entity))->getConstructor()) {
605:                 $this->addDependency($constructor);
606:                 $arguments = Helpers::autowireArguments($constructor, $arguments, $this);
607:             } elseif ($arguments) {
608:                 throw new ServiceCreationException("Unable to pass arguments, class $entity has no constructor.");
609:             }
610: 
611:         } elseif (!Nette\Utils\Arrays::isList($entity) || count($entity) !== 2) {
612:             throw new ServiceCreationException(sprintf('Expected class, method or property, %s given.', PhpHelpers::dump($entity)));
613: 
614:         } elseif (!preg_match('#^\$?' . PhpHelpers::PHP_IDENT . '(\[\])?\z#', $entity[1])) {
615:             throw new ServiceCreationException("Expected function, method or property name, '$entity[1]' given.");
616: 
617:         } elseif ($entity[0] === '') { // globalFunc
618:             if (!Nette\Utils\Arrays::isList($arguments)) {
619:                 throw new ServiceCreationException("Unable to pass specified arguments to $entity[0].");
620:             } elseif (!function_exists($entity[1])) {
621:                 throw new ServiceCreationException("Function $entity[1] doesn't exist.");
622:             }
623: 
624:             $rf = new \ReflectionFunction($entity[1]);
625:             $this->addDependency($rf);
626:             $arguments = Helpers::autowireArguments($rf, $arguments, $this);
627: 
628:         } else {
629:             if ($entity[0] instanceof Statement) {
630:                 $entity[0] = $this->completeStatement($entity[0]);
631:             } elseif ($service = $this->getServiceName($entity[0])) { // service method
632:                 $entity[0] = '@' . $service;
633:             }
634: 
635:             if ($entity[1][0] === '$') { // property getter, setter or appender
636:                 Validators::assert($arguments, 'list:0..1', "setup arguments for '" . Nette\Utils\Callback::toString($entity) . "'");
637:                 if (!$arguments && substr($entity[1], -2) === '[]') {
638:                     throw new ServiceCreationException("Missing argument for $entity[1].");
639:                 }
640:             } elseif ($class = empty($service) || $entity[1] === 'create'
641:                 ? $this->resolveEntityClass($entity[0])
642:                 : $this->definitions[$service]->getClass()
643:             ) {
644:                 $arguments = $this->autowireArguments($class, $entity[1], $arguments);
645:             }
646:         }
647: 
648:         array_walk_recursive($arguments, function (& $val) {
649:             if ($val instanceof Statement) {
650:                 $val = $this->completeStatement($val);
651: 
652:             } elseif ($val === $this) {
653:                 trigger_error("Replace object ContainerBuilder in Statement arguments with '@container'.", E_USER_DEPRECATED);
654:                 $val = self::literal('$this');
655: 
656:             } elseif ($val instanceof ServiceDefinition) {
657:                 $val = '@' . current(array_keys($this->getDefinitions(), $val, TRUE));
658: 
659:             } elseif (is_string($val) && strlen($val) > 1 && $val[0] === '@' && $val[1] !== '@') {
660:                 $pair = explode('::', $val, 2);
661:                 $name = $this->getServiceName($pair[0]);
662:                 if (!isset($pair[1])) { // @service
663:                     $val = '@' . $name;
664:                 } elseif (preg_match('#^[A-Z][A-Z0-9_]*\z#', $pair[1], $m)) { // @service::CONSTANT
665:                     $val = self::literal($this->getDefinition($name)->getClass() . '::' . $pair[1]);
666:                 } else { // @service::property
667:                     $val = new Statement(['@' . $name, '$' . $pair[1]]);
668:                 }
669:             }
670:         });
671: 
672:         return new Statement($entity, $arguments);
673:     }
674: 
675: 
676:     private function checkCase($class)
677:     {
678:         if ((class_exists($class) || interface_exists($class)) && $class !== ($name = (new ReflectionClass($class))->getName())) {
679:             throw new ServiceCreationException("Case mismatch on class name '$class', correct name is '$name'.");
680:         }
681:     }
682: 
683: 
684:     /**
685:      * Adds item to the list of dependencies.
686:      * @param  ReflectionClass|\ReflectionFunctionAbstract|string
687:      * @return self
688:      * @internal
689:      */
690:     public function addDependency($dep)
691:     {
692:         $this->dependencies[] = $dep;
693:         return $this;
694:     }
695: 
696: 
697:     /**
698:      * Returns the list of dependencies.
699:      * @return array
700:      */
701:     public function getDependencies()
702:     {
703:         return $this->dependencies;
704:     }
705: 
706: 
707:     /**
708:      * Expands %placeholders% in strings.
709:      * @return mixed
710:      * @deprecated
711:      */
712:     public function expand($value)
713:     {
714:         return Helpers::expand($value, $this->parameters);
715:     }
716: 
717: 
718:     /**
719:      * @return Nette\PhpGenerator\PhpLiteral
720:      */
721:     public static function literal($phpCode)
722:     {
723:         return new Nette\PhpGenerator\PhpLiteral($phpCode);
724:     }
725: 
726: 
727:     /** @internal */
728:     public function normalizeEntity($entity)
729:     {
730:         if (is_string($entity) && Strings::contains($entity, '::') && !Strings::contains($entity, '?')) { // Class::method -> [Class, method]
731:             $entity = explode('::', $entity);
732:         }
733: 
734:         if (is_array($entity) && $entity[0] instanceof ServiceDefinition) { // [ServiceDefinition, ...] -> [@serviceName, ...]
735:             $entity[0] = '@' . current(array_keys($this->definitions, $entity[0], TRUE));
736: 
737:         } elseif ($entity instanceof ServiceDefinition) { // ServiceDefinition -> @serviceName
738:             $entity = '@' . current(array_keys($this->definitions, $entity, TRUE));
739: 
740:         } elseif (is_array($entity) && $entity[0] === $this) { // [$this, ...] -> [@container, ...]
741:             trigger_error("Replace object ContainerBuilder in Statement entity with '@container'.", E_USER_DEPRECATED);
742:             $entity[0] = '@' . self::THIS_CONTAINER;
743:         }
744:         return $entity; // Class, @service, [Class, member], [@service, member], [, globalFunc], Statement
745:     }
746: 
747: 
748:     /**
749:      * Converts @service or @\Class -> service name and checks its existence.
750:      * @return string  of FALSE, if argument is not service name
751:      * @internal
752:      */
753:     public function getServiceName($arg)
754:     {
755:         if (!is_string($arg) || !preg_match('#^@[\w\\\\.][^:]*\z#', $arg)) {
756:             return FALSE;
757:         }
758:         $service = substr($arg, 1);
759:         if ($service === self::THIS_SERVICE) {
760:             $service = $this->currentService;
761:         }
762:         if (Strings::contains($service, '\\')) {
763:             if ($this->classList === FALSE) { // may be disabled by prepareClassList
764:                 return $service;
765:             }
766:             $res = $this->getByType($service);
767:             if (!$res) {
768:                 throw new ServiceCreationException("Reference to missing service of type $service.");
769:             }
770:             return $res;
771:         }
772:         $service = isset($this->aliases[$service]) ? $this->aliases[$service] : $service;
773:         if (!isset($this->definitions[$service])) {
774:             throw new ServiceCreationException("Reference to missing service '$service'.");
775:         }
776:         return $service;
777:     }
778: 
779: 
780:     /**
781:      * Creates a list of arguments using autowiring.
782:      * @return array
783:      * @internal
784:      */
785:     public function autowireArguments($class, $method, array $arguments)
786:     {
787:         $rc = new ReflectionClass($class);
788:         if (!$rc->hasMethod($method)) {
789:             if (!Nette\Utils\Arrays::isList($arguments)) {
790:                 throw new ServiceCreationException("Unable to pass specified arguments to $class::$method().");
791:             }
792:             return $arguments;
793:         }
794: 
795:         $rm = $rc->getMethod($method);
796:         if (!$rm->isPublic()) {
797:             throw new ServiceCreationException("$class::$method() is not callable.");
798:         }
799:         $this->addDependency($rm);
800:         return Helpers::autowireArguments($rm, $arguments, $this);
801:     }
802: 
803: 
804:     /** @deprecated */
805:     public function generateClasses($className = 'Container', $parentName = NULL)
806:     {
807:         trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
808:         return (new PhpGenerator($this))->generate($className);
809:     }
810: 
811: 
812:     /** @deprecated */
813:     public function formatStatement(Statement $statement)
814:     {
815:         trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
816:         return (new PhpGenerator($this))->formatStatement($statement);
817:     }
818: 
819: 
820:     /** @deprecated */
821:     public function formatPhp($statement, $args)
822:     {
823:         array_walk_recursive($args, function (& $val) {
824:             if ($val instanceof Statement) {
825:                 $val = $this->completeStatement($val);
826: 
827:             } elseif ($val === $this) {
828:                 trigger_error("Replace object ContainerBuilder in Statement arguments with '@container'.", E_USER_DEPRECATED);
829:                 $val = self::literal('$this');
830: 
831:             } elseif ($val instanceof ServiceDefinition) {
832:                 $val = '@' . current(array_keys($this->getDefinitions(), $val, TRUE));
833:             }
834:         });
835:         return (new PhpGenerator($this))->formatPhp($statement, $args);
836:     }
837: 
838: }
839: 
Nette 2.4-20161109 API API documentation generated by ApiGen 2.8.0