torrentpier-lts/library/Zend/Di/InstanceManager.php

517 lines
15 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\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;
}
}