use zend router
[GitHub/Stricted/Domain-Control-Panel.git] / vendor / Zend / ServiceManager / ServiceManager.php
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
4 *
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10 namespace Zend\ServiceManager;
11
12 class ServiceManager implements ServiceLocatorInterface
13 {
14 /**@#+
15 * Constants
16 */
17 const SCOPE_PARENT = 'parent';
18 const SCOPE_CHILD = 'child';
19 /**@#-*/
20
21 /**
22 * Lookup for canonicalized names.
23 *
24 * @var array
25 */
26 protected $canonicalNames = [];
27
28 /**
29 * @var bool
30 */
31 protected $allowOverride = false;
32
33 /**
34 * @var array
35 */
36 protected $invokableClasses = [];
37
38 /**
39 * @var string|callable|\Closure|FactoryInterface[]
40 */
41 protected $factories = [];
42
43 /**
44 * @var AbstractFactoryInterface[]
45 */
46 protected $abstractFactories = [];
47
48 /**
49 * @var array[]
50 */
51 protected $delegators = [];
52
53 /**
54 * @var array
55 */
56 protected $pendingAbstractFactoryRequests = [];
57
58 /**
59 * @var integer
60 */
61 protected $nestedContextCounter = -1;
62
63 /**
64 * @var array
65 */
66 protected $nestedContext = [];
67
68 /**
69 * @var array
70 */
71 protected $shared = [];
72
73 /**
74 * Registered services and cached values
75 *
76 * @var array
77 */
78 protected $instances = [];
79
80 /**
81 * @var array
82 */
83 protected $aliases = [];
84
85 /**
86 * @var array
87 */
88 protected $initializers = [];
89
90 /**
91 * @var ServiceManager[]
92 */
93 protected $peeringServiceManagers = [];
94
95 /**
96 * Whether or not to share by default
97 *
98 * @var bool
99 */
100 protected $shareByDefault = true;
101
102 /**
103 * @var bool
104 */
105 protected $retrieveFromPeeringManagerFirst = false;
106
107 /**
108 * @var bool Track whether not to throw exceptions during create()
109 */
110 protected $throwExceptionInCreate = true;
111
112 /**
113 * @var array map of characters to be replaced through strtr
114 */
115 protected $canonicalNamesReplacements = ['-' => '', '_' => '', ' ' => '', '\\' => '', '/' => ''];
116
117 /**
118 * @var ServiceLocatorInterface
119 */
120 protected $serviceManagerCaller;
121
122 /**
123 * Constructor
124 *
125 * @param ConfigInterface $config
126 */
127 public function __construct(ConfigInterface $config = null)
128 {
129 if ($config) {
130 $config->configureServiceManager($this);
131 }
132 }
133
134 /**
135 * Set allow override
136 *
137 * @param $allowOverride
138 * @return ServiceManager
139 */
140 public function setAllowOverride($allowOverride)
141 {
142 $this->allowOverride = (bool) $allowOverride;
143 return $this;
144 }
145
146 /**
147 * Get allow override
148 *
149 * @return bool
150 */
151 public function getAllowOverride()
152 {
153 return $this->allowOverride;
154 }
155
156 /**
157 * Set flag indicating whether services are shared by default
158 *
159 * @param bool $shareByDefault
160 * @return ServiceManager
161 * @throws Exception\RuntimeException if allowOverride is false
162 */
163 public function setShareByDefault($shareByDefault)
164 {
165 if ($this->allowOverride === false) {
166 throw new Exception\RuntimeException(sprintf(
167 '%s: cannot alter default shared service setting; container is marked immutable (allow_override is false)',
168 get_class($this) . '::' . __FUNCTION__
169 ));
170 }
171 $this->shareByDefault = (bool) $shareByDefault;
172 return $this;
173 }
174
175 /**
176 * Are services shared by default?
177 *
178 * @return bool
179 */
180 public function shareByDefault()
181 {
182 return $this->shareByDefault;
183 }
184
185 /**
186 * Set throw exceptions in create
187 *
188 * @param bool $throwExceptionInCreate
189 * @return ServiceManager
190 */
191 public function setThrowExceptionInCreate($throwExceptionInCreate)
192 {
193 $this->throwExceptionInCreate = $throwExceptionInCreate;
194 return $this;
195 }
196
197 /**
198 * Get throw exceptions in create
199 *
200 * @return bool
201 */
202 public function getThrowExceptionInCreate()
203 {
204 return $this->throwExceptionInCreate;
205 }
206
207 /**
208 * Set flag indicating whether to pull from peering manager before attempting creation
209 *
210 * @param bool $retrieveFromPeeringManagerFirst
211 * @return ServiceManager
212 */
213 public function setRetrieveFromPeeringManagerFirst($retrieveFromPeeringManagerFirst = true)
214 {
215 $this->retrieveFromPeeringManagerFirst = (bool) $retrieveFromPeeringManagerFirst;
216 return $this;
217 }
218
219 /**
220 * Should we retrieve from the peering manager prior to attempting to create a service?
221 *
222 * @return bool
223 */
224 public function retrieveFromPeeringManagerFirst()
225 {
226 return $this->retrieveFromPeeringManagerFirst;
227 }
228
229 /**
230 * Set invokable class
231 *
232 * @param string $name
233 * @param string $invokableClass
234 * @param bool $shared
235 * @return ServiceManager
236 * @throws Exception\InvalidServiceNameException
237 */
238 public function setInvokableClass($name, $invokableClass, $shared = null)
239 {
240 $cName = $this->canonicalizeName($name);
241
242 if ($this->has([$cName, $name], false)) {
243 if ($this->allowOverride === false) {
244 throw new Exception\InvalidServiceNameException(sprintf(
245 'A service by the name or alias "%s" already exists and cannot be overridden; please use an alternate name',
246 $name
247 ));
248 }
249 $this->unregisterService($cName);
250 }
251
252 if ($shared === null) {
253 $shared = $this->shareByDefault;
254 }
255
256 $this->invokableClasses[$cName] = $invokableClass;
257 $this->shared[$cName] = (bool) $shared;
258
259 return $this;
260 }
261
262 /**
263 * Set factory
264 *
265 * @param string $name
266 * @param string|FactoryInterface|callable $factory
267 * @param bool $shared
268 * @return ServiceManager
269 * @throws Exception\InvalidArgumentException
270 * @throws Exception\InvalidServiceNameException
271 */
272 public function setFactory($name, $factory, $shared = null)
273 {
274 $cName = $this->canonicalizeName($name);
275
276 if (!($factory instanceof FactoryInterface || is_string($factory) || is_callable($factory))) {
277 throw new Exception\InvalidArgumentException(sprintf(
278 'Provided factory must be the class name of a factory, callable or an instance of "%s".',
279 FactoryInterface::class
280 ));
281 }
282
283 if ($this->has([$cName, $name], false)) {
284 if ($this->allowOverride === false) {
285 throw new Exception\InvalidServiceNameException(sprintf(
286 'A service by the name or alias "%s" already exists and cannot be overridden, please use an alternate name',
287 $name
288 ));
289 }
290 $this->unregisterService($cName);
291 }
292
293 if ($shared === null) {
294 $shared = $this->shareByDefault;
295 }
296
297 $this->factories[$cName] = $factory;
298 $this->shared[$cName] = (bool) $shared;
299
300 return $this;
301 }
302
303 /**
304 * Add abstract factory
305 *
306 * @param AbstractFactoryInterface|string $factory
307 * @param bool $topOfStack
308 * @return ServiceManager
309 * @throws Exception\InvalidArgumentException if the abstract factory is invalid
310 */
311 public function addAbstractFactory($factory, $topOfStack = true)
312 {
313 if (!$factory instanceof AbstractFactoryInterface && is_string($factory)) {
314 $factory = new $factory();
315 }
316
317 if (!$factory instanceof AbstractFactoryInterface) {
318 throw new Exception\InvalidArgumentException(
319 'Provided abstract factory must be the class name of an abstract'
320 . ' factory or an instance of an AbstractFactoryInterface.'
321 );
322 }
323
324 if ($topOfStack) {
325 array_unshift($this->abstractFactories, $factory);
326 } else {
327 array_push($this->abstractFactories, $factory);
328 }
329 return $this;
330 }
331
332 /**
333 * Sets the given service name as to be handled by a delegator factory
334 *
335 * @param string $serviceName name of the service being the delegate
336 * @param string $delegatorFactoryName name of the service being the delegator factory
337 *
338 * @return ServiceManager
339 */
340 public function addDelegator($serviceName, $delegatorFactoryName)
341 {
342 $cName = $this->canonicalizeName($serviceName);
343
344 if (!isset($this->delegators[$cName])) {
345 $this->delegators[$cName] = [];
346 }
347
348 $this->delegators[$cName][] = $delegatorFactoryName;
349
350 return $this;
351 }
352
353 /**
354 * Add initializer
355 *
356 * @param callable|InitializerInterface $initializer
357 * @param bool $topOfStack
358 * @return ServiceManager
359 * @throws Exception\InvalidArgumentException
360 */
361 public function addInitializer($initializer, $topOfStack = true)
362 {
363 if (!($initializer instanceof InitializerInterface || is_callable($initializer))) {
364 if (is_string($initializer)) {
365 $initializer = new $initializer;
366 }
367
368 if (!($initializer instanceof InitializerInterface || is_callable($initializer))) {
369 throw new Exception\InvalidArgumentException('$initializer should be callable.');
370 }
371 }
372
373 if ($topOfStack) {
374 array_unshift($this->initializers, $initializer);
375 } else {
376 array_push($this->initializers, $initializer);
377 }
378 return $this;
379 }
380
381 /**
382 * Register a service with the locator
383 *
384 * @param string $name
385 * @param mixed $service
386 * @return ServiceManager
387 * @throws Exception\InvalidServiceNameException
388 */
389 public function setService($name, $service)
390 {
391 $cName = $this->canonicalizeName($name);
392
393 if ($this->has($cName, false)) {
394 if ($this->allowOverride === false) {
395 throw new Exception\InvalidServiceNameException(sprintf(
396 '%s: A service by the name "%s" or alias already exists and cannot be overridden, please use an alternate name.',
397 get_class($this) . '::' . __FUNCTION__,
398 $name
399 ));
400 }
401 $this->unregisterService($cName);
402 }
403
404 $this->instances[$cName] = $service;
405
406 return $this;
407 }
408
409 /**
410 * @param string $name
411 * @param bool $isShared
412 * @return ServiceManager
413 * @throws Exception\ServiceNotFoundException
414 */
415 public function setShared($name, $isShared)
416 {
417 $cName = $this->canonicalizeName($name);
418
419 if (!isset($this->invokableClasses[$cName])
420 && !isset($this->factories[$cName])
421 && !$this->canCreateFromAbstractFactory($cName, $name)
422 ) {
423 throw new Exception\ServiceNotFoundException(sprintf(
424 '%s: A service by the name "%s" was not found and could not be marked as shared',
425 get_class($this) . '::' . __FUNCTION__,
426 $name
427 ));
428 }
429
430 $this->shared[$cName] = (bool) $isShared;
431 return $this;
432 }
433
434 /**
435 * @param string $name
436 * @return bool
437 * @throws Exception\ServiceNotFoundException
438 */
439 public function isShared($name)
440 {
441 $cName = $this->canonicalizeName($name);
442
443 if (!$this->has($name)) {
444 throw new Exception\ServiceNotFoundException(sprintf(
445 '%s: A service by the name "%s" was not found',
446 get_class($this) . '::' . __FUNCTION__,
447 $name
448 ));
449 }
450
451 if (!isset($this->shared[$cName])) {
452 return $this->shareByDefault();
453 }
454
455 return $this->shared[$cName];
456 }
457
458 /**
459 * Resolve the alias for the given canonical name
460 *
461 * @param string $cName The canonical name to resolve
462 * @return string The resolved canonical name
463 */
464 protected function resolveAlias($cName)
465 {
466 $stack = [];
467
468 while ($this->hasAlias($cName)) {
469 if (isset($stack[$cName])) {
470 throw new Exception\CircularReferenceException(sprintf(
471 'Circular alias reference: %s -> %s',
472 implode(' -> ', $stack),
473 $cName
474 ));
475 }
476
477 $stack[$cName] = $cName;
478 $cName = $this->aliases[$this->canonicalizeName($cName)];
479 }
480
481 return $cName;
482 }
483
484 /**
485 * Retrieve a registered instance
486 *
487 * @param string $name
488 * @param bool $usePeeringServiceManagers
489 * @throws Exception\ServiceNotFoundException
490 * @return object|array
491 */
492 public function get($name, $usePeeringServiceManagers = true)
493 {
494 // inlined code from ServiceManager::canonicalizeName for performance
495 if (isset($this->canonicalNames[$name])) {
496 $cName = $this->canonicalNames[$name];
497 } else {
498 $cName = $this->canonicalizeName($name);
499 }
500
501 $isAlias = false;
502
503 if ($this->hasAlias($cName)) {
504 $isAlias = true;
505 $name = $this->resolveAlias($cName);
506 $cName = $this->canonicalizeName($name);
507 }
508
509 $instance = null;
510
511 if ($usePeeringServiceManagers && $this->retrieveFromPeeringManagerFirst) {
512 $instance = $this->retrieveFromPeeringManager($name);
513
514 if (null !== $instance) {
515 return $instance;
516 }
517 }
518
519 if (isset($this->instances[$cName])) {
520 return $this->instances[$cName];
521 }
522
523 if (!$instance) {
524 $this->checkNestedContextStart($cName);
525 if (isset($this->invokableClasses[$cName])
526 || isset($this->factories[$cName])
527 || isset($this->aliases[$cName])
528 || $this->canCreateFromAbstractFactory($cName, $name)
529 ) {
530 $instance = $this->create([$cName, $name]);
531 } elseif ($isAlias && $this->canCreateFromAbstractFactory($name, $cName)) {
532 /*
533 * case of an alias leading to an abstract factory :
534 * 'my-alias' => 'my-abstract-defined-service'
535 * $name = 'my-alias'
536 * $cName = 'my-abstract-defined-service'
537 */
538 $instance = $this->create([$name, $cName]);
539 } elseif ($usePeeringServiceManagers && !$this->retrieveFromPeeringManagerFirst) {
540 $instance = $this->retrieveFromPeeringManager($name);
541 }
542 $this->checkNestedContextStop();
543 }
544
545 // Still no instance? raise an exception
546 if ($instance === null) {
547 $this->checkNestedContextStop(true);
548 if ($isAlias) {
549 throw new Exception\ServiceNotFoundException(sprintf(
550 'An alias "%s" was requested but no service could be found.',
551 $name
552 ));
553 }
554
555 throw new Exception\ServiceNotFoundException(sprintf(
556 '%s was unable to fetch or create an instance for %s',
557 get_class($this) . '::' . __FUNCTION__,
558 $name
559 ));
560 }
561
562 if (($this->shareByDefault && !isset($this->shared[$cName]))
563 || (isset($this->shared[$cName]) && $this->shared[$cName] === true)
564 ) {
565 $this->instances[$cName] = $instance;
566 }
567
568 return $instance;
569 }
570
571 /**
572 * Create an instance of the requested service
573 *
574 * @param string|array $name
575 *
576 * @return bool|object
577 */
578 public function create($name)
579 {
580 if (is_array($name)) {
581 list($cName, $rName) = $name;
582 } else {
583 $rName = $name;
584
585 // inlined code from ServiceManager::canonicalizeName for performance
586 if (isset($this->canonicalNames[$rName])) {
587 $cName = $this->canonicalNames[$name];
588 } else {
589 $cName = $this->canonicalizeName($name);
590 }
591 }
592
593 if (isset($this->delegators[$cName])) {
594 return $this->createDelegatorFromFactory($cName, $rName);
595 }
596
597 return $this->doCreate($rName, $cName);
598 }
599
600 /**
601 * Creates a callback that uses a delegator to create a service
602 *
603 * @param DelegatorFactoryInterface|callable $delegatorFactory the delegator factory
604 * @param string $rName requested service name
605 * @param string $cName canonical service name
606 * @param callable $creationCallback callback for instantiating the real service
607 *
608 * @return callable
609 */
610 private function createDelegatorCallback($delegatorFactory, $rName, $cName, $creationCallback)
611 {
612 return function () use ($delegatorFactory, $rName, $cName, $creationCallback) {
613 return $delegatorFactory instanceof DelegatorFactoryInterface
614 ? $delegatorFactory->createDelegatorWithName($this, $cName, $rName, $creationCallback)
615 : $delegatorFactory($this, $cName, $rName, $creationCallback);
616 };
617 }
618
619 /**
620 * Actually creates the service
621 *
622 * @param string $rName real service name
623 * @param string $cName canonicalized service name
624 *
625 * @return bool|mixed|null|object
626 * @throws Exception\ServiceNotFoundException
627 *
628 */
629 protected function doCreate($rName, $cName)
630 {
631 $instance = null;
632
633 if (isset($this->factories[$cName])) {
634 $instance = $this->createFromFactory($cName, $rName);
635 }
636
637 if ($instance === null && isset($this->invokableClasses[$cName])) {
638 $instance = $this->createFromInvokable($cName, $rName);
639 }
640 $this->checkNestedContextStart($cName);
641 if ($instance === null && $this->canCreateFromAbstractFactory($cName, $rName)) {
642 $instance = $this->createFromAbstractFactory($cName, $rName);
643 }
644 $this->checkNestedContextStop();
645
646 if ($instance === null && $this->throwExceptionInCreate) {
647 $this->checkNestedContextStop(true);
648 throw new Exception\ServiceNotFoundException(sprintf(
649 'No valid instance was found for %s%s',
650 $cName,
651 ($rName ? '(alias: ' . $rName . ')' : '')
652 ));
653 }
654
655 // Do not call initializers if we do not have an instance
656 if ($instance === null) {
657 return $instance;
658 }
659
660 foreach ($this->initializers as $initializer) {
661 if ($initializer instanceof InitializerInterface) {
662 $initializer->initialize($instance, $this);
663 } else {
664 call_user_func($initializer, $instance, $this);
665 }
666 }
667
668 return $instance;
669 }
670
671 /**
672 * Determine if we can create an instance.
673 * Proxies to has()
674 *
675 * @param string|array $name
676 * @param bool $checkAbstractFactories
677 * @return bool
678 * @deprecated this method is being deprecated as of zendframework 2.3, and may be removed in future major versions
679 */
680 public function canCreate($name, $checkAbstractFactories = true)
681 {
682 trigger_error(sprintf('%s is deprecated; please use %s::has', __METHOD__, __CLASS__), E_USER_DEPRECATED);
683 return $this->has($name, $checkAbstractFactories, false);
684 }
685
686 /**
687 * Determine if an instance exists.
688 *
689 * @param string|array $name An array argument accepts exactly two values.
690 * Example: array('canonicalName', 'requestName')
691 * @param bool $checkAbstractFactories
692 * @param bool $usePeeringServiceManagers
693 * @return bool
694 */
695 public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true)
696 {
697 if (is_string($name)) {
698 $rName = $name;
699
700 // inlined code from ServiceManager::canonicalizeName for performance
701 if (isset($this->canonicalNames[$rName])) {
702 $cName = $this->canonicalNames[$rName];
703 } else {
704 $cName = $this->canonicalizeName($name);
705 }
706 } elseif (is_array($name) && count($name) >= 2) {
707 list($cName, $rName) = $name;
708 } else {
709 return false;
710 }
711
712 if (isset($this->invokableClasses[$cName])
713 || isset($this->factories[$cName])
714 || isset($this->aliases[$cName])
715 || isset($this->instances[$cName])
716 || ($checkAbstractFactories && $this->canCreateFromAbstractFactory($cName, $rName))
717 ) {
718 return true;
719 }
720
721 if ($usePeeringServiceManagers) {
722 $caller = $this->serviceManagerCaller;
723 foreach ($this->peeringServiceManagers as $peeringServiceManager) {
724 // ignore peering service manager if they are the same instance
725 if ($caller === $peeringServiceManager) {
726 continue;
727 }
728
729 $peeringServiceManager->serviceManagerCaller = $this;
730
731 if ($peeringServiceManager->has($name)) {
732 $peeringServiceManager->serviceManagerCaller = null;
733 return true;
734 }
735
736 $peeringServiceManager->serviceManagerCaller = null;
737 }
738 }
739
740 return false;
741 }
742
743 /**
744 * Determine if we can create an instance from an abstract factory.
745 *
746 * @param string $cName
747 * @param string $rName
748 * @return bool
749 */
750 public function canCreateFromAbstractFactory($cName, $rName)
751 {
752 if (array_key_exists($cName, $this->nestedContext)) {
753 $context = $this->nestedContext[$cName];
754 if ($context === false) {
755 return false;
756 } elseif (is_object($context)) {
757 return !isset($this->pendingAbstractFactoryRequests[get_class($context).$cName]);
758 }
759 }
760 $this->checkNestedContextStart($cName);
761 // check abstract factories
762 $result = false;
763 $this->nestedContext[$cName] = false;
764 foreach ($this->abstractFactories as $abstractFactory) {
765 $pendingKey = get_class($abstractFactory).$cName;
766 if (isset($this->pendingAbstractFactoryRequests[$pendingKey])) {
767 $result = false;
768 break;
769 }
770
771 if ($abstractFactory->canCreateServiceWithName($this, $cName, $rName)) {
772 $this->nestedContext[$cName] = $abstractFactory;
773 $result = true;
774 break;
775 }
776 }
777 $this->checkNestedContextStop();
778 return $result;
779 }
780
781 /**
782 * Ensure the alias definition will not result in a circular reference
783 *
784 * @param string $alias
785 * @param string $nameOrAlias
786 * @throws Exception\CircularReferenceException
787 * @return self
788 */
789 protected function checkForCircularAliasReference($alias, $nameOrAlias)
790 {
791 $aliases = $this->aliases;
792 $aliases[$alias] = $this->canonicalizeName($nameOrAlias);
793 $stack = [];
794
795 while (isset($aliases[$alias])) {
796 if (isset($stack[$alias])) {
797 throw new Exception\CircularReferenceException(sprintf(
798 'The alias definition "%s" : "%s" results in a circular reference: "%s" -> "%s"',
799 $alias,
800 $nameOrAlias,
801 implode('" -> "', $stack),
802 $alias
803 ));
804 }
805
806 $stack[$alias] = $alias;
807 $alias = $this->canonicalizeName($aliases[$alias]);
808 }
809
810 return $this;
811 }
812
813 /**
814 * @param string $alias
815 * @param string $nameOrAlias
816 * @return ServiceManager
817 * @throws Exception\ServiceNotFoundException
818 * @throws Exception\InvalidServiceNameException
819 */
820 public function setAlias($alias, $nameOrAlias)
821 {
822 if (!is_string($alias) || !is_string($nameOrAlias)) {
823 throw new Exception\InvalidServiceNameException('Service or alias names must be strings.');
824 }
825
826 $cAlias = $this->canonicalizeName($alias);
827
828 if ($alias == '' || $nameOrAlias == '') {
829 throw new Exception\InvalidServiceNameException('Invalid service name alias');
830 }
831
832 if ($this->allowOverride === false && $this->has([$cAlias, $alias], false)) {
833 throw new Exception\InvalidServiceNameException(sprintf(
834 'An alias by the name "%s" or "%s" already exists',
835 $cAlias,
836 $alias
837 ));
838 }
839
840 if ($this->hasAlias($alias)) {
841 $this->checkForCircularAliasReference($cAlias, $nameOrAlias);
842 }
843
844 $this->aliases[$cAlias] = $nameOrAlias;
845 return $this;
846 }
847
848 /**
849 * Determine if we have an alias
850 *
851 * @param string $alias
852 * @return bool
853 */
854 public function hasAlias($alias)
855 {
856 return isset($this->aliases[$this->canonicalizeName($alias)]);
857 }
858
859 /**
860 * Create scoped service manager
861 *
862 * @param string $peering
863 * @return ServiceManager
864 */
865 public function createScopedServiceManager($peering = self::SCOPE_PARENT)
866 {
867 $scopedServiceManager = new ServiceManager();
868 if ($peering == self::SCOPE_PARENT) {
869 $scopedServiceManager->peeringServiceManagers[] = $this;
870 }
871 if ($peering == self::SCOPE_CHILD) {
872 $this->peeringServiceManagers[] = $scopedServiceManager;
873 }
874 return $scopedServiceManager;
875 }
876
877 /**
878 * Add a peering relationship
879 *
880 * @param ServiceManager $manager
881 * @param string $peering
882 * @return ServiceManager
883 */
884 public function addPeeringServiceManager(ServiceManager $manager, $peering = self::SCOPE_PARENT)
885 {
886 if ($peering == self::SCOPE_PARENT) {
887 $this->peeringServiceManagers[] = $manager;
888 }
889 if ($peering == self::SCOPE_CHILD) {
890 $manager->peeringServiceManagers[] = $this;
891 }
892 return $this;
893 }
894
895 /**
896 * Canonicalize name
897 *
898 * @param string $name
899 * @return string
900 */
901 protected function canonicalizeName($name)
902 {
903 if (isset($this->canonicalNames[$name])) {
904 return $this->canonicalNames[$name];
905 }
906
907 // this is just for performance instead of using str_replace
908 return $this->canonicalNames[$name] = strtolower(strtr($name, $this->canonicalNamesReplacements));
909 }
910
911 /**
912 * Create service via callback
913 *
914 * @param callable $callable
915 * @param string $cName
916 * @param string $rName
917 * @throws Exception\ServiceNotCreatedException
918 * @throws Exception\ServiceNotFoundException
919 * @throws Exception\CircularDependencyFoundException
920 * @return object
921 */
922 protected function createServiceViaCallback($callable, $cName, $rName)
923 {
924 static $circularDependencyResolver = [];
925 $depKey = spl_object_hash($this) . '-' . $cName;
926
927 if (isset($circularDependencyResolver[$depKey])) {
928 $circularDependencyResolver = [];
929 throw new Exception\CircularDependencyFoundException('Circular dependency for LazyServiceLoader was found for instance ' . $rName);
930 }
931
932 try {
933 $circularDependencyResolver[$depKey] = true;
934 $instance = call_user_func($callable, $this, $cName, $rName);
935 unset($circularDependencyResolver[$depKey]);
936 } catch (Exception\ServiceNotFoundException $e) {
937 unset($circularDependencyResolver[$depKey]);
938 throw $e;
939 } catch (\Exception $e) {
940 unset($circularDependencyResolver[$depKey]);
941 throw new Exception\ServiceNotCreatedException(
942 sprintf('An exception was raised while creating "%s"; no instance returned', $rName),
943 $e->getCode(),
944 $e
945 );
946 }
947 if ($instance === null) {
948 throw new Exception\ServiceNotCreatedException('The factory was called but did not return an instance.');
949 }
950
951 return $instance;
952 }
953
954 /**
955 * Retrieve a keyed list of all registered services. Handy for debugging!
956 *
957 * @return array
958 */
959 public function getRegisteredServices()
960 {
961 return [
962 'invokableClasses' => array_keys($this->invokableClasses),
963 'factories' => array_keys($this->factories),
964 'aliases' => array_keys($this->aliases),
965 'instances' => array_keys($this->instances),
966 ];
967 }
968
969 /**
970 * Retrieve a keyed list of all canonical names. Handy for debugging!
971 *
972 * @return array
973 */
974 public function getCanonicalNames()
975 {
976 return $this->canonicalNames;
977 }
978
979 /**
980 * Allows to override the canonical names lookup map with predefined
981 * values.
982 *
983 * @param array $canonicalNames
984 * @return ServiceManager
985 */
986 public function setCanonicalNames($canonicalNames)
987 {
988 $this->canonicalNames = $canonicalNames;
989
990 return $this;
991 }
992
993 /**
994 * Attempt to retrieve an instance via a peering manager
995 *
996 * @param string $name
997 * @return mixed
998 */
999 protected function retrieveFromPeeringManager($name)
1000 {
1001 if (null !== ($service = $this->loopPeeringServiceManagers($name))) {
1002 return $service;
1003 }
1004
1005 $name = $this->canonicalizeName($name);
1006
1007 if ($this->hasAlias($name)) {
1008 do {
1009 $name = $this->aliases[$name];
1010 } while ($this->hasAlias($name));
1011 }
1012
1013 if (null !== ($service = $this->loopPeeringServiceManagers($name))) {
1014 return $service;
1015 }
1016
1017 return;
1018 }
1019
1020 /**
1021 * Loop over peering service managers.
1022 *
1023 * @param string $name
1024 * @return mixed
1025 */
1026 protected function loopPeeringServiceManagers($name)
1027 {
1028 $caller = $this->serviceManagerCaller;
1029
1030 foreach ($this->peeringServiceManagers as $peeringServiceManager) {
1031 // ignore peering service manager if they are the same instance
1032 if ($caller === $peeringServiceManager) {
1033 continue;
1034 }
1035
1036 // pass this instance to peering service manager
1037 $peeringServiceManager->serviceManagerCaller = $this;
1038
1039 if ($peeringServiceManager->has($name)) {
1040 $this->shared[$name] = $peeringServiceManager->isShared($name);
1041 $instance = $peeringServiceManager->get($name);
1042 $peeringServiceManager->serviceManagerCaller = null;
1043 return $instance;
1044 }
1045
1046 $peeringServiceManager->serviceManagerCaller = null;
1047 }
1048
1049 return;
1050 }
1051
1052 /**
1053 * Attempt to create an instance via an invokable class
1054 *
1055 * @param string $canonicalName
1056 * @param string $requestedName
1057 * @return null|\stdClass
1058 * @throws Exception\ServiceNotFoundException If resolved class does not exist
1059 */
1060 protected function createFromInvokable($canonicalName, $requestedName)
1061 {
1062 $invokable = $this->invokableClasses[$canonicalName];
1063 if (!class_exists($invokable)) {
1064 throw new Exception\ServiceNotFoundException(sprintf(
1065 '%s: failed retrieving "%s%s" via invokable class "%s"; class does not exist',
1066 get_class($this) . '::' . __FUNCTION__,
1067 $canonicalName,
1068 ($requestedName ? '(alias: ' . $requestedName . ')' : ''),
1069 $invokable
1070 ));
1071 }
1072 $instance = new $invokable;
1073 return $instance;
1074 }
1075
1076 /**
1077 * Attempt to create an instance via a factory
1078 *
1079 * @param string $canonicalName
1080 * @param string $requestedName
1081 * @return mixed
1082 * @throws Exception\ServiceNotCreatedException If factory is not callable
1083 */
1084 protected function createFromFactory($canonicalName, $requestedName)
1085 {
1086 $factory = $this->factories[$canonicalName];
1087 if (is_string($factory) && class_exists($factory, true)) {
1088 $factory = new $factory;
1089 $this->factories[$canonicalName] = $factory;
1090 }
1091 if ($factory instanceof FactoryInterface) {
1092 $instance = $this->createServiceViaCallback([$factory, 'createService'], $canonicalName, $requestedName);
1093 } elseif (is_callable($factory)) {
1094 $instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName);
1095 } else {
1096 throw new Exception\ServiceNotCreatedException(sprintf(
1097 'While attempting to create %s%s an invalid factory was registered for this instance type.',
1098 $canonicalName,
1099 ($requestedName ? '(alias: ' . $requestedName . ')' : '')
1100 ));
1101 }
1102 return $instance;
1103 }
1104
1105 /**
1106 * Attempt to create an instance via an abstract factory
1107 *
1108 * @param string $canonicalName
1109 * @param string $requestedName
1110 * @return object|null
1111 * @throws Exception\ServiceNotCreatedException If abstract factory is not callable
1112 */
1113 protected function createFromAbstractFactory($canonicalName, $requestedName)
1114 {
1115 if (isset($this->nestedContext[$canonicalName])) {
1116 $abstractFactory = $this->nestedContext[$canonicalName];
1117 $pendingKey = get_class($abstractFactory).$canonicalName;
1118 try {
1119 $this->pendingAbstractFactoryRequests[$pendingKey] = true;
1120 $instance = $this->createServiceViaCallback(
1121 [$abstractFactory, 'createServiceWithName'],
1122 $canonicalName,
1123 $requestedName
1124 );
1125 unset($this->pendingAbstractFactoryRequests[$pendingKey]);
1126 return $instance;
1127 } catch (\Exception $e) {
1128 unset($this->pendingAbstractFactoryRequests[$pendingKey]);
1129 $this->checkNestedContextStop(true);
1130 throw new Exception\ServiceNotCreatedException(
1131 sprintf(
1132 'An abstract factory could not create an instance of %s%s.',
1133 $canonicalName,
1134 ($requestedName ? '(alias: ' . $requestedName . ')' : '')
1135 ),
1136 $e->getCode(),
1137 $e
1138 );
1139 }
1140 }
1141 return;
1142 }
1143
1144 /**
1145 *
1146 * @param string $cName
1147 * @return self
1148 */
1149 protected function checkNestedContextStart($cName)
1150 {
1151 if ($this->nestedContextCounter === -1 || !isset($this->nestedContext[$cName])) {
1152 $this->nestedContext[$cName] = null;
1153 }
1154 $this->nestedContextCounter++;
1155 return $this;
1156 }
1157
1158 /**
1159 *
1160 * @param bool $force
1161 * @return self
1162 */
1163 protected function checkNestedContextStop($force = false)
1164 {
1165 if ($force) {
1166 $this->nestedContextCounter = -1;
1167 $this->nestedContext = [];
1168 return $this;
1169 }
1170
1171 $this->nestedContextCounter--;
1172 if ($this->nestedContextCounter === -1) {
1173 $this->nestedContext = [];
1174 }
1175 return $this;
1176 }
1177
1178 /**
1179 * @param $canonicalName
1180 * @param $requestedName
1181 * @return mixed
1182 * @throws Exception\ServiceNotCreatedException
1183 */
1184 protected function createDelegatorFromFactory($canonicalName, $requestedName)
1185 {
1186 $delegatorsCount = count($this->delegators[$canonicalName]);
1187 $creationCallback = function () use ($requestedName, $canonicalName) {
1188 return $this->doCreate($requestedName, $canonicalName);
1189 };
1190
1191 for ($i = 0; $i < $delegatorsCount; $i += 1) {
1192 $delegatorFactory = $this->delegators[$canonicalName][$i];
1193
1194 if (is_string($delegatorFactory)) {
1195 $delegatorFactory = !$this->has($delegatorFactory) && class_exists($delegatorFactory, true) ?
1196 new $delegatorFactory
1197 : $this->get($delegatorFactory);
1198 $this->delegators[$canonicalName][$i] = $delegatorFactory;
1199 }
1200
1201 if (!$delegatorFactory instanceof DelegatorFactoryInterface && !is_callable($delegatorFactory)) {
1202 throw new Exception\ServiceNotCreatedException(sprintf(
1203 'While attempting to create %s%s an invalid factory was registered for this instance type.',
1204 $canonicalName,
1205 ($requestedName ? '(alias: ' . $requestedName . ')' : '')
1206 ));
1207 }
1208
1209 $creationCallback = $this->createDelegatorCallback(
1210 $delegatorFactory,
1211 $requestedName,
1212 $canonicalName,
1213 $creationCallback
1214 );
1215 }
1216
1217 return $creationCallback($this, $canonicalName, $requestedName, $creationCallback);
1218 }
1219
1220 /**
1221 * Checks if the object has this class as one of its parents
1222 *
1223 * @see https://bugs.php.net/bug.php?id=53727
1224 * @see https://github.com/zendframework/zf2/pull/1807
1225 *
1226 * @deprecated since zf 2.3 requires PHP >= 5.3.23
1227 *
1228 * @param string $className
1229 * @param string $type
1230 * @return bool
1231 *
1232 * @deprecated this method is being deprecated as of zendframework 2.2, and may be removed in future major versions
1233 */
1234 protected static function isSubclassOf($className, $type)
1235 {
1236 return is_subclass_of($className, $type);
1237 }
1238
1239 /**
1240 * Unregister a service
1241 *
1242 * Called when $allowOverride is true and we detect that a service being
1243 * added to the instance already exists. This will remove the duplicate
1244 * entry, and also any shared flags previously registered.
1245 *
1246 * @param string $canonical
1247 * @return void
1248 */
1249 protected function unregisterService($canonical)
1250 {
1251 $types = ['invokableClasses', 'factories', 'aliases'];
1252 foreach ($types as $type) {
1253 if (isset($this->{$type}[$canonical])) {
1254 unset($this->{$type}[$canonical]);
1255 break;
1256 }
1257 }
1258
1259 if (isset($this->instances[$canonical])) {
1260 unset($this->instances[$canonical]);
1261 }
1262
1263 if (isset($this->shared[$canonical])) {
1264 unset($this->shared[$canonical]);
1265 }
1266 }
1267 }