Container.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2023 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. declare(strict_types=1);
  12. namespace think;
  13. use ArrayAccess;
  14. use ArrayIterator;
  15. use Closure;
  16. use Countable;
  17. use InvalidArgumentException;
  18. use IteratorAggregate;
  19. use Psr\Container\ContainerInterface;
  20. use ReflectionClass;
  21. use ReflectionException;
  22. use ReflectionNamedType;
  23. use ReflectionFunction;
  24. use ReflectionFunctionAbstract;
  25. use ReflectionMethod;
  26. use think\exception\ClassNotFoundException;
  27. use think\exception\FuncNotFoundException;
  28. use think\helper\Str;
  29. use Traversable;
  30. /**
  31. * 容器管理类 支持PSR-11
  32. */
  33. class Container implements ContainerInterface, ArrayAccess, IteratorAggregate, Countable
  34. {
  35. /**
  36. * 容器对象实例
  37. * @var Container|Closure
  38. */
  39. protected static $instance;
  40. /**
  41. * 容器中的对象实例
  42. * @var array
  43. */
  44. protected $instances = [];
  45. /**
  46. * 容器绑定标识
  47. * @var array
  48. */
  49. protected $bind = [];
  50. /**
  51. * 容器回调
  52. * @var array
  53. */
  54. protected $invokeCallback = [];
  55. /**
  56. * 获取当前容器的实例(单例)
  57. * @access public
  58. * @return static
  59. */
  60. public static function getInstance()
  61. {
  62. if (is_null(static::$instance)) {
  63. static::$instance = new static;
  64. }
  65. if (static::$instance instanceof Closure) {
  66. return (static::$instance)();
  67. }
  68. return static::$instance;
  69. }
  70. /**
  71. * 设置当前容器的实例
  72. * @access public
  73. * @param object|Closure $instance
  74. * @return void
  75. */
  76. public static function setInstance($instance): void
  77. {
  78. static::$instance = $instance;
  79. }
  80. /**
  81. * 注册一个容器对象回调
  82. *
  83. * @param string|Closure $abstract
  84. * @param Closure|null $callback
  85. * @return void
  86. */
  87. public function resolving(string|Closure $abstract, Closure $callback = null): void
  88. {
  89. if ($abstract instanceof Closure) {
  90. $this->invokeCallback['*'][] = $abstract;
  91. return;
  92. }
  93. $abstract = $this->getAlias($abstract);
  94. $this->invokeCallback[$abstract][] = $callback;
  95. }
  96. /**
  97. * 获取容器中的对象实例 不存在则创建
  98. * @template T
  99. * @param string|class-string<T> $abstract 类名或者标识
  100. * @param array $vars 变量
  101. * @param bool $newInstance 是否每次创建新的实例
  102. * @return T|object
  103. */
  104. public static function pull(string $abstract, array $vars = [], bool $newInstance = false)
  105. {
  106. return static::getInstance()->make($abstract, $vars, $newInstance);
  107. }
  108. /**
  109. * 获取容器中的对象实例
  110. * @template T
  111. * @param string|class-string<T> $abstract 类名或者标识
  112. * @return T|object
  113. */
  114. public function get(string $abstract)
  115. {
  116. if ($this->has($abstract)) {
  117. return $this->make($abstract);
  118. }
  119. throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
  120. }
  121. /**
  122. * 绑定一个类、闭包、实例、接口实现到容器
  123. * @access public
  124. * @param string|array $abstract 类标识、接口
  125. * @param mixed $concrete 要绑定的类、闭包或者实例
  126. * @return $this
  127. */
  128. public function bind(string|array $abstract, $concrete = null)
  129. {
  130. if (is_array($abstract)) {
  131. foreach ($abstract as $key => $val) {
  132. $this->bind($key, $val);
  133. }
  134. } elseif ($concrete instanceof Closure) {
  135. $this->bind[$abstract] = $concrete;
  136. } elseif (is_object($concrete)) {
  137. $this->instance($abstract, $concrete);
  138. } else {
  139. $abstract = $this->getAlias($abstract);
  140. if ($abstract != $concrete) {
  141. $this->bind[$abstract] = $concrete;
  142. }
  143. }
  144. return $this;
  145. }
  146. /**
  147. * 根据别名获取真实类名
  148. * @param string $abstract
  149. * @return string
  150. */
  151. public function getAlias(string $abstract): string
  152. {
  153. if (isset($this->bind[$abstract])) {
  154. $bind = $this->bind[$abstract];
  155. if (is_string($bind)) {
  156. return $this->getAlias($bind);
  157. }
  158. }
  159. return $abstract;
  160. }
  161. /**
  162. * 绑定一个类实例到容器
  163. * @access public
  164. * @param string $abstract 类名或者标识
  165. * @param object $instance 类的实例
  166. * @return $this
  167. */
  168. public function instance(string $abstract, $instance)
  169. {
  170. $abstract = $this->getAlias($abstract);
  171. $this->instances[$abstract] = $instance;
  172. return $this;
  173. }
  174. /**
  175. * 判断容器中是否存在类及标识
  176. * @access public
  177. * @param string $abstract 类名或者标识
  178. * @return bool
  179. */
  180. public function bound(string $abstract): bool
  181. {
  182. return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
  183. }
  184. /**
  185. * 判断容器中是否存在类及标识
  186. * @access public
  187. * @param string $name 类名或者标识
  188. * @return bool
  189. */
  190. public function has(string $name): bool
  191. {
  192. return $this->bound($name);
  193. }
  194. /**
  195. * 判断容器中是否存在对象实例
  196. * @access public
  197. * @param string $abstract 类名或者标识
  198. * @return bool
  199. */
  200. public function exists(string $abstract): bool
  201. {
  202. $abstract = $this->getAlias($abstract);
  203. return isset($this->instances[$abstract]);
  204. }
  205. /**
  206. * 创建类的实例 已经存在则直接获取
  207. * @template T
  208. * @param string|class-string<T> $abstract 类名或者标识
  209. * @param array $vars 变量
  210. * @param bool $newInstance 是否每次创建新的实例
  211. * @return T|object
  212. */
  213. public function make(string $abstract, array $vars = [], bool $newInstance = false)
  214. {
  215. $abstract = $this->getAlias($abstract);
  216. if (isset($this->instances[$abstract]) && !$newInstance) {
  217. return $this->instances[$abstract];
  218. }
  219. if (isset($this->bind[$abstract]) && $this->bind[$abstract] instanceof Closure) {
  220. $object = $this->invokeFunction($this->bind[$abstract], $vars);
  221. } else {
  222. $object = $this->invokeClass($abstract, $vars);
  223. }
  224. if (!$newInstance) {
  225. $this->instances[$abstract] = $object;
  226. }
  227. return $object;
  228. }
  229. /**
  230. * 删除容器中的对象实例
  231. * @access public
  232. * @param string $name 类名或者标识
  233. * @return void
  234. */
  235. public function delete(string $name)
  236. {
  237. $name = $this->getAlias($name);
  238. if (isset($this->instances[$name])) {
  239. unset($this->instances[$name]);
  240. }
  241. }
  242. /**
  243. * 执行函数或者闭包方法 支持参数调用
  244. * @access public
  245. * @param string|Closure $function 函数或者闭包
  246. * @param array $vars 参数
  247. * @return mixed
  248. */
  249. public function invokeFunction(string|Closure $function, array $vars = [])
  250. {
  251. try {
  252. $reflect = new ReflectionFunction($function);
  253. } catch (ReflectionException $e) {
  254. throw new FuncNotFoundException("function not exists: {$function}()", $function, $e);
  255. }
  256. $args = $this->bindParams($reflect, $vars);
  257. return $function(...$args);
  258. }
  259. /**
  260. * 调用反射执行类的方法 支持参数绑定
  261. * @access public
  262. * @param mixed $method 方法
  263. * @param array $vars 参数
  264. * @param bool $accessible 设置是否可访问
  265. * @return mixed
  266. */
  267. public function invokeMethod($method, array $vars = [], bool $accessible = false)
  268. {
  269. if (is_array($method)) {
  270. [$class, $method] = $method;
  271. $class = is_object($class) ? $class : $this->invokeClass($class);
  272. } else {
  273. // 静态方法
  274. [$class, $method] = explode('::', $method);
  275. }
  276. try {
  277. $reflect = new ReflectionMethod($class, $method);
  278. } catch (ReflectionException $e) {
  279. $class = is_object($class) ? $class::class : $class;
  280. throw new FuncNotFoundException('method not exists: ' . $class . '::' . $method . '()', "{$class}::{$method}", $e);
  281. }
  282. $args = $this->bindParams($reflect, $vars);
  283. if ($accessible) {
  284. $reflect->setAccessible($accessible);
  285. }
  286. return $reflect->invokeArgs(is_object($class) ? $class : null, $args);
  287. }
  288. /**
  289. * 调用反射执行类的方法 支持参数绑定
  290. * @access public
  291. * @param object $instance 对象实例
  292. * @param mixed $reflect 反射类
  293. * @param array $vars 参数
  294. * @return mixed
  295. */
  296. public function invokeReflectMethod($instance, $reflect, array $vars = [])
  297. {
  298. $args = $this->bindParams($reflect, $vars);
  299. return $reflect->invokeArgs($instance, $args);
  300. }
  301. /**
  302. * 调用反射执行callable 支持参数绑定
  303. * @access public
  304. * @param mixed $callable
  305. * @param array $vars 参数
  306. * @param bool $accessible 设置是否可访问
  307. * @return mixed
  308. */
  309. public function invoke($callable, array $vars = [], bool $accessible = false)
  310. {
  311. if ($callable instanceof Closure) {
  312. return $this->invokeFunction($callable, $vars);
  313. } elseif (is_string($callable) && !str_contains($callable, '::')) {
  314. return $this->invokeFunction($callable, $vars);
  315. } else {
  316. return $this->invokeMethod($callable, $vars, $accessible);
  317. }
  318. }
  319. /**
  320. * 调用反射执行类的实例化 支持依赖注入
  321. * @access public
  322. * @param string $class 类名
  323. * @param array $vars 参数
  324. * @return mixed
  325. */
  326. public function invokeClass(string $class, array $vars = [])
  327. {
  328. try {
  329. $reflect = new ReflectionClass($class);
  330. } catch (ReflectionException $e) {
  331. throw new ClassNotFoundException('class not exists: ' . $class, $class, $e);
  332. }
  333. if ($reflect->hasMethod('__make')) {
  334. $method = $reflect->getMethod('__make');
  335. if ($method->isPublic() && $method->isStatic()) {
  336. $args = $this->bindParams($method, $vars);
  337. $object = $method->invokeArgs(null, $args);
  338. $this->invokeAfter($class, $object);
  339. return $object;
  340. }
  341. }
  342. $constructor = $reflect->getConstructor();
  343. $args = $constructor ? $this->bindParams($constructor, $vars) : [];
  344. $object = $reflect->newInstanceArgs($args);
  345. $this->invokeAfter($class, $object);
  346. return $object;
  347. }
  348. /**
  349. * 执行invokeClass回调
  350. * @access protected
  351. * @param string $class 对象类名
  352. * @param object $object 容器对象实例
  353. * @return void
  354. */
  355. protected function invokeAfter(string $class, $object): void
  356. {
  357. if (isset($this->invokeCallback['*'])) {
  358. foreach ($this->invokeCallback['*'] as $callback) {
  359. $callback($object, $this);
  360. }
  361. }
  362. if (isset($this->invokeCallback[$class])) {
  363. foreach ($this->invokeCallback[$class] as $callback) {
  364. $callback($object, $this);
  365. }
  366. }
  367. }
  368. /**
  369. * 绑定参数
  370. * @access protected
  371. * @param ReflectionFunctionAbstract $reflect 反射类
  372. * @param array $vars 参数
  373. * @return array
  374. */
  375. protected function bindParams(ReflectionFunctionAbstract $reflect, array $vars = []): array
  376. {
  377. if ($reflect->getNumberOfParameters() == 0) {
  378. return [];
  379. }
  380. // 判断数组类型 数字数组时按顺序绑定参数
  381. reset($vars);
  382. $type = key($vars) === 0 ? 1 : 0;
  383. $params = $reflect->getParameters();
  384. $args = [];
  385. foreach ($params as $param) {
  386. $name = $param->getName();
  387. $lowerName = Str::snake($name);
  388. $reflectionType = $param->getType();
  389. if ($param->isVariadic()) {
  390. return array_merge($args, array_values($vars));
  391. } elseif ($reflectionType && $reflectionType instanceof ReflectionNamedType && $reflectionType->isBuiltin() === false) {
  392. $args[] = $this->getObjectParam($reflectionType->getName(), $vars);
  393. } elseif (1 == $type && !empty($vars)) {
  394. $args[] = array_shift($vars);
  395. } elseif (0 == $type && array_key_exists($name, $vars)) {
  396. $args[] = $vars[$name];
  397. } elseif (0 == $type && array_key_exists($lowerName, $vars)) {
  398. $args[] = $vars[$lowerName];
  399. } elseif ($param->isDefaultValueAvailable()) {
  400. $args[] = $param->getDefaultValue();
  401. } else {
  402. throw new InvalidArgumentException('method param miss:' . $name);
  403. }
  404. }
  405. return $args;
  406. }
  407. /**
  408. * 创建工厂对象实例
  409. * @param string $name 工厂类名
  410. * @param string $namespace 默认命名空间
  411. * @param array $args
  412. * @return mixed
  413. * @deprecated
  414. * @access public
  415. */
  416. public static function factory(string $name, string $namespace = '', ...$args)
  417. {
  418. $class = str_contains($name, '\\') ? $name : $namespace . ucwords($name);
  419. return Container::getInstance()->invokeClass($class, $args);
  420. }
  421. /**
  422. * 获取对象类型的参数值
  423. * @access protected
  424. * @param string $className 类名
  425. * @param array $vars 参数
  426. * @return mixed
  427. */
  428. protected function getObjectParam(string $className, array &$vars)
  429. {
  430. $array = $vars;
  431. $value = array_shift($array);
  432. if ($value instanceof $className) {
  433. $result = $value;
  434. array_shift($vars);
  435. } else {
  436. $result = $this->make($className);
  437. }
  438. return $result;
  439. }
  440. public function __set($name, $value)
  441. {
  442. $this->bind($name, $value);
  443. }
  444. public function __get($name)
  445. {
  446. return $this->get($name);
  447. }
  448. public function __isset($name): bool
  449. {
  450. return $this->exists($name);
  451. }
  452. public function __unset($name)
  453. {
  454. $this->delete($name);
  455. }
  456. public function offsetExists(mixed $key): bool
  457. {
  458. return $this->exists($key);
  459. }
  460. public function offsetGet(mixed $key): mixed
  461. {
  462. return $this->make($key);
  463. }
  464. public function offsetSet(mixed $key, mixed $value): void
  465. {
  466. $this->bind($key, $value);
  467. }
  468. public function offsetUnset(mixed $key): void
  469. {
  470. $this->delete($key);
  471. }
  472. //Countable
  473. public function count(): int
  474. {
  475. return count($this->instances);
  476. }
  477. //IteratorAggregate
  478. public function getIterator(): Traversable
  479. {
  480. return new ArrayIterator($this->instances);
  481. }
  482. }