- <?php
- /**
- * Zend Framework (http://framework.zend.com/)
- *
- * @link http://github.com/zendframework/zf2 for the canonical source repository
- * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
-
- namespace Zend\Di;
-
- /**
- * Registry of instantiated objects, their names and the parameters used to build them
- */
- class InstanceManager /* implements InstanceManagerInterface */
- {
- /**
- * Array of shared instances
- * @var array
- */
- protected $sharedInstances = array();
-
- /**
- * Array of shared instances with params
- * @var array
- */
- protected $sharedInstancesWithParams = array('hashShort' => array(), 'hashLong' => array());
-
- /**
- * Array of class aliases
- * @var array key: alias, value: class
- */
- protected $aliases = array();
-
- /**
- * The template to use for housing configuration information
- * @var array
- */
- protected $configurationTemplate = array(
- /**
- * alias|class => alias|class
- * interface|abstract => alias|class|object
- * name => value
- */
- 'parameters' => array(),
- /**
- * injection type => array of ordered method params
- */
- 'injections' => array(),
- /**
- * alias|class => bool
- */
- 'shared' => true
- );
-
- /**
- * An array of instance configuration data
- * @var array
- */
- protected $configurations = array();
-
- /**
- * An array of globally preferred implementations for interfaces/abstracts
- * @var array
- */
- protected $typePreferences = array();
-
- /**
- * Does this instance manager have this shared instance
- * @param string $classOrAlias
- * @return bool
- */
- public function hasSharedInstance($classOrAlias)
- {
- return isset($this->sharedInstances[$classOrAlias]);
- }
-
- /**
- * getSharedInstance()
- */
- public function getSharedInstance($classOrAlias)
- {
- return $this->sharedInstances[$classOrAlias];
- }
-
- /**
- * Add shared instance
- *
- * @param object $instance
- * @param string $classOrAlias
- * @throws Exception\InvalidArgumentException
- */
- public function addSharedInstance($instance, $classOrAlias)
- {
- if (!is_object($instance)) {
- throw new Exception\InvalidArgumentException('This method requires an object to be shared. Class or Alias given: ' . $classOrAlias);
- }
-
- $this->sharedInstances[$classOrAlias] = $instance;
- }
-
- /**
- * hasSharedInstanceWithParameters()
- *
- * @param string $classOrAlias
- * @param array $params
- * @param bool $returnFastHashLookupKey
- * @return bool|string
- */
- public function hasSharedInstanceWithParameters($classOrAlias, array $params, $returnFastHashLookupKey = false)
- {
- ksort($params);
- $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params));
- if (isset($this->sharedInstancesWithParams['hashShort'][$hashKey])) {
- $hashValue = $this->createHashForValues($classOrAlias, $params);
- if (isset($this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue])) {
- return ($returnFastHashLookupKey) ? $hashKey . '/' . $hashValue : true;
- }
- }
-
- return false;
- }
-
- /**
- * addSharedInstanceWithParameters()
- *
- * @param object $instance
- * @param string $classOrAlias
- * @param array $params
- * @return void
- */
- public function addSharedInstanceWithParameters($instance, $classOrAlias, array $params)
- {
- ksort($params);
- $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params));
- $hashValue = $this->createHashForValues($classOrAlias, $params);
-
- if (!isset($this->sharedInstancesWithParams[$hashKey])
- || !is_array($this->sharedInstancesWithParams[$hashKey])) {
- $this->sharedInstancesWithParams[$hashKey] = array();
- }
-
- $this->sharedInstancesWithParams['hashShort'][$hashKey] = true;
- $this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue] = $instance;
- }
-
- /**
- * Retrieves an instance by its name and the parameters stored at its instantiation
- *
- * @param string $classOrAlias
- * @param array $params
- * @param bool|null $fastHashFromHasLookup
- * @return object|bool false if no instance was found
- */
- public function getSharedInstanceWithParameters($classOrAlias, array $params, $fastHashFromHasLookup = null)
- {
- if ($fastHashFromHasLookup) {
- return $this->sharedInstancesWithParams['hashLong'][$fastHashFromHasLookup];
- }
-
- ksort($params);
- $hashKey = $this->createHashForKeys($classOrAlias, array_keys($params));
- if (isset($this->sharedInstancesWithParams['hashShort'][$hashKey])) {
- $hashValue = $this->createHashForValues($classOrAlias, $params);
- if (isset($this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue])) {
- return $this->sharedInstancesWithParams['hashLong'][$hashKey . '/' . $hashValue];
- }
- }
-
- return false;
- }
-
- /**
- * Check for an alias
- *
- * @param string $alias
- * @return bool
- */
- public function hasAlias($alias)
- {
- return (isset($this->aliases[$alias]));
- }
-
- /**
- * Get aliases
- *
- * @return array
- */
- public function getAliases()
- {
- return $this->aliases;
- }
-
- /**
- * getClassFromAlias()
- *
- * @param string
- * @return string|bool
- * @throws Exception\RuntimeException
- */
- public function getClassFromAlias($alias)
- {
- if (!isset($this->aliases[$alias])) {
- return false;
- }
- $r = 0;
- while (isset($this->aliases[$alias])) {
- $alias = $this->aliases[$alias];
- $r++;
- if ($r > 100) {
- throw new Exception\RuntimeException(
- sprintf('Possible infinite recursion in DI alias! Max recursion of 100 levels reached at alias "%s".', $alias)
- );
- }
- }
-
- return $alias;
- }
-
- /**
- * @param string $alias
- * @return string|bool
- * @throws Exception\RuntimeException
- */
- protected function getBaseAlias($alias)
- {
- if (!$this->hasAlias($alias)) {
- return false;
- }
- $lastAlias = false;
- $r = 0;
- while (isset($this->aliases[$alias])) {
- $lastAlias = $alias;
- $alias = $this->aliases[$alias];
- $r++;
- if ($r > 100) {
- throw new Exception\RuntimeException(
- sprintf('Possible infinite recursion in DI alias! Max recursion of 100 levels reached at alias "%s".', $alias)
- );
- }
- }
-
- return $lastAlias;
- }
-
- /**
- * Add alias
- *
- * @throws Exception\InvalidArgumentException
- * @param string $alias
- * @param string $class
- * @param array $parameters
- * @return void
- */
- public function addAlias($alias, $class, array $parameters = array())
- {
- if (!preg_match('#^[a-zA-Z0-9-_]+$#', $alias)) {
- throw new Exception\InvalidArgumentException(
- 'Aliases must be alphanumeric and can contain dashes and underscores only.'
- );
- }
- $this->aliases[$alias] = $class;
- if ($parameters) {
- $this->setParameters($alias, $parameters);
- }
- }
-
- /**
- * Check for configuration
- *
- * @param string $aliasOrClass
- * @return bool
- */
- public function hasConfig($aliasOrClass)
- {
- $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $this->getBaseAlias($aliasOrClass) : $aliasOrClass;
- if (!isset($this->configurations[$key])) {
- return false;
- }
- if ($this->configurations[$key] === $this->configurationTemplate) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Sets configuration for a single alias/class
- *
- * @param string $aliasOrClass
- * @param array $configuration
- * @param bool $append
- */
- public function setConfig($aliasOrClass, array $configuration, $append = false)
- {
- $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $this->getBaseAlias($aliasOrClass) : $aliasOrClass;
- if (!isset($this->configurations[$key]) || !$append) {
- $this->configurations[$key] = $this->configurationTemplate;
- }
- // Ignore anything but 'parameters' and 'injections'
- $configuration = array(
- 'parameters' => isset($configuration['parameters']) ? $configuration['parameters'] : array(),
- 'injections' => isset($configuration['injections']) ? $configuration['injections'] : array(),
- 'shared' => isset($configuration['shared']) ? $configuration['shared'] : true
- );
- $this->configurations[$key] = array_replace_recursive($this->configurations[$key], $configuration);
- }
-
- /**
- * Get classes
- *
- * @return array
- */
- public function getClasses()
- {
- $classes = array();
- foreach ($this->configurations as $name => $data) {
- if (strpos($name, 'alias') === 0) continue;
- $classes[] = $name;
- }
-
- return $classes;
- }
-
- /**
- * @param string $aliasOrClass
- * @return array
- */
- public function getConfig($aliasOrClass)
- {
- $key = ($this->hasAlias($aliasOrClass)) ? 'alias:' . $this->getBaseAlias($aliasOrClass) : $aliasOrClass;
- if (isset($this->configurations[$key])) {
- return $this->configurations[$key];
- }
-
- return $this->configurationTemplate;
- }
-
- /**
- * setParameters() is a convenience method for:
- * setConfig($type, array('parameters' => array(...)), true);
- *
- * @param string $aliasOrClass Alias or Class
- * @param array $parameters Multi-dim array of parameters and their values
- * @return void
- */
- public function setParameters($aliasOrClass, array $parameters)
- {
- $this->setConfig($aliasOrClass, array('parameters' => $parameters), true);
- }
-
- /**
- * setInjections() is a convenience method for:
- * setConfig($type, array('injections' => array(...)), true);
- *
- * @param string $aliasOrClass Alias or Class
- * @param array $injections Multi-dim array of methods and their parameters
- * @return void
- */
- public function setInjections($aliasOrClass, array $injections)
- {
- $this->setConfig($aliasOrClass, array('injections' => $injections), true);
- }
-
- /**
- * Set shared
- *
- * @param string $aliasOrClass
- * @param bool $isShared
- * @return void
- */
- public function setShared($aliasOrClass, $isShared)
- {
- $this->setConfig($aliasOrClass, array('shared' => (bool) $isShared), true);
- }
-
- /**
- * Check for type preferences
- *
- * @param string $interfaceOrAbstract
- * @return bool
- */
- public function hasTypePreferences($interfaceOrAbstract)
- {
- $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
-
- return (isset($this->typePreferences[$key]) && $this->typePreferences[$key]);
- }
-
- /**
- * Set type preference
- *
- * @param string $interfaceOrAbstract
- * @param array $preferredImplementations
- * @return InstanceManager
- */
- public function setTypePreference($interfaceOrAbstract, array $preferredImplementations)
- {
- $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
- foreach ($preferredImplementations as $preferredImplementation) {
- $this->addTypePreference($key, $preferredImplementation);
- }
-
- return $this;
- }
-
- /**
- * Get type preferences
- *
- * @param string $interfaceOrAbstract
- * @return array
- */
- public function getTypePreferences($interfaceOrAbstract)
- {
- $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
- if (isset($this->typePreferences[$key])) {
- return $this->typePreferences[$key];
- }
-
- return array();
- }
-
- /**
- * Unset type preferences
- *
- * @param string $interfaceOrAbstract
- * @return void
- */
- public function unsetTypePreferences($interfaceOrAbstract)
- {
- $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
- unset($this->typePreferences[$key]);
- }
-
- /**
- * Adds a type preference. A type preference is a redirection to a preferred alias or type when an abstract type
- * $interfaceOrAbstract is requested
- *
- * @param string $interfaceOrAbstract
- * @param string $preferredImplementation
- * @return self
- */
- public function addTypePreference($interfaceOrAbstract, $preferredImplementation)
- {
- $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
- if (!isset($this->typePreferences[$key])) {
- $this->typePreferences[$key] = array();
- }
- $this->typePreferences[$key][] = $preferredImplementation;
-
- return $this;
- }
-
- /**
- * Removes a previously set type preference
- *
- * @param string $interfaceOrAbstract
- * @param string $preferredType
- * @return bool|self
- */
- public function removeTypePreference($interfaceOrAbstract, $preferredType)
- {
- $key = ($this->hasAlias($interfaceOrAbstract)) ? 'alias:' . $interfaceOrAbstract : $interfaceOrAbstract;
- if (!isset($this->typePreferences[$key]) || !in_array($preferredType, $this->typePreferences[$key])) {
- return false;
- }
- unset($this->typePreferences[$key][array_search($key, $this->typePreferences)]);
-
- return $this;
- }
-
- /**
- * @param string $classOrAlias
- * @param string[] $paramKeys
- * @return string
- */
- protected function createHashForKeys($classOrAlias, $paramKeys)
- {
- return $classOrAlias . ':' . implode('|', $paramKeys);
- }
-
- /**
- * @param string $classOrAlias
- * @param array $paramValues
- * @return string
- */
- protected function createHashForValues($classOrAlias, $paramValues)
- {
- $hashValue = '';
- foreach ($paramValues as $param) {
- switch (gettype($param)) {
- case 'object':
- $hashValue .= spl_object_hash($param) . '|';
- break;
- case 'integer':
- case 'string':
- case 'boolean':
- case 'NULL':
- case 'double':
- $hashValue .= $param . '|';
- break;
- case 'array':
- $hashValue .= 'Array|';
- break;
- case 'resource':
- $hashValue .= 'resource|';
- break;
- }
- }
-
- return $hashValue;
- }
- }