Container.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2018 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. namespace think;
  12. use ArrayAccess;
  13. use ArrayIterator;
  14. use Closure;
  15. use Countable;
  16. use InvalidArgumentException;
  17. use IteratorAggregate;
  18. use ReflectionClass;
  19. use ReflectionException;
  20. use ReflectionFunction;
  21. use ReflectionMethod;
  22. use think\exception\ClassNotFoundException;
  23. /**
  24. * @package think
  25. * @property Build $build
  26. * @property Cache $cache
  27. * @property Config $config
  28. * @property Cookie $cookie
  29. * @property Debug $debug
  30. * @property Env $env
  31. * @property Hook $hook
  32. * @property Lang $lang
  33. * @property Middleware $middleware
  34. * @property Request $request
  35. * @property Response $response
  36. * @property Route $route
  37. * @property Session $session
  38. * @property Template $template
  39. * @property Url $url
  40. * @property Validate $validate
  41. * @property View $view
  42. * @property route\RuleName $rule_name
  43. * @property Log $log
  44. */
  45. class Container implements ArrayAccess, IteratorAggregate, Countable
  46. {
  47. /**
  48. * 容器对象实例
  49. * @var Container
  50. */
  51. protected static $instance;
  52. /**
  53. * 容器中的对象实例
  54. * @var array
  55. */
  56. protected $instances = [];
  57. /**
  58. * 容器绑定标识
  59. * @var array
  60. */
  61. protected $bind = [
  62. 'app' => App::class,
  63. 'build' => Build::class,
  64. 'cache' => Cache::class,
  65. 'config' => Config::class,
  66. 'cookie' => Cookie::class,
  67. 'debug' => Debug::class,
  68. 'env' => Env::class,
  69. 'hook' => Hook::class,
  70. 'lang' => Lang::class,
  71. 'log' => Log::class,
  72. 'middleware' => Middleware::class,
  73. 'request' => Request::class,
  74. 'response' => Response::class,
  75. 'route' => Route::class,
  76. 'session' => Session::class,
  77. 'template' => Template::class,
  78. 'url' => Url::class,
  79. 'validate' => Validate::class,
  80. 'view' => View::class,
  81. 'rule_name' => route\RuleName::class,
  82. // 接口依赖注入
  83. 'think\LoggerInterface' => Log::class,
  84. ];
  85. /**
  86. * 容器标识别名
  87. * @var array
  88. */
  89. protected $name = [];
  90. /**
  91. * 获取当前容器的实例(单例)
  92. * @access public
  93. * @return static
  94. */
  95. public static function getInstance()
  96. {
  97. if (is_null(static::$instance)) {
  98. static::$instance = new static;
  99. }
  100. return static::$instance;
  101. }
  102. /**
  103. * 设置当前容器的实例
  104. * @access public
  105. * @param object $instance
  106. * @return void
  107. */
  108. public static function setInstance($instance)
  109. {
  110. static::$instance = $instance;
  111. }
  112. /**
  113. * 获取容器中的对象实例
  114. * @access public
  115. * @param string $abstract 类名或者标识
  116. * @param array|true $vars 变量
  117. * @param bool $newInstance 是否每次创建新的实例
  118. * @return object
  119. */
  120. public static function get($abstract, $vars = [], $newInstance = false)
  121. {
  122. return static::getInstance()->make($abstract, $vars, $newInstance);
  123. }
  124. /**
  125. * 绑定一个类、闭包、实例、接口实现到容器
  126. * @access public
  127. * @param string $abstract 类标识、接口
  128. * @param mixed $concrete 要绑定的类、闭包或者实例
  129. * @return Container
  130. */
  131. public static function set($abstract, $concrete = null)
  132. {
  133. return static::getInstance()->bindTo($abstract, $concrete);
  134. }
  135. /**
  136. * 移除容器中的对象实例
  137. * @access public
  138. * @param string $abstract 类标识、接口
  139. * @return void
  140. */
  141. public static function remove($abstract)
  142. {
  143. return static::getInstance()->delete($abstract);
  144. }
  145. /**
  146. * 清除容器中的对象实例
  147. * @access public
  148. * @return void
  149. */
  150. public static function clear()
  151. {
  152. return static::getInstance()->flush();
  153. }
  154. /**
  155. * 绑定一个类、闭包、实例、接口实现到容器
  156. * @access public
  157. * @param string|array $abstract 类标识、接口
  158. * @param mixed $concrete 要绑定的类、闭包或者实例
  159. * @return $this
  160. */
  161. public function bindTo($abstract, $concrete = null)
  162. {
  163. if (is_array($abstract)) {
  164. $this->bind = array_merge($this->bind, $abstract);
  165. } elseif ($concrete instanceof Closure) {
  166. $this->bind[$abstract] = $concrete;
  167. } elseif (is_object($concrete)) {
  168. if (isset($this->bind[$abstract])) {
  169. $abstract = $this->bind[$abstract];
  170. }
  171. $this->instances[$abstract] = $concrete;
  172. } else {
  173. $this->bind[$abstract] = $concrete;
  174. }
  175. return $this;
  176. }
  177. /**
  178. * 绑定一个类实例当容器
  179. * @access public
  180. * @param string $abstract 类名或者标识
  181. * @param object|\Closure $instance 类的实例
  182. * @return $this
  183. */
  184. public function instance($abstract, $instance)
  185. {
  186. if ($instance instanceof \Closure) {
  187. $this->bind[$abstract] = $instance;
  188. } else {
  189. if (isset($this->bind[$abstract])) {
  190. $abstract = $this->bind[$abstract];
  191. }
  192. $this->instances[$abstract] = $instance;
  193. }
  194. return $this;
  195. }
  196. /**
  197. * 判断容器中是否存在类及标识
  198. * @access public
  199. * @param string $abstract 类名或者标识
  200. * @return bool
  201. */
  202. public function bound($abstract)
  203. {
  204. return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
  205. }
  206. /**
  207. * 判断容器中是否存在对象实例
  208. * @access public
  209. * @param string $abstract 类名或者标识
  210. * @return bool
  211. */
  212. public function exists($abstract)
  213. {
  214. if (isset($this->bind[$abstract])) {
  215. $abstract = $this->bind[$abstract];
  216. }
  217. return isset($this->instances[$abstract]);
  218. }
  219. /**
  220. * 判断容器中是否存在类及标识
  221. * @access public
  222. * @param string $name 类名或者标识
  223. * @return bool
  224. */
  225. public function has($name)
  226. {
  227. return $this->bound($name);
  228. }
  229. /**
  230. * 创建类的实例
  231. * @access public
  232. * @param string $abstract 类名或者标识
  233. * @param array|true $vars 变量
  234. * @param bool $newInstance 是否每次创建新的实例
  235. * @return object
  236. */
  237. public function make($abstract, $vars = [], $newInstance = false)
  238. {
  239. if (true === $vars) {
  240. // 总是创建新的实例化对象
  241. $newInstance = true;
  242. $vars = [];
  243. }
  244. $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
  245. if (isset($this->instances[$abstract]) && !$newInstance) {
  246. return $this->instances[$abstract];
  247. }
  248. if (isset($this->bind[$abstract])) {
  249. $concrete = $this->bind[$abstract];
  250. if ($concrete instanceof Closure) {
  251. $object = $this->invokeFunction($concrete, $vars);
  252. } else {
  253. $this->name[$abstract] = $concrete;
  254. return $this->make($concrete, $vars, $newInstance);
  255. }
  256. } else {
  257. $object = $this->invokeClass($abstract, $vars);
  258. }
  259. if (!$newInstance) {
  260. $this->instances[$abstract] = $object;
  261. }
  262. return $object;
  263. }
  264. /**
  265. * 删除容器中的对象实例
  266. * @access public
  267. * @param string|array $abstract 类名或者标识
  268. * @return void
  269. */
  270. public function delete($abstract)
  271. {
  272. foreach ((array) $abstract as $name) {
  273. $name = isset($this->name[$name]) ? $this->name[$name] : $name;
  274. if (isset($this->instances[$name])) {
  275. unset($this->instances[$name]);
  276. }
  277. }
  278. }
  279. /**
  280. * 获取容器中的对象实例
  281. * @access public
  282. * @return array
  283. */
  284. public function all()
  285. {
  286. return $this->instances;
  287. }
  288. /**
  289. * 清除容器中的对象实例
  290. * @access public
  291. * @return void
  292. */
  293. public function flush()
  294. {
  295. $this->instances = [];
  296. $this->bind = [];
  297. $this->name = [];
  298. }
  299. /**
  300. * 执行函数或者闭包方法 支持参数调用
  301. * @access public
  302. * @param mixed $function 函数或者闭包
  303. * @param array $vars 参数
  304. * @return mixed
  305. */
  306. public function invokeFunction($function, $vars = [])
  307. {
  308. try {
  309. $reflect = new ReflectionFunction($function);
  310. $args = $this->bindParams($reflect, $vars);
  311. return call_user_func_array($function, $args);
  312. } catch (ReflectionException $e) {
  313. throw new Exception('function not exists: ' . $function . '()');
  314. }
  315. }
  316. /**
  317. * 调用反射执行类的方法 支持参数绑定
  318. * @access public
  319. * @param mixed $method 方法
  320. * @param array $vars 参数
  321. * @return mixed
  322. */
  323. public function invokeMethod($method, $vars = [])
  324. {
  325. try {
  326. if (is_array($method)) {
  327. $class = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
  328. $reflect = new ReflectionMethod($class, $method[1]);
  329. } else {
  330. // 静态方法
  331. $reflect = new ReflectionMethod($method);
  332. }
  333. $args = $this->bindParams($reflect, $vars);
  334. return $reflect->invokeArgs(isset($class) ? $class : null, $args);
  335. } catch (ReflectionException $e) {
  336. if (is_array($method) && is_object($method[0])) {
  337. $method[0] = get_class($method[0]);
  338. }
  339. throw new Exception('method not exists: ' . (is_array($method) ? $method[0] . '::' . $method[1] : $method) . '()');
  340. }
  341. }
  342. /**
  343. * 调用反射执行类的方法 支持参数绑定
  344. * @access public
  345. * @param object $instance 对象实例
  346. * @param mixed $reflect 反射类
  347. * @param array $vars 参数
  348. * @return mixed
  349. */
  350. public function invokeReflectMethod($instance, $reflect, $vars = [])
  351. {
  352. $args = $this->bindParams($reflect, $vars);
  353. return $reflect->invokeArgs($instance, $args);
  354. }
  355. /**
  356. * 调用反射执行callable 支持参数绑定
  357. * @access public
  358. * @param mixed $callable
  359. * @param array $vars 参数
  360. * @return mixed
  361. */
  362. public function invoke($callable, $vars = [])
  363. {
  364. if ($callable instanceof Closure) {
  365. return $this->invokeFunction($callable, $vars);
  366. }
  367. return $this->invokeMethod($callable, $vars);
  368. }
  369. /**
  370. * 调用反射执行类的实例化 支持依赖注入
  371. * @access public
  372. * @param string $class 类名
  373. * @param array $vars 参数
  374. * @return mixed
  375. */
  376. public function invokeClass($class, $vars = [])
  377. {
  378. try {
  379. $reflect = new ReflectionClass($class);
  380. if ($reflect->hasMethod('__make')) {
  381. $method = new ReflectionMethod($class, '__make');
  382. if ($method->isPublic() && $method->isStatic()) {
  383. $args = $this->bindParams($method, $vars);
  384. return $method->invokeArgs(null, $args);
  385. }
  386. }
  387. $constructor = $reflect->getConstructor();
  388. $args = $constructor ? $this->bindParams($constructor, $vars) : [];
  389. return $reflect->newInstanceArgs($args);
  390. } catch (ReflectionException $e) {
  391. throw new ClassNotFoundException('class not exists: ' . $class, $class);
  392. }
  393. }
  394. /**
  395. * 绑定参数
  396. * @access protected
  397. * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
  398. * @param array $vars 参数
  399. * @return array
  400. */
  401. protected function bindParams($reflect, $vars = [])
  402. {
  403. if ($reflect->getNumberOfParameters() == 0) {
  404. return [];
  405. }
  406. // 判断数组类型 数字数组时按顺序绑定参数
  407. reset($vars);
  408. $type = key($vars) === 0 ? 1 : 0;
  409. $params = $reflect->getParameters();
  410. if (PHP_VERSION > 8.0) {
  411. $args = $this->parseParamsForPHP8($params, $vars, $type);
  412. } else {
  413. $args = $this->parseParams($params, $vars, $type);
  414. }
  415. return $args;
  416. }
  417. /**
  418. * 解析参数
  419. * @access protected
  420. * @param array $params 参数列表
  421. * @param array $vars 参数数据
  422. * @param int $type 参数类别
  423. * @return array
  424. */
  425. protected function parseParams($params, $vars, $type)
  426. {
  427. foreach ($params as $param) {
  428. $name = $param->getName();
  429. $lowerName = Loader::parseName($name);
  430. $class = $param->getClass();
  431. if ($class) {
  432. $args[] = $this->getObjectParam($class->getName(), $vars);
  433. } elseif (1 == $type && !empty($vars)) {
  434. $args[] = array_shift($vars);
  435. } elseif (0 == $type && isset($vars[$name])) {
  436. $args[] = $vars[$name];
  437. } elseif (0 == $type && isset($vars[$lowerName])) {
  438. $args[] = $vars[$lowerName];
  439. } elseif ($param->isDefaultValueAvailable()) {
  440. $args[] = $param->getDefaultValue();
  441. } else {
  442. throw new InvalidArgumentException('method param miss:' . $name);
  443. }
  444. }
  445. return $args;
  446. }
  447. /**
  448. * 解析参数
  449. * @access protected
  450. * @param array $params 参数列表
  451. * @param array $vars 参数数据
  452. * @param int $type 参数类别
  453. * @return array
  454. */
  455. protected function parseParamsForPHP8($params, $vars, $type)
  456. {
  457. foreach ($params as $param) {
  458. $name = $param->getName();
  459. $lowerName = Loader::parseName($name);
  460. $reflectionType = $param->getType();
  461. if ($reflectionType && $reflectionType->isBuiltin() === false) {
  462. $args[] = $this->getObjectParam($reflectionType->getName(), $vars);
  463. } elseif (1 == $type && !empty($vars)) {
  464. $args[] = array_shift($vars);
  465. } elseif (0 == $type && array_key_exists($name, $vars)) {
  466. $args[] = $vars[$name];
  467. } elseif (0 == $type && array_key_exists($lowerName, $vars)) {
  468. $args[] = $vars[$lowerName];
  469. } elseif ($param->isDefaultValueAvailable()) {
  470. $args[] = $param->getDefaultValue();
  471. } else {
  472. throw new InvalidArgumentException('method param miss:' . $name);
  473. }
  474. }
  475. return $args;
  476. }
  477. /**
  478. * 获取对象类型的参数值
  479. * @access protected
  480. * @param string $className 类名
  481. * @param array $vars 参数
  482. * @return mixed
  483. */
  484. protected function getObjectParam($className, &$vars)
  485. {
  486. $array = $vars;
  487. $value = array_shift($array);
  488. if ($value instanceof $className) {
  489. $result = $value;
  490. array_shift($vars);
  491. } else {
  492. $result = $this->make($className);
  493. }
  494. return $result;
  495. }
  496. public function __set($name, $value)
  497. {
  498. $this->bindTo($name, $value);
  499. }
  500. public function __get($name)
  501. {
  502. return $this->make($name);
  503. }
  504. public function __isset($name)
  505. {
  506. return $this->bound($name);
  507. }
  508. public function __unset($name)
  509. {
  510. $this->delete($name);
  511. }
  512. public function offsetExists($key)
  513. {
  514. return $this->__isset($key);
  515. }
  516. public function offsetGet($key)
  517. {
  518. return $this->__get($key);
  519. }
  520. public function offsetSet($key, $value)
  521. {
  522. $this->__set($key, $value);
  523. }
  524. public function offsetUnset($key)
  525. {
  526. $this->__unset($key);
  527. }
  528. //Countable
  529. public function count()
  530. {
  531. return count($this->instances);
  532. }
  533. //IteratorAggregate
  534. public function getIterator()
  535. {
  536. return new ArrayIterator($this->instances);
  537. }
  538. public function __debugInfo()
  539. {
  540. $data = get_object_vars($this);
  541. unset($data['instances'], $data['instance']);
  542. return $data;
  543. }
  544. }