<?php<liu21st@gmail.com>
namespace think;
use Closure;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use ReflectionMethod;
use think\exception\ClassNotFoundException;
class Container
{
protected static $instance;
protected $instances = [];
protected $bind = [];
protected $name = [];
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
public static function get($abstract, $vars = [], $newInstance = false)
{
return static::getInstance()->make($abstract, $vars, $newInstance);
}
public static function set($abstract, $concrete = null)
{
return static::getInstance()->bind($abstract, $concrete);
}
public static function remove($abstract)
{
return static::getInstance()->delete($abstract);
}
public static function clear()
{
return static::getInstance()->flush();
}
public function bind($abstract, $concrete = null)
{
if (is_array($abstract)) {
$this->bind = array_merge($this->bind, $abstract);
} elseif ($concrete instanceof Closure) {
$this->bind[$abstract] = $concrete;
} elseif (is_object($concrete)) {
$this->instances[$abstract] = $concrete;
} else {
$this->bind[$abstract] = $concrete;
}
return $this;
}
public function instance($abstract, $instance)
{
if (isset($this->bind[$abstract])) {
$abstract = $this->bind[$abstract];
}
$this->instances[$abstract] = $instance;
return $this;
}
public function bound($abstract)
{
return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
}
public function has($name)
{
return $this->bound($name);
}
public function make($abstract, $vars = [], $newInstance = false)
{
if (true === $vars) {
$newInstance = true;
$vars = [];
}
$abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
if (isset($this->instances[$abstract]) && !$newInstance) {
return $this->instances[$abstract];
}
if (isset($this->bind[$abstract])) {
$concrete = $this->bind[$abstract];
if ($concrete instanceof Closure) {
$object = $this->invokeFunction($concrete, $vars);
} else {
$this->name[$abstract] = $concrete;
return $this->make($concrete, $vars, $newInstance);
}
} else {
$object = $this->invokeClass($abstract, $vars);
}
if (!$newInstance) {
$this->instances[$abstract] = $object;
}
return $object;
}
public function delete($abstract)
{
foreach ((array) $abstract as $name) {
$name = isset($this->name[$name]) ? $this->name[$name] : $name;
if (isset($this->instances[$name])) {
unset($this->instances[$name]);
}
}
}
public function flush()
{
$this->instances = [];
$this->bind = [];
$this->name = [];
}
public function invokeFunction($function, $vars = [])
{
try {
$reflect = new ReflectionFunction($function);
$args = $this->bindParams($reflect, $vars);
return $reflect->invokeArgs($args);
} catch (ReflectionException $e) {
throw new Exception('function not exists: ' . $function . '()');
}
}
public function invokeMethod($method, $vars = [])
{
try {
if (is_array($method)) {
$class = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
$reflect = new ReflectionMethod($class, $method[1]);
} else {
$reflect = new ReflectionMethod($method);
}
$args = $this->bindParams($reflect, $vars);
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
} catch (ReflectionException $e) {
throw new Exception('method not exists: ' . (is_array($method) ? $method[0] . '::' . $method[1] : $method) . '()');
}
}
public function invokeReflectMethod($instance, $reflect, $vars = [])
{
$args = $this->bindParams($reflect, $vars);
return $reflect->invokeArgs($instance, $args);
}
public function invoke($callable, $vars = [])
{
if ($callable instanceof Closure) {
return $this->invokeFunction($callable, $vars);
}
return $this->invokeMethod($callable, $vars);
}
public function invokeClass($class, $vars = [])
{
try {
$reflect = new ReflectionClass($class);
$constructor = $reflect->getConstructor();
$args = $constructor ? $this->bindParams($constructor, $vars) : [];
return $reflect->newInstanceArgs($args);
} catch (ReflectionException $e) {
throw new ClassNotFoundException('class not exists: ' . $class, $class);
}
}
protected function bindParams($reflect, $vars = [])
{
if ($reflect->getNumberOfParameters() == 0) {
return [];
}
reset($vars);
$type = key($vars) === 0 ? 1 : 0;
$params = $reflect->getParameters();
foreach ($params as $param) {
$name = $param->getName();
$class = $param->getClass();
if ($class) {
$className = $class->getName();
$args[] = $this->make($className);
} elseif (1 == $type && !empty($vars)) {
$args[] = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$args[] = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new InvalidArgumentException('method param miss:' . $name);
}
}
return $args;
}
}