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