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 10namespace Zend\ServiceManager; 11 12class 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 = array(); 27 28 /** 29 * @var bool 30 */ 31 protected $allowOverride = false; 32 33 /** 34 * @var array 35 */ 36 protected $invokableClasses = array(); 37 38 /** 39 * @var string|callable|\Closure|FactoryInterface[] 40 */ 41 protected $factories = array(); 42 43 /** 44 * @var AbstractFactoryInterface[] 45 */ 46 protected $abstractFactories = array(); 47 48 /** 49 * @var array[] 50 */ 51 protected $delegators = array(); 52 53 /** 54 * @var array 55 */ 56 protected $pendingAbstractFactoryRequests = array(); 57 58 /** 59 * @var integer 60 */ 61 protected $nestedContextCounter = -1; 62 63 /** 64 * @var array 65 */ 66 protected $nestedContext = array(); 67 68 /** 69 * @var array 70 */ 71 protected $shared = array(); 72 73 /** 74 * Registered services and cached values 75 * 76 * @var array 77 */ 78 protected $instances = array(); 79 80 /** 81 * @var array 82 */ 83 protected $aliases = array(); 84 85 /** 86 * @var array 87 */ 88 protected $initializers = array(); 89 90 /** 91 * @var ServiceManager[] 92 */ 93 protected $peeringServiceManagers = array(); 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 = array('-' => '', '_' => '', ' ' => '', '\\' => '', '/' => ''); 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(array($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( 278 'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.' 279 ); 280 } 281 282 if ($this->has(array($cName, $name), false)) { 283 if ($this->allowOverride === false) { 284 throw new Exception\InvalidServiceNameException(sprintf( 285 'A service by the name or alias "%s" already exists and cannot be overridden, please use an alternate name', 286 $name 287 )); 288 } 289 $this->unregisterService($cName); 290 } 291 292 if ($shared === null) { 293 $shared = $this->shareByDefault; 294 } 295 296 $this->factories[$cName] = $factory; 297 $this->shared[$cName] = (bool) $shared; 298 299 return $this; 300 } 301 302 /** 303 * Add abstract factory 304 * 305 * @param AbstractFactoryInterface|string $factory 306 * @param bool $topOfStack 307 * @return ServiceManager 308 * @throws Exception\InvalidArgumentException if the abstract factory is invalid 309 */ 310 public function addAbstractFactory($factory, $topOfStack = true) 311 { 312 if (!$factory instanceof AbstractFactoryInterface && is_string($factory)) { 313 $factory = new $factory(); 314 } 315 316 if (!$factory instanceof AbstractFactoryInterface) { 317 throw new Exception\InvalidArgumentException( 318 'Provided abstract factory must be the class name of an abstract' 319 . ' factory or an instance of an AbstractFactoryInterface.' 320 ); 321 } 322 323 if ($topOfStack) { 324 array_unshift($this->abstractFactories, $factory); 325 } else { 326 array_push($this->abstractFactories, $factory); 327 } 328 return $this; 329 } 330 331 /** 332 * Sets the given service name as to be handled by a delegator factory 333 * 334 * @param string $serviceName name of the service being the delegate 335 * @param string $delegatorFactoryName name of the service being the delegator factory 336 * 337 * @return ServiceManager 338 */ 339 public function addDelegator($serviceName, $delegatorFactoryName) 340 { 341 $cName = $this->canonicalizeName($serviceName); 342 343 if (!isset($this->delegators[$cName])) { 344 $this->delegators[$cName] = array(); 345 } 346 347 $this->delegators[$cName][] = $delegatorFactoryName; 348 349 return $this; 350 } 351 352 /** 353 * Add initializer 354 * 355 * @param callable|InitializerInterface $initializer 356 * @param bool $topOfStack 357 * @return ServiceManager 358 * @throws Exception\InvalidArgumentException 359 */ 360 public function addInitializer($initializer, $topOfStack = true) 361 { 362 if (!($initializer instanceof InitializerInterface || is_callable($initializer))) { 363 if (is_string($initializer)) { 364 $initializer = new $initializer; 365 } 366 367 if (!($initializer instanceof InitializerInterface || is_callable($initializer))) { 368 throw new Exception\InvalidArgumentException('$initializer should be callable.'); 369 } 370 } 371 372 if ($topOfStack) { 373 array_unshift($this->initializers, $initializer); 374 } else { 375 array_push($this->initializers, $initializer); 376 } 377 return $this; 378 } 379 380 /** 381 * Register a service with the locator 382 * 383 * @param string $name 384 * @param mixed $service 385 * @return ServiceManager 386 * @throws Exception\InvalidServiceNameException 387 */ 388 public function setService($name, $service) 389 { 390 $cName = $this->canonicalizeName($name); 391 392 if ($this->has($cName, false)) { 393 if ($this->allowOverride === false) { 394 throw new Exception\InvalidServiceNameException(sprintf( 395 '%s: A service by the name "%s" or alias already exists and cannot be overridden, please use an alternate name.', 396 get_class($this) . '::' . __FUNCTION__, 397 $name 398 )); 399 } 400 $this->unregisterService($cName); 401 } 402 403 $this->instances[$cName] = $service; 404 405 return $this; 406 } 407 408 /** 409 * @param string $name 410 * @param bool $isShared 411 * @return ServiceManager 412 * @throws Exception\ServiceNotFoundException 413 */ 414 public function setShared($name, $isShared) 415 { 416 $cName = $this->canonicalizeName($name); 417 418 if ( 419 !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 = array(); 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[$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 $cName = $this->resolveAlias($cName); 506 } 507 508 $instance = null; 509 510 if ($usePeeringServiceManagers && $this->retrieveFromPeeringManagerFirst) { 511 $instance = $this->retrieveFromPeeringManager($name); 512 513 if (null !== $instance) { 514 return $instance; 515 } 516 } 517 518 if (isset($this->instances[$cName])) { 519 return $this->instances[$cName]; 520 } 521 522 if (!$instance) { 523 $this->checkNestedContextStart($cName); 524 if ( 525 isset($this->invokableClasses[$cName]) 526 || isset($this->factories[$cName]) 527 || isset($this->aliases[$cName]) 528 || $this->canCreateFromAbstractFactory($cName, $name) 529 ) { 530 $instance = $this->create(array($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(array($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 ( 563 ($this->shareByDefault && !isset($this->shared[$cName])) 564 || (isset($this->shared[$cName]) && $this->shared[$cName] === true) 565 ) { 566 $this->instances[$cName] = $instance; 567 } 568 569 return $instance; 570 } 571 572 /** 573 * Create an instance of the requested service 574 * 575 * @param string|array $name 576 * 577 * @return bool|object 578 */ 579 public function create($name) 580 { 581 if (is_array($name)) { 582 list($cName, $rName) = $name; 583 } else { 584 $rName = $name; 585 586 // inlined code from ServiceManager::canonicalizeName for performance 587 if (isset($this->canonicalNames[$rName])) { 588 $cName = $this->canonicalNames[$name]; 589 } else { 590 $cName = $this->canonicalizeName($name); 591 } 592 } 593 594 if (isset($this->delegators[$cName])) { 595 return $this->createDelegatorFromFactory($cName, $rName); 596 } 597 598 return $this->doCreate($rName, $cName); 599 } 600 601 /** 602 * Creates a callback that uses a delegator to create a service 603 * 604 * @param DelegatorFactoryInterface|callable $delegatorFactory the delegator factory 605 * @param string $rName requested service name 606 * @param string $cName canonical service name 607 * @param callable $creationCallback callback for instantiating the real service 608 * 609 * @return callable 610 */ 611 private function createDelegatorCallback($delegatorFactory, $rName, $cName, $creationCallback) 612 { 613 $serviceManager = $this; 614 615 return function () use ($serviceManager, $delegatorFactory, $rName, $cName, $creationCallback) { 616 return $delegatorFactory instanceof DelegatorFactoryInterface 617 ? $delegatorFactory->createDelegatorWithName($serviceManager, $cName, $rName, $creationCallback) 618 : $delegatorFactory($serviceManager, $cName, $rName, $creationCallback); 619 }; 620 } 621 622 /** 623 * Actually creates the service 624 * 625 * @param string $rName real service name 626 * @param string $cName canonicalized service name 627 * 628 * @return bool|mixed|null|object 629 * @throws Exception\ServiceNotFoundException 630 * 631 * @internal this method is internal because of PHP 5.3 compatibility - do not explicitly use it 632 */ 633 public function doCreate($rName, $cName) 634 { 635 $instance = null; 636 637 if (isset($this->factories[$cName])) { 638 $instance = $this->createFromFactory($cName, $rName); 639 } 640 641 if ($instance === null && isset($this->invokableClasses[$cName])) { 642 $instance = $this->createFromInvokable($cName, $rName); 643 } 644 $this->checkNestedContextStart($cName); 645 if ($instance === null && $this->canCreateFromAbstractFactory($cName, $rName)) { 646 $instance = $this->createFromAbstractFactory($cName, $rName); 647 } 648 $this->checkNestedContextStop(); 649 650 if ($instance === null && $this->throwExceptionInCreate) { 651 $this->checkNestedContextStop(true); 652 throw new Exception\ServiceNotFoundException(sprintf( 653 'No valid instance was found for %s%s', 654 $cName, 655 ($rName ? '(alias: ' . $rName . ')' : '') 656 )); 657 } 658 659 // Do not call initializers if we do not have an instance 660 if ($instance === null) { 661 return $instance; 662 } 663 664 foreach ($this->initializers as $initializer) { 665 if ($initializer instanceof InitializerInterface) { 666 $initializer->initialize($instance, $this); 667 } else { 668 call_user_func($initializer, $instance, $this); 669 } 670 } 671 672 return $instance; 673 } 674 675 /** 676 * Determine if we can create an instance. 677 * Proxies to has() 678 * 679 * @param string|array $name 680 * @param bool $checkAbstractFactories 681 * @return bool 682 * @deprecated this method is being deprecated as of zendframework 2.3, and may be removed in future major versions 683 */ 684 public function canCreate($name, $checkAbstractFactories = true) 685 { 686 trigger_error(sprintf('%s is deprecated; please use %s::has', __METHOD__, __CLASS__), E_USER_DEPRECATED); 687 return $this->has($name, $checkAbstractFactories, false); 688 } 689 690 /** 691 * Determine if an instance exists. 692 * 693 * @param string|array $name An array argument accepts exactly two values. 694 * Example: array('canonicalName', 'requestName') 695 * @param bool $checkAbstractFactories 696 * @param bool $usePeeringServiceManagers 697 * @return bool 698 */ 699 public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true) 700 { 701 if (is_string($name)) { 702 $rName = $name; 703 704 // inlined code from ServiceManager::canonicalizeName for performance 705 if (isset($this->canonicalNames[$rName])) { 706 $cName = $this->canonicalNames[$rName]; 707 } else { 708 $cName = $this->canonicalizeName($name); 709 } 710 } elseif (is_array($name) && count($name) >= 2) { 711 list($cName, $rName) = $name; 712 } else { 713 return false; 714 } 715 716 if (isset($this->invokableClasses[$cName]) 717 || isset($this->factories[$cName]) 718 || isset($this->aliases[$cName]) 719 || isset($this->instances[$cName]) 720 || ($checkAbstractFactories && $this->canCreateFromAbstractFactory($cName, $rName)) 721 ) { 722 return true; 723 } 724 725 if ($usePeeringServiceManagers) { 726 $caller = $this->serviceManagerCaller; 727 foreach ($this->peeringServiceManagers as $peeringServiceManager) { 728 // ignore peering service manager if they are the same instance 729 if ($caller === $peeringServiceManager) { 730 continue; 731 } 732 733 $peeringServiceManager->serviceManagerCaller = $this; 734 735 if ($peeringServiceManager->has($name)) { 736 $peeringServiceManager->serviceManagerCaller = null; 737 return true; 738 } 739 740 $peeringServiceManager->serviceManagerCaller = null; 741 } 742 } 743 744 return false; 745 } 746 747 /** 748 * Determine if we can create an instance from an abstract factory. 749 * 750 * @param string $cName 751 * @param string $rName 752 * @return bool 753 */ 754 public function canCreateFromAbstractFactory($cName, $rName) 755 { 756 if (array_key_exists($cName, $this->nestedContext)) { 757 $context = $this->nestedContext[$cName]; 758 if ($context === false) { 759 return false; 760 } elseif (is_object($context)) { 761 return !isset($this->pendingAbstractFactoryRequests[get_class($context).$cName]); 762 } 763 } 764 $this->checkNestedContextStart($cName); 765 // check abstract factories 766 $result = false; 767 $this->nestedContext[$cName] = false; 768 foreach ($this->abstractFactories as $abstractFactory) { 769 $pendingKey = get_class($abstractFactory).$cName; 770 if (isset($this->pendingAbstractFactoryRequests[$pendingKey])) { 771 $result = false; 772 break; 773 } 774 775 if ($abstractFactory->canCreateServiceWithName($this, $cName, $rName)) { 776 $this->nestedContext[$cName] = $abstractFactory; 777 $result = true; 778 break; 779 } 780 } 781 $this->checkNestedContextStop(); 782 return $result; 783 } 784 785 /** 786 * Ensure the alias definition will not result in a circular reference 787 * 788 * @param string $alias 789 * @param string $nameOrAlias 790 * @throws Exception\CircularReferenceException 791 * @return self 792 */ 793 protected function checkForCircularAliasReference($alias, $nameOrAlias) 794 { 795 $aliases = $this->aliases; 796 $aliases[$alias] = $nameOrAlias; 797 $stack = array(); 798 799 while (isset($aliases[$alias])) { 800 if (isset($stack[$alias])) { 801 throw new Exception\CircularReferenceException(sprintf( 802 'The alias definition "%s" : "%s" results in a circular reference: "%s" -> "%s"', 803 $alias, 804 $nameOrAlias, 805 implode('" -> "', $stack), 806 $alias 807 )); 808 } 809 810 $stack[$alias] = $alias; 811 $alias = $aliases[$alias]; 812 } 813 814 return $this; 815 } 816 817 /** 818 * @param string $alias 819 * @param string $nameOrAlias 820 * @return ServiceManager 821 * @throws Exception\ServiceNotFoundException 822 * @throws Exception\InvalidServiceNameException 823 */ 824 public function setAlias($alias, $nameOrAlias) 825 { 826 if (!is_string($alias) || !is_string($nameOrAlias)) { 827 throw new Exception\InvalidServiceNameException('Service or alias names must be strings.'); 828 } 829 830 $cAlias = $this->canonicalizeName($alias); 831 $nameOrAlias = $this->canonicalizeName($nameOrAlias); 832 833 if ($alias == '' || $nameOrAlias == '') { 834 throw new Exception\InvalidServiceNameException('Invalid service name alias'); 835 } 836 837 if ($this->allowOverride === false && $this->has(array($cAlias, $alias), false)) { 838 throw new Exception\InvalidServiceNameException(sprintf( 839 'An alias by the name "%s" or "%s" already exists', 840 $cAlias, 841 $alias 842 )); 843 } 844 845 if ($this->hasAlias($alias)) { 846 $this->checkForCircularAliasReference($cAlias, $nameOrAlias); 847 } 848 849 $this->aliases[$cAlias] = $nameOrAlias; 850 return $this; 851 } 852 853 /** 854 * Determine if we have an alias 855 * 856 * @param string $alias 857 * @return bool 858 */ 859 public function hasAlias($alias) 860 { 861 return isset($this->aliases[$this->canonicalizeName($alias)]); 862 } 863 864 /** 865 * Create scoped service manager 866 * 867 * @param string $peering 868 * @return ServiceManager 869 */ 870 public function createScopedServiceManager($peering = self::SCOPE_PARENT) 871 { 872 $scopedServiceManager = new ServiceManager(); 873 if ($peering == self::SCOPE_PARENT) { 874 $scopedServiceManager->peeringServiceManagers[] = $this; 875 } 876 if ($peering == self::SCOPE_CHILD) { 877 $this->peeringServiceManagers[] = $scopedServiceManager; 878 } 879 return $scopedServiceManager; 880 } 881 882 /** 883 * Add a peering relationship 884 * 885 * @param ServiceManager $manager 886 * @param string $peering 887 * @return ServiceManager 888 */ 889 public function addPeeringServiceManager(ServiceManager $manager, $peering = self::SCOPE_PARENT) 890 { 891 if ($peering == self::SCOPE_PARENT) { 892 $this->peeringServiceManagers[] = $manager; 893 } 894 if ($peering == self::SCOPE_CHILD) { 895 $manager->peeringServiceManagers[] = $this; 896 } 897 return $this; 898 } 899 900 /** 901 * Canonicalize name 902 * 903 * @param string $name 904 * @return string 905 */ 906 protected function canonicalizeName($name) 907 { 908 if (isset($this->canonicalNames[$name])) { 909 return $this->canonicalNames[$name]; 910 } 911 912 // this is just for performance instead of using str_replace 913 return $this->canonicalNames[$name] = strtolower(strtr($name, $this->canonicalNamesReplacements)); 914 } 915 916 /** 917 * Create service via callback 918 * 919 * @param callable $callable 920 * @param string $cName 921 * @param string $rName 922 * @throws Exception\ServiceNotCreatedException 923 * @throws Exception\ServiceNotFoundException 924 * @throws Exception\CircularDependencyFoundException 925 * @return object 926 */ 927 protected function createServiceViaCallback($callable, $cName, $rName) 928 { 929 static $circularDependencyResolver = array(); 930 $depKey = spl_object_hash($this) . '-' . $cName; 931 932 if (isset($circularDependencyResolver[$depKey])) { 933 $circularDependencyResolver = array(); 934 throw new Exception\CircularDependencyFoundException('Circular dependency for LazyServiceLoader was found for instance ' . $rName); 935 } 936 937 try { 938 $circularDependencyResolver[$depKey] = true; 939 $instance = call_user_func($callable, $this, $cName, $rName); 940 unset($circularDependencyResolver[$depKey]); 941 } catch (Exception\ServiceNotFoundException $e) { 942 unset($circularDependencyResolver[$depKey]); 943 throw $e; 944 } catch (\Exception $e) { 945 unset($circularDependencyResolver[$depKey]); 946 throw new Exception\ServiceNotCreatedException( 947 sprintf('An exception was raised while creating "%s"; no instance returned', $rName), 948 $e->getCode(), 949 $e 950 ); 951 } 952 if ($instance === null) { 953 throw new Exception\ServiceNotCreatedException('The factory was called but did not return an instance.'); 954 } 955 956 return $instance; 957 } 958 959 /** 960 * Retrieve a keyed list of all registered services. Handy for debugging! 961 * 962 * @return array 963 */ 964 public function getRegisteredServices() 965 { 966 return array( 967 'invokableClasses' => array_keys($this->invokableClasses), 968 'factories' => array_keys($this->factories), 969 'aliases' => array_keys($this->aliases), 970 'instances' => array_keys($this->instances), 971 ); 972 } 973 974 /** 975 * Retrieve a keyed list of all canonical names. Handy for debugging! 976 * 977 * @return array 978 */ 979 public function getCanonicalNames() 980 { 981 return $this->canonicalNames; 982 } 983 984 /** 985 * Allows to override the canonical names lookup map with predefined 986 * values. 987 * 988 * @param array $canonicalNames 989 * @return ServiceManager 990 */ 991 public function setCanonicalNames($canonicalNames) 992 { 993 $this->canonicalNames = $canonicalNames; 994 995 return $this; 996 } 997 998 /** 999 * Attempt to retrieve an instance via a peering manager 1000 * 1001 * @param string $name 1002 * @return mixed 1003 */ 1004 protected function retrieveFromPeeringManager($name) 1005 { 1006 if (null !== ($service = $this->loopPeeringServiceManagers($name))) { 1007 return $service; 1008 } 1009 1010 $name = $this->canonicalizeName($name); 1011 1012 if ($this->hasAlias($name)) { 1013 do { 1014 $name = $this->aliases[$name]; 1015 } while ($this->hasAlias($name)); 1016 } 1017 1018 if (null !== ($service = $this->loopPeeringServiceManagers($name))) { 1019 return $service; 1020 } 1021 1022 return; 1023 } 1024 1025 /** 1026 * Loop over peering service managers. 1027 * 1028 * @param string $name 1029 * @return mixed 1030 */ 1031 protected function loopPeeringServiceManagers($name) 1032 { 1033 $caller = $this->serviceManagerCaller; 1034 1035 foreach ($this->peeringServiceManagers as $peeringServiceManager) { 1036 // ignore peering service manager if they are the same instance 1037 if ($caller === $peeringServiceManager) { 1038 continue; 1039 } 1040 1041 // pass this instance to peering service manager 1042 $peeringServiceManager->serviceManagerCaller = $this; 1043 1044 if ($peeringServiceManager->has($name)) { 1045 $this->shared[$name] = $peeringServiceManager->isShared($name); 1046 $instance = $peeringServiceManager->get($name); 1047 $peeringServiceManager->serviceManagerCaller = null; 1048 return $instance; 1049 } 1050 1051 $peeringServiceManager->serviceManagerCaller = null; 1052 } 1053 1054 return; 1055 } 1056 1057 /** 1058 * Attempt to create an instance via an invokable class 1059 * 1060 * @param string $canonicalName 1061 * @param string $requestedName 1062 * @return null|\stdClass 1063 * @throws Exception\ServiceNotFoundException If resolved class does not exist 1064 */ 1065 protected function createFromInvokable($canonicalName, $requestedName) 1066 { 1067 $invokable = $this->invokableClasses[$canonicalName]; 1068 if (!class_exists($invokable)) { 1069 throw new Exception\ServiceNotFoundException(sprintf( 1070 '%s: failed retrieving "%s%s" via invokable class "%s"; class does not exist', 1071 get_class($this) . '::' . __FUNCTION__, 1072 $canonicalName, 1073 ($requestedName ? '(alias: ' . $requestedName . ')' : ''), 1074 $invokable 1075 )); 1076 } 1077 $instance = new $invokable; 1078 return $instance; 1079 } 1080 1081 /** 1082 * Attempt to create an instance via a factory 1083 * 1084 * @param string $canonicalName 1085 * @param string $requestedName 1086 * @return mixed 1087 * @throws Exception\ServiceNotCreatedException If factory is not callable 1088 */ 1089 protected function createFromFactory($canonicalName, $requestedName) 1090 { 1091 $factory = $this->factories[$canonicalName]; 1092 if (is_string($factory) && class_exists($factory, true)) { 1093 $factory = new $factory; 1094 $this->factories[$canonicalName] = $factory; 1095 } 1096 if ($factory instanceof FactoryInterface) { 1097 $instance = $this->createServiceViaCallback(array($factory, 'createService'), $canonicalName, $requestedName); 1098 } elseif (is_callable($factory)) { 1099 $instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName); 1100 } else { 1101 throw new Exception\ServiceNotCreatedException(sprintf( 1102 'While attempting to create %s%s an invalid factory was registered for this instance type.', 1103 $canonicalName, 1104 ($requestedName ? '(alias: ' . $requestedName . ')' : '') 1105 )); 1106 } 1107 return $instance; 1108 } 1109 1110 /** 1111 * Attempt to create an instance via an abstract factory 1112 * 1113 * @param string $canonicalName 1114 * @param string $requestedName 1115 * @return object|null 1116 * @throws Exception\ServiceNotCreatedException If abstract factory is not callable 1117 */ 1118 protected function createFromAbstractFactory($canonicalName, $requestedName) 1119 { 1120 if (isset($this->nestedContext[$canonicalName])) { 1121 $abstractFactory = $this->nestedContext[$canonicalName]; 1122 $pendingKey = get_class($abstractFactory).$canonicalName; 1123 try { 1124 $this->pendingAbstractFactoryRequests[$pendingKey] = true; 1125 $instance = $this->createServiceViaCallback( 1126 array($abstractFactory, 'createServiceWithName'), 1127 $canonicalName, 1128 $requestedName 1129 ); 1130 unset($this->pendingAbstractFactoryRequests[$pendingKey]); 1131 return $instance; 1132 } catch (\Exception $e) { 1133 unset($this->pendingAbstractFactoryRequests[$pendingKey]); 1134 $this->checkNestedContextStop(true); 1135 throw new Exception\ServiceNotCreatedException( 1136 sprintf( 1137 'An abstract factory could not create an instance of %s%s.', 1138 $canonicalName, 1139 ($requestedName ? '(alias: ' . $requestedName . ')' : '') 1140 ), 1141 $e->getCode(), 1142 $e 1143 ); 1144 } 1145 } 1146 return; 1147 } 1148 1149 /** 1150 * 1151 * @param string $cName 1152 * @return self 1153 */ 1154 protected function checkNestedContextStart($cName) 1155 { 1156 if ($this->nestedContextCounter === -1 || !isset($this->nestedContext[$cName])) { 1157 $this->nestedContext[$cName] = null; 1158 } 1159 $this->nestedContextCounter++; 1160 return $this; 1161 } 1162 1163 /** 1164 * 1165 * @param bool $force 1166 * @return self 1167 */ 1168 protected function checkNestedContextStop($force = false) 1169 { 1170 if ($force) { 1171 $this->nestedContextCounter = -1; 1172 $this->nestedContext = array(); 1173 return $this; 1174 } 1175 1176 $this->nestedContextCounter--; 1177 if ($this->nestedContextCounter === -1) { 1178 $this->nestedContext = array(); 1179 } 1180 return $this; 1181 } 1182 1183 /** 1184 * @param $canonicalName 1185 * @param $requestedName 1186 * @return mixed 1187 * @throws Exception\ServiceNotCreatedException 1188 */ 1189 protected function createDelegatorFromFactory($canonicalName, $requestedName) 1190 { 1191 $serviceManager = $this; 1192 $delegatorsCount = count($this->delegators[$canonicalName]); 1193 $creationCallback = function () use ($serviceManager, $requestedName, $canonicalName) { 1194 return $serviceManager->doCreate($requestedName, $canonicalName); 1195 }; 1196 1197 for ($i = 0; $i < $delegatorsCount; $i += 1) { 1198 $delegatorFactory = $this->delegators[$canonicalName][$i]; 1199 1200 if (is_string($delegatorFactory)) { 1201 $delegatorFactory = !$this->has($delegatorFactory) && class_exists($delegatorFactory, true) ? 1202 new $delegatorFactory 1203 : $this->get($delegatorFactory); 1204 $this->delegators[$canonicalName][$i] = $delegatorFactory; 1205 } 1206 1207 if (!$delegatorFactory instanceof DelegatorFactoryInterface && !is_callable($delegatorFactory)) { 1208 throw new Exception\ServiceNotCreatedException(sprintf( 1209 'While attempting to create %s%s an invalid factory was registered for this instance type.', 1210 $canonicalName, 1211 ($requestedName ? '(alias: ' . $requestedName . ')' : '') 1212 )); 1213 } 1214 1215 $creationCallback = $this->createDelegatorCallback( 1216 $delegatorFactory, 1217 $requestedName, 1218 $canonicalName, 1219 $creationCallback 1220 ); 1221 } 1222 1223 return $creationCallback($serviceManager, $canonicalName, $requestedName, $creationCallback); 1224 } 1225 1226 /** 1227 * Checks if the object has this class as one of its parents 1228 * 1229 * @see https://bugs.php.net/bug.php?id=53727 1230 * @see https://github.com/zendframework/zf2/pull/1807 1231 * 1232 * @deprecated since zf 2.3 requires PHP >= 5.3.23 1233 * 1234 * @param string $className 1235 * @param string $type 1236 * @return bool 1237 * 1238 * @deprecated this method is being deprecated as of zendframework 2.2, and may be removed in future major versions 1239 */ 1240 protected static function isSubclassOf($className, $type) 1241 { 1242 return is_subclass_of($className, $type); 1243 } 1244 1245 /** 1246 * Unregister a service 1247 * 1248 * Called when $allowOverride is true and we detect that a service being 1249 * added to the instance already exists. This will remove the duplicate 1250 * entry, and also any shared flags previously registered. 1251 * 1252 * @param string $canonical 1253 * @return void 1254 */ 1255 protected function unregisterService($canonical) 1256 { 1257 $types = array('invokableClasses', 'factories', 'aliases'); 1258 foreach ($types as $type) { 1259 if (isset($this->{$type}[$canonical])) { 1260 unset($this->{$type}[$canonical]); 1261 break; 1262 } 1263 } 1264 1265 if (isset($this->instances[$canonical])) { 1266 unset($this->instances[$canonical]); 1267 } 1268 1269 if (isset($this->shared[$canonical])) { 1270 unset($this->shared[$canonical]); 1271 } 1272 } 1273} 1274