View file upload/src/addons/AddonsLab/Core/Container.php

File size: 6.79Kb
<?php
/** 
This software is furnished under a license and may be used and copied
only  in  accordance  with  the  terms  of such  license and with the
inclusion of the above copyright notice.  This software  or any other
copies thereof may not be provided or otherwise made available to any
other person.  No title to and  ownership of the  software is  hereby
transferred.                                                         
                                                                     
You may not reverse  engineer, decompile, defeat  license  encryption
mechanisms, or  disassemble this software product or software product
license.  AddonsLab may terminate this license if you don't comply with
any of these terms and conditions.  In such event,  licensee  agrees 
to return licensor  or destroy  all copies of software  upon termination 
of the license.
*/


namespace AddonsLab\Core;

/**
 * Dependency injection container and object type factory.
 * This is practically the duplicate of XF2 implementation
 * It allows us to use the same interface in XF 1.x and XF2.x versions for loading dependencies
 */
class Container implements \ArrayAccess
{
	protected $data = array();
	protected $cache = array();
	protected $cacheable = array();

	protected $factory = array();
	protected $factoryObjects = array();

	public function offsetGet($key)
	{
		if (array_key_exists($key, $this->cache))
		{
			return $this->cache[$key];
		}

		if (array_key_exists($key, $this->data))
		{
			$value = $this->data[$key];

			$output = ($this->isInvokable($value) ? $value($this) : $value);
			if (!empty($this->cacheable[$key]))
			{
				$this->cache[$key] = $output;
			}

			return $output;
		}

		throw new \InvalidArgumentException("Container key '$key' was not found");
	}

	public function isCached($key)
	{
		return array_key_exists($key, $this->cache);
	}

	public function decache($key)
	{
		unset($this->cache[$key]);
	}

	public function offsetSet($key, $value)
	{
		$this->set($key, $value);
	}

	public function offsetExists($key)
	{
		if (array_key_exists($key, $this->data))
		{
			return true;
		}

		return false;
	}

	public function offsetUnset($key)
	{
		unset($this->data[$key], $this->cache[$key], $this->cacheable[$key]);
	}

	public function __get($key)
	{
		return $this->offsetGet($key);
	}

	public function __set($key, $value)
	{
		$this->set($key, $value);
	}

	public function __isset($key)
	{
		return $this->offsetExists($key);
	}

	public function set($key, $value, $cache = null)
	{
		$this->data[$key] = $value;

		if ($cache === null)
		{
			$cache = $this->isInvokable($value);
		}
		$this->cacheable[$key] = (bool)$cache;
		unset($this->cache[$key]);
	}
	
	public function wrap($callable)
	{
		return function() use ($callable)
		{
			return $callable;
		};
	}

	public function extend($key, $callable)
	{
		if (!$this->isInvokable($callable))
		{
			throw new \InvalidArgumentException("Extension must be invokable");
		}

		if (!array_key_exists($key, $this->data))
		{
			throw new \InvalidArgumentException("Container key '$key' was not found");
		}

		$value = $this->data[$key];
		$this->data[$key] = function(Container $container) use ($value, $callable)
		{
			$output = ($container->isInvokable($value) ? $value($container) : $value);
			return $callable($output, $container);
		};

		if (array_key_exists($key, $this->cache))
		{
			// this has already been cached, so we need to run this now so that the
			// next call picks this up
			$this->cache[$key] = $callable($this->cache[$key], $this);
		}
	}

	public function getOriginal($key)
	{
		if (array_key_exists($key, $this->data))
		{
			return $this->data[$key];
		}

		throw new \InvalidArgumentException("Container key '$key' was not found");
	}

	public function factory($type, $callable, $cacheable = true)
	{
		if (!$this->isInvokable($callable))
		{
			throw new \InvalidArgumentException("Factory must be invokable");
		}

		$this->factory[$type] = array($callable, $cacheable);
	}

	public function extendFactory($type, $callable)
	{
		if (!$this->isInvokable($callable))
		{
			throw new \InvalidArgumentException("Extension must be invokable");
		}

		if (!isset($this->factory[$type]))
		{
			throw new \InvalidArgumentException("Factory type '$type' was not found");
		}

		$original = $this->factory[$type][0];
		$this->factory[$type][0] = function($class, array $params, Container $container) use ($original, $callable)
		{
			return $callable($class, $params, $container, $original);
		};
	}

	public function removeFactory($type)
	{
		unset($this->factory[$type], $this->factoryObjects[$type]);
	}

	public function create($type, $key, array $params = array())
	{
		if (!isset($this->factory[$type]))
		{
			throw new \InvalidArgumentException("Factory type '$type' was not found");
		}

		list($callable, $cacheable) = $this->factory[$type];

		if ($cacheable && isset($this->factoryObjects[$type][$key]))
		{
			return $this->factoryObjects[$type][$key];
		}

		$object = $callable($key, $params, $this);
		if ($cacheable)
		{
			$this->factoryObjects[$type][$key] = $object;
		}

		return $object;
	}

	public function getInvokableFactory($type)
	{
	    $obj=$this;
		return function($class, array $params = array()) use ($type, $obj)
		{
			return $obj->create($type, $class, $params);
		};
	}

	public function decacheFactory($type, $class = null)
	{
		if ($class)
		{
			unset($this->factoryObjects[$type][$class]);
		}
		else
		{
			unset($this->factoryObjects[$type]);
		}
	}

	public function createObject($class, array $params = array(), $failSilently = false)
	{
		$params = array_values($params);

		if (!class_exists($class))
		{
			if ($failSilently)
			{
				return null;
			}
			throw new \LogicException("Class $class does not exist");
		}

		switch (count($params))
		{
			case 0: return new $class();
			case 1: return new $class($params[0]);
			case 2: return new $class($params[0], $params[1]);
			case 3: return new $class($params[0], $params[1], $params[2]);
			case 4: return new $class($params[0], $params[1], $params[2], $params[3]);
			case 5: return new $class($params[0], $params[1], $params[2], $params[3], $params[4]);

			default:
				$reflection = new \ReflectionClass($class);
				return $reflection->newInstanceArgs($params);
		}
	}

	protected function isInvokable($value)
	{
		return is_object($value) && method_exists($value, '__invoke');
	}

	public function __sleep()
	{
		throw new \LogicException('Instances of ' . __CLASS__ . ' cannot be serialized or unserialized');
	}

	public function __wakeup()
	{
		throw new \LogicException('Instances of ' . __CLASS__ . ' cannot be serialized or unserialized');
	}
}