mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-02-28 15:10:54 +03:00
1274 lines
39 KiB
PHP
1274 lines
39 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework (http://framework.zend.com/)
|
|
*
|
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
|
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
|
|
namespace Zend\ServiceManager;
|
|
|
|
class ServiceManager implements ServiceLocatorInterface
|
|
{
|
|
/**@#+
|
|
* Constants
|
|
*/
|
|
const SCOPE_PARENT = 'parent';
|
|
const SCOPE_CHILD = 'child';
|
|
/**@#-*/
|
|
|
|
/**
|
|
* Lookup for canonicalized names.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $canonicalNames = array();
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
protected $allowOverride = false;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $invokableClasses = array();
|
|
|
|
/**
|
|
* @var string|callable|\Closure|FactoryInterface[]
|
|
*/
|
|
protected $factories = array();
|
|
|
|
/**
|
|
* @var AbstractFactoryInterface[]
|
|
*/
|
|
protected $abstractFactories = array();
|
|
|
|
/**
|
|
* @var array[]
|
|
*/
|
|
protected $delegators = array();
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $pendingAbstractFactoryRequests = array();
|
|
|
|
/**
|
|
* @var integer
|
|
*/
|
|
protected $nestedContextCounter = -1;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $nestedContext = array();
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $shared = array();
|
|
|
|
/**
|
|
* Registered services and cached values
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $instances = array();
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $aliases = array();
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $initializers = array();
|
|
|
|
/**
|
|
* @var ServiceManager[]
|
|
*/
|
|
protected $peeringServiceManagers = array();
|
|
|
|
/**
|
|
* Whether or not to share by default
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $shareByDefault = true;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
protected $retrieveFromPeeringManagerFirst = false;
|
|
|
|
/**
|
|
* @var bool Track whether not to throw exceptions during create()
|
|
*/
|
|
protected $throwExceptionInCreate = true;
|
|
|
|
/**
|
|
* @var array map of characters to be replaced through strtr
|
|
*/
|
|
protected $canonicalNamesReplacements = array('-' => '', '_' => '', ' ' => '', '\\' => '', '/' => '');
|
|
|
|
/**
|
|
* @var ServiceLocatorInterface
|
|
*/
|
|
protected $serviceManagerCaller;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param ConfigInterface $config
|
|
*/
|
|
public function __construct(ConfigInterface $config = null)
|
|
{
|
|
if ($config) {
|
|
$config->configureServiceManager($this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set allow override
|
|
*
|
|
* @param $allowOverride
|
|
* @return ServiceManager
|
|
*/
|
|
public function setAllowOverride($allowOverride)
|
|
{
|
|
$this->allowOverride = (bool) $allowOverride;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get allow override
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function getAllowOverride()
|
|
{
|
|
return $this->allowOverride;
|
|
}
|
|
|
|
/**
|
|
* Set flag indicating whether services are shared by default
|
|
*
|
|
* @param bool $shareByDefault
|
|
* @return ServiceManager
|
|
* @throws Exception\RuntimeException if allowOverride is false
|
|
*/
|
|
public function setShareByDefault($shareByDefault)
|
|
{
|
|
if ($this->allowOverride === false) {
|
|
throw new Exception\RuntimeException(sprintf(
|
|
'%s: cannot alter default shared service setting; container is marked immutable (allow_override is false)',
|
|
get_class($this) . '::' . __FUNCTION__
|
|
));
|
|
}
|
|
$this->shareByDefault = (bool) $shareByDefault;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Are services shared by default?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function shareByDefault()
|
|
{
|
|
return $this->shareByDefault;
|
|
}
|
|
|
|
/**
|
|
* Set throw exceptions in create
|
|
*
|
|
* @param bool $throwExceptionInCreate
|
|
* @return ServiceManager
|
|
*/
|
|
public function setThrowExceptionInCreate($throwExceptionInCreate)
|
|
{
|
|
$this->throwExceptionInCreate = $throwExceptionInCreate;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get throw exceptions in create
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function getThrowExceptionInCreate()
|
|
{
|
|
return $this->throwExceptionInCreate;
|
|
}
|
|
|
|
/**
|
|
* Set flag indicating whether to pull from peering manager before attempting creation
|
|
*
|
|
* @param bool $retrieveFromPeeringManagerFirst
|
|
* @return ServiceManager
|
|
*/
|
|
public function setRetrieveFromPeeringManagerFirst($retrieveFromPeeringManagerFirst = true)
|
|
{
|
|
$this->retrieveFromPeeringManagerFirst = (bool) $retrieveFromPeeringManagerFirst;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Should we retrieve from the peering manager prior to attempting to create a service?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function retrieveFromPeeringManagerFirst()
|
|
{
|
|
return $this->retrieveFromPeeringManagerFirst;
|
|
}
|
|
|
|
/**
|
|
* Set invokable class
|
|
*
|
|
* @param string $name
|
|
* @param string $invokableClass
|
|
* @param bool $shared
|
|
* @return ServiceManager
|
|
* @throws Exception\InvalidServiceNameException
|
|
*/
|
|
public function setInvokableClass($name, $invokableClass, $shared = null)
|
|
{
|
|
$cName = $this->canonicalizeName($name);
|
|
|
|
if ($this->has(array($cName, $name), false)) {
|
|
if ($this->allowOverride === false) {
|
|
throw new Exception\InvalidServiceNameException(sprintf(
|
|
'A service by the name or alias "%s" already exists and cannot be overridden; please use an alternate name',
|
|
$name
|
|
));
|
|
}
|
|
$this->unregisterService($cName);
|
|
}
|
|
|
|
if ($shared === null) {
|
|
$shared = $this->shareByDefault;
|
|
}
|
|
|
|
$this->invokableClasses[$cName] = $invokableClass;
|
|
$this->shared[$cName] = (bool) $shared;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set factory
|
|
*
|
|
* @param string $name
|
|
* @param string|FactoryInterface|callable $factory
|
|
* @param bool $shared
|
|
* @return ServiceManager
|
|
* @throws Exception\InvalidArgumentException
|
|
* @throws Exception\InvalidServiceNameException
|
|
*/
|
|
public function setFactory($name, $factory, $shared = null)
|
|
{
|
|
$cName = $this->canonicalizeName($name);
|
|
|
|
if (!($factory instanceof FactoryInterface || is_string($factory) || is_callable($factory))) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Provided abstract factory must be the class name of an abstract factory or an instance of an AbstractFactoryInterface.'
|
|
);
|
|
}
|
|
|
|
if ($this->has(array($cName, $name), false)) {
|
|
if ($this->allowOverride === false) {
|
|
throw new Exception\InvalidServiceNameException(sprintf(
|
|
'A service by the name or alias "%s" already exists and cannot be overridden, please use an alternate name',
|
|
$name
|
|
));
|
|
}
|
|
$this->unregisterService($cName);
|
|
}
|
|
|
|
if ($shared === null) {
|
|
$shared = $this->shareByDefault;
|
|
}
|
|
|
|
$this->factories[$cName] = $factory;
|
|
$this->shared[$cName] = (bool) $shared;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add abstract factory
|
|
*
|
|
* @param AbstractFactoryInterface|string $factory
|
|
* @param bool $topOfStack
|
|
* @return ServiceManager
|
|
* @throws Exception\InvalidArgumentException if the abstract factory is invalid
|
|
*/
|
|
public function addAbstractFactory($factory, $topOfStack = true)
|
|
{
|
|
if (!$factory instanceof AbstractFactoryInterface && is_string($factory)) {
|
|
$factory = new $factory();
|
|
}
|
|
|
|
if (!$factory instanceof AbstractFactoryInterface) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Provided abstract factory must be the class name of an abstract'
|
|
. ' factory or an instance of an AbstractFactoryInterface.'
|
|
);
|
|
}
|
|
|
|
if ($topOfStack) {
|
|
array_unshift($this->abstractFactories, $factory);
|
|
} else {
|
|
array_push($this->abstractFactories, $factory);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sets the given service name as to be handled by a delegator factory
|
|
*
|
|
* @param string $serviceName name of the service being the delegate
|
|
* @param string $delegatorFactoryName name of the service being the delegator factory
|
|
*
|
|
* @return ServiceManager
|
|
*/
|
|
public function addDelegator($serviceName, $delegatorFactoryName)
|
|
{
|
|
$cName = $this->canonicalizeName($serviceName);
|
|
|
|
if (!isset($this->delegators[$cName])) {
|
|
$this->delegators[$cName] = array();
|
|
}
|
|
|
|
$this->delegators[$cName][] = $delegatorFactoryName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add initializer
|
|
*
|
|
* @param callable|InitializerInterface $initializer
|
|
* @param bool $topOfStack
|
|
* @return ServiceManager
|
|
* @throws Exception\InvalidArgumentException
|
|
*/
|
|
public function addInitializer($initializer, $topOfStack = true)
|
|
{
|
|
if (!($initializer instanceof InitializerInterface || is_callable($initializer))) {
|
|
if (is_string($initializer)) {
|
|
$initializer = new $initializer;
|
|
}
|
|
|
|
if (!($initializer instanceof InitializerInterface || is_callable($initializer))) {
|
|
throw new Exception\InvalidArgumentException('$initializer should be callable.');
|
|
}
|
|
}
|
|
|
|
if ($topOfStack) {
|
|
array_unshift($this->initializers, $initializer);
|
|
} else {
|
|
array_push($this->initializers, $initializer);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Register a service with the locator
|
|
*
|
|
* @param string $name
|
|
* @param mixed $service
|
|
* @return ServiceManager
|
|
* @throws Exception\InvalidServiceNameException
|
|
*/
|
|
public function setService($name, $service)
|
|
{
|
|
$cName = $this->canonicalizeName($name);
|
|
|
|
if ($this->has($cName, false)) {
|
|
if ($this->allowOverride === false) {
|
|
throw new Exception\InvalidServiceNameException(sprintf(
|
|
'%s: A service by the name "%s" or alias already exists and cannot be overridden, please use an alternate name.',
|
|
get_class($this) . '::' . __FUNCTION__,
|
|
$name
|
|
));
|
|
}
|
|
$this->unregisterService($cName);
|
|
}
|
|
|
|
$this->instances[$cName] = $service;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param bool $isShared
|
|
* @return ServiceManager
|
|
* @throws Exception\ServiceNotFoundException
|
|
*/
|
|
public function setShared($name, $isShared)
|
|
{
|
|
$cName = $this->canonicalizeName($name);
|
|
|
|
if (
|
|
!isset($this->invokableClasses[$cName])
|
|
&& !isset($this->factories[$cName])
|
|
&& !$this->canCreateFromAbstractFactory($cName, $name)
|
|
) {
|
|
throw new Exception\ServiceNotFoundException(sprintf(
|
|
'%s: A service by the name "%s" was not found and could not be marked as shared',
|
|
get_class($this) . '::' . __FUNCTION__,
|
|
$name
|
|
));
|
|
}
|
|
|
|
$this->shared[$cName] = (bool) $isShared;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @return bool
|
|
* @throws Exception\ServiceNotFoundException
|
|
*/
|
|
public function isShared($name)
|
|
{
|
|
$cName = $this->canonicalizeName($name);
|
|
|
|
if (!$this->has($name)) {
|
|
throw new Exception\ServiceNotFoundException(sprintf(
|
|
'%s: A service by the name "%s" was not found',
|
|
get_class($this) . '::' . __FUNCTION__,
|
|
$name
|
|
));
|
|
}
|
|
|
|
if (!isset($this->shared[$cName])) {
|
|
return $this->shareByDefault();
|
|
}
|
|
|
|
return $this->shared[$cName];
|
|
}
|
|
|
|
/**
|
|
* Resolve the alias for the given canonical name
|
|
*
|
|
* @param string $cName The canonical name to resolve
|
|
* @return string The resolved canonical name
|
|
*/
|
|
protected function resolveAlias($cName)
|
|
{
|
|
$stack = array();
|
|
|
|
while ($this->hasAlias($cName)) {
|
|
if (isset($stack[$cName])) {
|
|
throw new Exception\CircularReferenceException(sprintf(
|
|
'Circular alias reference: %s -> %s',
|
|
implode(' -> ', $stack),
|
|
$cName
|
|
));
|
|
}
|
|
|
|
$stack[$cName] = $cName;
|
|
$cName = $this->aliases[$cName];
|
|
}
|
|
|
|
return $cName;
|
|
}
|
|
|
|
/**
|
|
* Retrieve a registered instance
|
|
*
|
|
* @param string $name
|
|
* @param bool $usePeeringServiceManagers
|
|
* @throws Exception\ServiceNotFoundException
|
|
* @return object|array
|
|
*/
|
|
public function get($name, $usePeeringServiceManagers = true)
|
|
{
|
|
// inlined code from ServiceManager::canonicalizeName for performance
|
|
if (isset($this->canonicalNames[$name])) {
|
|
$cName = $this->canonicalNames[$name];
|
|
} else {
|
|
$cName = $this->canonicalizeName($name);
|
|
}
|
|
|
|
$isAlias = false;
|
|
|
|
if ($this->hasAlias($cName)) {
|
|
$isAlias = true;
|
|
$cName = $this->resolveAlias($cName);
|
|
}
|
|
|
|
$instance = null;
|
|
|
|
if ($usePeeringServiceManagers && $this->retrieveFromPeeringManagerFirst) {
|
|
$instance = $this->retrieveFromPeeringManager($name);
|
|
|
|
if (null !== $instance) {
|
|
return $instance;
|
|
}
|
|
}
|
|
|
|
if (isset($this->instances[$cName])) {
|
|
return $this->instances[$cName];
|
|
}
|
|
|
|
if (!$instance) {
|
|
$this->checkNestedContextStart($cName);
|
|
if (
|
|
isset($this->invokableClasses[$cName])
|
|
|| isset($this->factories[$cName])
|
|
|| isset($this->aliases[$cName])
|
|
|| $this->canCreateFromAbstractFactory($cName, $name)
|
|
) {
|
|
$instance = $this->create(array($cName, $name));
|
|
} elseif ($isAlias && $this->canCreateFromAbstractFactory($name, $cName)) {
|
|
/*
|
|
* case of an alias leading to an abstract factory :
|
|
* 'my-alias' => 'my-abstract-defined-service'
|
|
* $name = 'my-alias'
|
|
* $cName = 'my-abstract-defined-service'
|
|
*/
|
|
$instance = $this->create(array($name, $cName));
|
|
} elseif ($usePeeringServiceManagers && !$this->retrieveFromPeeringManagerFirst) {
|
|
$instance = $this->retrieveFromPeeringManager($name);
|
|
}
|
|
$this->checkNestedContextStop();
|
|
}
|
|
|
|
// Still no instance? raise an exception
|
|
if ($instance === null) {
|
|
$this->checkNestedContextStop(true);
|
|
if ($isAlias) {
|
|
throw new Exception\ServiceNotFoundException(sprintf(
|
|
'An alias "%s" was requested but no service could be found.',
|
|
$name
|
|
));
|
|
}
|
|
|
|
throw new Exception\ServiceNotFoundException(sprintf(
|
|
'%s was unable to fetch or create an instance for %s',
|
|
get_class($this) . '::' . __FUNCTION__,
|
|
$name
|
|
));
|
|
}
|
|
|
|
if (
|
|
($this->shareByDefault && !isset($this->shared[$cName]))
|
|
|| (isset($this->shared[$cName]) && $this->shared[$cName] === true)
|
|
) {
|
|
$this->instances[$cName] = $instance;
|
|
}
|
|
|
|
return $instance;
|
|
}
|
|
|
|
/**
|
|
* Create an instance of the requested service
|
|
*
|
|
* @param string|array $name
|
|
*
|
|
* @return bool|object
|
|
*/
|
|
public function create($name)
|
|
{
|
|
if (is_array($name)) {
|
|
list($cName, $rName) = $name;
|
|
} else {
|
|
$rName = $name;
|
|
|
|
// inlined code from ServiceManager::canonicalizeName for performance
|
|
if (isset($this->canonicalNames[$rName])) {
|
|
$cName = $this->canonicalNames[$name];
|
|
} else {
|
|
$cName = $this->canonicalizeName($name);
|
|
}
|
|
}
|
|
|
|
if (isset($this->delegators[$cName])) {
|
|
return $this->createDelegatorFromFactory($cName, $rName);
|
|
}
|
|
|
|
return $this->doCreate($rName, $cName);
|
|
}
|
|
|
|
/**
|
|
* Creates a callback that uses a delegator to create a service
|
|
*
|
|
* @param DelegatorFactoryInterface|callable $delegatorFactory the delegator factory
|
|
* @param string $rName requested service name
|
|
* @param string $cName canonical service name
|
|
* @param callable $creationCallback callback for instantiating the real service
|
|
*
|
|
* @return callable
|
|
*/
|
|
private function createDelegatorCallback($delegatorFactory, $rName, $cName, $creationCallback)
|
|
{
|
|
$serviceManager = $this;
|
|
|
|
return function () use ($serviceManager, $delegatorFactory, $rName, $cName, $creationCallback) {
|
|
return $delegatorFactory instanceof DelegatorFactoryInterface
|
|
? $delegatorFactory->createDelegatorWithName($serviceManager, $cName, $rName, $creationCallback)
|
|
: $delegatorFactory($serviceManager, $cName, $rName, $creationCallback);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Actually creates the service
|
|
*
|
|
* @param string $rName real service name
|
|
* @param string $cName canonicalized service name
|
|
*
|
|
* @return bool|mixed|null|object
|
|
* @throws Exception\ServiceNotFoundException
|
|
*
|
|
* @internal this method is internal because of PHP 5.3 compatibility - do not explicitly use it
|
|
*/
|
|
public function doCreate($rName, $cName)
|
|
{
|
|
$instance = null;
|
|
|
|
if (isset($this->factories[$cName])) {
|
|
$instance = $this->createFromFactory($cName, $rName);
|
|
}
|
|
|
|
if ($instance === null && isset($this->invokableClasses[$cName])) {
|
|
$instance = $this->createFromInvokable($cName, $rName);
|
|
}
|
|
$this->checkNestedContextStart($cName);
|
|
if ($instance === null && $this->canCreateFromAbstractFactory($cName, $rName)) {
|
|
$instance = $this->createFromAbstractFactory($cName, $rName);
|
|
}
|
|
$this->checkNestedContextStop();
|
|
|
|
if ($instance === null && $this->throwExceptionInCreate) {
|
|
$this->checkNestedContextStop(true);
|
|
throw new Exception\ServiceNotFoundException(sprintf(
|
|
'No valid instance was found for %s%s',
|
|
$cName,
|
|
($rName ? '(alias: ' . $rName . ')' : '')
|
|
));
|
|
}
|
|
|
|
// Do not call initializers if we do not have an instance
|
|
if ($instance === null) {
|
|
return $instance;
|
|
}
|
|
|
|
foreach ($this->initializers as $initializer) {
|
|
if ($initializer instanceof InitializerInterface) {
|
|
$initializer->initialize($instance, $this);
|
|
} else {
|
|
call_user_func($initializer, $instance, $this);
|
|
}
|
|
}
|
|
|
|
return $instance;
|
|
}
|
|
|
|
/**
|
|
* Determine if we can create an instance.
|
|
* Proxies to has()
|
|
*
|
|
* @param string|array $name
|
|
* @param bool $checkAbstractFactories
|
|
* @return bool
|
|
* @deprecated this method is being deprecated as of zendframework 2.3, and may be removed in future major versions
|
|
*/
|
|
public function canCreate($name, $checkAbstractFactories = true)
|
|
{
|
|
trigger_error(sprintf('%s is deprecated; please use %s::has', __METHOD__, __CLASS__), E_USER_DEPRECATED);
|
|
return $this->has($name, $checkAbstractFactories, false);
|
|
}
|
|
|
|
/**
|
|
* Determine if an instance exists.
|
|
*
|
|
* @param string|array $name An array argument accepts exactly two values.
|
|
* Example: array('canonicalName', 'requestName')
|
|
* @param bool $checkAbstractFactories
|
|
* @param bool $usePeeringServiceManagers
|
|
* @return bool
|
|
*/
|
|
public function has($name, $checkAbstractFactories = true, $usePeeringServiceManagers = true)
|
|
{
|
|
if (is_string($name)) {
|
|
$rName = $name;
|
|
|
|
// inlined code from ServiceManager::canonicalizeName for performance
|
|
if (isset($this->canonicalNames[$rName])) {
|
|
$cName = $this->canonicalNames[$rName];
|
|
} else {
|
|
$cName = $this->canonicalizeName($name);
|
|
}
|
|
} elseif (is_array($name) && count($name) >= 2) {
|
|
list($cName, $rName) = $name;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (isset($this->invokableClasses[$cName])
|
|
|| isset($this->factories[$cName])
|
|
|| isset($this->aliases[$cName])
|
|
|| isset($this->instances[$cName])
|
|
|| ($checkAbstractFactories && $this->canCreateFromAbstractFactory($cName, $rName))
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
if ($usePeeringServiceManagers) {
|
|
$caller = $this->serviceManagerCaller;
|
|
foreach ($this->peeringServiceManagers as $peeringServiceManager) {
|
|
// ignore peering service manager if they are the same instance
|
|
if ($caller === $peeringServiceManager) {
|
|
continue;
|
|
}
|
|
|
|
$peeringServiceManager->serviceManagerCaller = $this;
|
|
|
|
if ($peeringServiceManager->has($name)) {
|
|
$peeringServiceManager->serviceManagerCaller = null;
|
|
return true;
|
|
}
|
|
|
|
$peeringServiceManager->serviceManagerCaller = null;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Determine if we can create an instance from an abstract factory.
|
|
*
|
|
* @param string $cName
|
|
* @param string $rName
|
|
* @return bool
|
|
*/
|
|
public function canCreateFromAbstractFactory($cName, $rName)
|
|
{
|
|
if (array_key_exists($cName, $this->nestedContext)) {
|
|
$context = $this->nestedContext[$cName];
|
|
if ($context === false) {
|
|
return false;
|
|
} elseif (is_object($context)) {
|
|
return !isset($this->pendingAbstractFactoryRequests[get_class($context).$cName]);
|
|
}
|
|
}
|
|
$this->checkNestedContextStart($cName);
|
|
// check abstract factories
|
|
$result = false;
|
|
$this->nestedContext[$cName] = false;
|
|
foreach ($this->abstractFactories as $abstractFactory) {
|
|
$pendingKey = get_class($abstractFactory).$cName;
|
|
if (isset($this->pendingAbstractFactoryRequests[$pendingKey])) {
|
|
$result = false;
|
|
break;
|
|
}
|
|
|
|
if ($abstractFactory->canCreateServiceWithName($this, $cName, $rName)) {
|
|
$this->nestedContext[$cName] = $abstractFactory;
|
|
$result = true;
|
|
break;
|
|
}
|
|
}
|
|
$this->checkNestedContextStop();
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Ensure the alias definition will not result in a circular reference
|
|
*
|
|
* @param string $alias
|
|
* @param string $nameOrAlias
|
|
* @throws Exception\CircularReferenceException
|
|
* @return self
|
|
*/
|
|
protected function checkForCircularAliasReference($alias, $nameOrAlias)
|
|
{
|
|
$aliases = $this->aliases;
|
|
$aliases[$alias] = $nameOrAlias;
|
|
$stack = array();
|
|
|
|
while (isset($aliases[$alias])) {
|
|
if (isset($stack[$alias])) {
|
|
throw new Exception\CircularReferenceException(sprintf(
|
|
'The alias definition "%s" : "%s" results in a circular reference: "%s" -> "%s"',
|
|
$alias,
|
|
$nameOrAlias,
|
|
implode('" -> "', $stack),
|
|
$alias
|
|
));
|
|
}
|
|
|
|
$stack[$alias] = $alias;
|
|
$alias = $aliases[$alias];
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param string $alias
|
|
* @param string $nameOrAlias
|
|
* @return ServiceManager
|
|
* @throws Exception\ServiceNotFoundException
|
|
* @throws Exception\InvalidServiceNameException
|
|
*/
|
|
public function setAlias($alias, $nameOrAlias)
|
|
{
|
|
if (!is_string($alias) || !is_string($nameOrAlias)) {
|
|
throw new Exception\InvalidServiceNameException('Service or alias names must be strings.');
|
|
}
|
|
|
|
$cAlias = $this->canonicalizeName($alias);
|
|
$nameOrAlias = $this->canonicalizeName($nameOrAlias);
|
|
|
|
if ($alias == '' || $nameOrAlias == '') {
|
|
throw new Exception\InvalidServiceNameException('Invalid service name alias');
|
|
}
|
|
|
|
if ($this->allowOverride === false && $this->has(array($cAlias, $alias), false)) {
|
|
throw new Exception\InvalidServiceNameException(sprintf(
|
|
'An alias by the name "%s" or "%s" already exists',
|
|
$cAlias,
|
|
$alias
|
|
));
|
|
}
|
|
|
|
if ($this->hasAlias($alias)) {
|
|
$this->checkForCircularAliasReference($cAlias, $nameOrAlias);
|
|
}
|
|
|
|
$this->aliases[$cAlias] = $nameOrAlias;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Determine if we have an alias
|
|
*
|
|
* @param string $alias
|
|
* @return bool
|
|
*/
|
|
public function hasAlias($alias)
|
|
{
|
|
return isset($this->aliases[$this->canonicalizeName($alias)]);
|
|
}
|
|
|
|
/**
|
|
* Create scoped service manager
|
|
*
|
|
* @param string $peering
|
|
* @return ServiceManager
|
|
*/
|
|
public function createScopedServiceManager($peering = self::SCOPE_PARENT)
|
|
{
|
|
$scopedServiceManager = new ServiceManager();
|
|
if ($peering == self::SCOPE_PARENT) {
|
|
$scopedServiceManager->peeringServiceManagers[] = $this;
|
|
}
|
|
if ($peering == self::SCOPE_CHILD) {
|
|
$this->peeringServiceManagers[] = $scopedServiceManager;
|
|
}
|
|
return $scopedServiceManager;
|
|
}
|
|
|
|
/**
|
|
* Add a peering relationship
|
|
*
|
|
* @param ServiceManager $manager
|
|
* @param string $peering
|
|
* @return ServiceManager
|
|
*/
|
|
public function addPeeringServiceManager(ServiceManager $manager, $peering = self::SCOPE_PARENT)
|
|
{
|
|
if ($peering == self::SCOPE_PARENT) {
|
|
$this->peeringServiceManagers[] = $manager;
|
|
}
|
|
if ($peering == self::SCOPE_CHILD) {
|
|
$manager->peeringServiceManagers[] = $this;
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Canonicalize name
|
|
*
|
|
* @param string $name
|
|
* @return string
|
|
*/
|
|
protected function canonicalizeName($name)
|
|
{
|
|
if (isset($this->canonicalNames[$name])) {
|
|
return $this->canonicalNames[$name];
|
|
}
|
|
|
|
// this is just for performance instead of using str_replace
|
|
return $this->canonicalNames[$name] = strtolower(strtr($name, $this->canonicalNamesReplacements));
|
|
}
|
|
|
|
/**
|
|
* Create service via callback
|
|
*
|
|
* @param callable $callable
|
|
* @param string $cName
|
|
* @param string $rName
|
|
* @throws Exception\ServiceNotCreatedException
|
|
* @throws Exception\ServiceNotFoundException
|
|
* @throws Exception\CircularDependencyFoundException
|
|
* @return object
|
|
*/
|
|
protected function createServiceViaCallback($callable, $cName, $rName)
|
|
{
|
|
static $circularDependencyResolver = array();
|
|
$depKey = spl_object_hash($this) . '-' . $cName;
|
|
|
|
if (isset($circularDependencyResolver[$depKey])) {
|
|
$circularDependencyResolver = array();
|
|
throw new Exception\CircularDependencyFoundException('Circular dependency for LazyServiceLoader was found for instance ' . $rName);
|
|
}
|
|
|
|
try {
|
|
$circularDependencyResolver[$depKey] = true;
|
|
$instance = call_user_func($callable, $this, $cName, $rName);
|
|
unset($circularDependencyResolver[$depKey]);
|
|
} catch (Exception\ServiceNotFoundException $e) {
|
|
unset($circularDependencyResolver[$depKey]);
|
|
throw $e;
|
|
} catch (\Exception $e) {
|
|
unset($circularDependencyResolver[$depKey]);
|
|
throw new Exception\ServiceNotCreatedException(
|
|
sprintf('An exception was raised while creating "%s"; no instance returned', $rName),
|
|
$e->getCode(),
|
|
$e
|
|
);
|
|
}
|
|
if ($instance === null) {
|
|
throw new Exception\ServiceNotCreatedException('The factory was called but did not return an instance.');
|
|
}
|
|
|
|
return $instance;
|
|
}
|
|
|
|
/**
|
|
* Retrieve a keyed list of all registered services. Handy for debugging!
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getRegisteredServices()
|
|
{
|
|
return array(
|
|
'invokableClasses' => array_keys($this->invokableClasses),
|
|
'factories' => array_keys($this->factories),
|
|
'aliases' => array_keys($this->aliases),
|
|
'instances' => array_keys($this->instances),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Retrieve a keyed list of all canonical names. Handy for debugging!
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getCanonicalNames()
|
|
{
|
|
return $this->canonicalNames;
|
|
}
|
|
|
|
/**
|
|
* Allows to override the canonical names lookup map with predefined
|
|
* values.
|
|
*
|
|
* @param array $canonicalNames
|
|
* @return ServiceManager
|
|
*/
|
|
public function setCanonicalNames($canonicalNames)
|
|
{
|
|
$this->canonicalNames = $canonicalNames;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Attempt to retrieve an instance via a peering manager
|
|
*
|
|
* @param string $name
|
|
* @return mixed
|
|
*/
|
|
protected function retrieveFromPeeringManager($name)
|
|
{
|
|
if (null !== ($service = $this->loopPeeringServiceManagers($name))) {
|
|
return $service;
|
|
}
|
|
|
|
$name = $this->canonicalizeName($name);
|
|
|
|
if ($this->hasAlias($name)) {
|
|
do {
|
|
$name = $this->aliases[$name];
|
|
} while ($this->hasAlias($name));
|
|
}
|
|
|
|
if (null !== ($service = $this->loopPeeringServiceManagers($name))) {
|
|
return $service;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Loop over peering service managers.
|
|
*
|
|
* @param string $name
|
|
* @return mixed
|
|
*/
|
|
protected function loopPeeringServiceManagers($name)
|
|
{
|
|
$caller = $this->serviceManagerCaller;
|
|
|
|
foreach ($this->peeringServiceManagers as $peeringServiceManager) {
|
|
// ignore peering service manager if they are the same instance
|
|
if ($caller === $peeringServiceManager) {
|
|
continue;
|
|
}
|
|
|
|
// pass this instance to peering service manager
|
|
$peeringServiceManager->serviceManagerCaller = $this;
|
|
|
|
if ($peeringServiceManager->has($name)) {
|
|
$this->shared[$name] = $peeringServiceManager->isShared($name);
|
|
$instance = $peeringServiceManager->get($name);
|
|
$peeringServiceManager->serviceManagerCaller = null;
|
|
return $instance;
|
|
}
|
|
|
|
$peeringServiceManager->serviceManagerCaller = null;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Attempt to create an instance via an invokable class
|
|
*
|
|
* @param string $canonicalName
|
|
* @param string $requestedName
|
|
* @return null|\stdClass
|
|
* @throws Exception\ServiceNotFoundException If resolved class does not exist
|
|
*/
|
|
protected function createFromInvokable($canonicalName, $requestedName)
|
|
{
|
|
$invokable = $this->invokableClasses[$canonicalName];
|
|
if (!class_exists($invokable)) {
|
|
throw new Exception\ServiceNotFoundException(sprintf(
|
|
'%s: failed retrieving "%s%s" via invokable class "%s"; class does not exist',
|
|
get_class($this) . '::' . __FUNCTION__,
|
|
$canonicalName,
|
|
($requestedName ? '(alias: ' . $requestedName . ')' : ''),
|
|
$invokable
|
|
));
|
|
}
|
|
$instance = new $invokable;
|
|
return $instance;
|
|
}
|
|
|
|
/**
|
|
* Attempt to create an instance via a factory
|
|
*
|
|
* @param string $canonicalName
|
|
* @param string $requestedName
|
|
* @return mixed
|
|
* @throws Exception\ServiceNotCreatedException If factory is not callable
|
|
*/
|
|
protected function createFromFactory($canonicalName, $requestedName)
|
|
{
|
|
$factory = $this->factories[$canonicalName];
|
|
if (is_string($factory) && class_exists($factory, true)) {
|
|
$factory = new $factory;
|
|
$this->factories[$canonicalName] = $factory;
|
|
}
|
|
if ($factory instanceof FactoryInterface) {
|
|
$instance = $this->createServiceViaCallback(array($factory, 'createService'), $canonicalName, $requestedName);
|
|
} elseif (is_callable($factory)) {
|
|
$instance = $this->createServiceViaCallback($factory, $canonicalName, $requestedName);
|
|
} else {
|
|
throw new Exception\ServiceNotCreatedException(sprintf(
|
|
'While attempting to create %s%s an invalid factory was registered for this instance type.',
|
|
$canonicalName,
|
|
($requestedName ? '(alias: ' . $requestedName . ')' : '')
|
|
));
|
|
}
|
|
return $instance;
|
|
}
|
|
|
|
/**
|
|
* Attempt to create an instance via an abstract factory
|
|
*
|
|
* @param string $canonicalName
|
|
* @param string $requestedName
|
|
* @return object|null
|
|
* @throws Exception\ServiceNotCreatedException If abstract factory is not callable
|
|
*/
|
|
protected function createFromAbstractFactory($canonicalName, $requestedName)
|
|
{
|
|
if (isset($this->nestedContext[$canonicalName])) {
|
|
$abstractFactory = $this->nestedContext[$canonicalName];
|
|
$pendingKey = get_class($abstractFactory).$canonicalName;
|
|
try {
|
|
$this->pendingAbstractFactoryRequests[$pendingKey] = true;
|
|
$instance = $this->createServiceViaCallback(
|
|
array($abstractFactory, 'createServiceWithName'),
|
|
$canonicalName,
|
|
$requestedName
|
|
);
|
|
unset($this->pendingAbstractFactoryRequests[$pendingKey]);
|
|
return $instance;
|
|
} catch (\Exception $e) {
|
|
unset($this->pendingAbstractFactoryRequests[$pendingKey]);
|
|
$this->checkNestedContextStop(true);
|
|
throw new Exception\ServiceNotCreatedException(
|
|
sprintf(
|
|
'An abstract factory could not create an instance of %s%s.',
|
|
$canonicalName,
|
|
($requestedName ? '(alias: ' . $requestedName . ')' : '')
|
|
),
|
|
$e->getCode(),
|
|
$e
|
|
);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param string $cName
|
|
* @return self
|
|
*/
|
|
protected function checkNestedContextStart($cName)
|
|
{
|
|
if ($this->nestedContextCounter === -1 || !isset($this->nestedContext[$cName])) {
|
|
$this->nestedContext[$cName] = null;
|
|
}
|
|
$this->nestedContextCounter++;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param bool $force
|
|
* @return self
|
|
*/
|
|
protected function checkNestedContextStop($force = false)
|
|
{
|
|
if ($force) {
|
|
$this->nestedContextCounter = -1;
|
|
$this->nestedContext = array();
|
|
return $this;
|
|
}
|
|
|
|
$this->nestedContextCounter--;
|
|
if ($this->nestedContextCounter === -1) {
|
|
$this->nestedContext = array();
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param $canonicalName
|
|
* @param $requestedName
|
|
* @return mixed
|
|
* @throws Exception\ServiceNotCreatedException
|
|
*/
|
|
protected function createDelegatorFromFactory($canonicalName, $requestedName)
|
|
{
|
|
$serviceManager = $this;
|
|
$delegatorsCount = count($this->delegators[$canonicalName]);
|
|
$creationCallback = function () use ($serviceManager, $requestedName, $canonicalName) {
|
|
return $serviceManager->doCreate($requestedName, $canonicalName);
|
|
};
|
|
|
|
for ($i = 0; $i < $delegatorsCount; $i += 1) {
|
|
$delegatorFactory = $this->delegators[$canonicalName][$i];
|
|
|
|
if (is_string($delegatorFactory)) {
|
|
$delegatorFactory = !$this->has($delegatorFactory) && class_exists($delegatorFactory, true) ?
|
|
new $delegatorFactory
|
|
: $this->get($delegatorFactory);
|
|
$this->delegators[$canonicalName][$i] = $delegatorFactory;
|
|
}
|
|
|
|
if (!$delegatorFactory instanceof DelegatorFactoryInterface && !is_callable($delegatorFactory)) {
|
|
throw new Exception\ServiceNotCreatedException(sprintf(
|
|
'While attempting to create %s%s an invalid factory was registered for this instance type.',
|
|
$canonicalName,
|
|
($requestedName ? '(alias: ' . $requestedName . ')' : '')
|
|
));
|
|
}
|
|
|
|
$creationCallback = $this->createDelegatorCallback(
|
|
$delegatorFactory,
|
|
$requestedName,
|
|
$canonicalName,
|
|
$creationCallback
|
|
);
|
|
}
|
|
|
|
return $creationCallback($serviceManager, $canonicalName, $requestedName, $creationCallback);
|
|
}
|
|
|
|
/**
|
|
* Checks if the object has this class as one of its parents
|
|
*
|
|
* @see https://bugs.php.net/bug.php?id=53727
|
|
* @see https://github.com/zendframework/zf2/pull/1807
|
|
*
|
|
* @deprecated since zf 2.3 requires PHP >= 5.3.23
|
|
*
|
|
* @param string $className
|
|
* @param string $type
|
|
* @return bool
|
|
*
|
|
* @deprecated this method is being deprecated as of zendframework 2.2, and may be removed in future major versions
|
|
*/
|
|
protected static function isSubclassOf($className, $type)
|
|
{
|
|
return is_subclass_of($className, $type);
|
|
}
|
|
|
|
/**
|
|
* Unregister a service
|
|
*
|
|
* Called when $allowOverride is true and we detect that a service being
|
|
* added to the instance already exists. This will remove the duplicate
|
|
* entry, and also any shared flags previously registered.
|
|
*
|
|
* @param string $canonical
|
|
* @return void
|
|
*/
|
|
protected function unregisterService($canonical)
|
|
{
|
|
$types = array('invokableClasses', 'factories', 'aliases');
|
|
foreach ($types as $type) {
|
|
if (isset($this->{$type}[$canonical])) {
|
|
unset($this->{$type}[$canonical]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (isset($this->instances[$canonical])) {
|
|
unset($this->instances[$canonical]);
|
|
}
|
|
|
|
if (isset($this->shared[$canonical])) {
|
|
unset($this->shared[$canonical]);
|
|
}
|
|
}
|
|
}
|