Loader.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  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 think\exception\ClassNotFoundException;
  13. class Loader
  14. {
  15. /**
  16. * @var array 实例数组
  17. */
  18. protected static $instance = [];
  19. /**
  20. * @var array 类名映射
  21. */
  22. protected static $map = [];
  23. /**
  24. * @var array 命名空间别名
  25. */
  26. protected static $namespaceAlias = [];
  27. /**
  28. * @var array PSR-4 命名空间前缀长度映射
  29. */
  30. private static $prefixLengthsPsr4 = [];
  31. /**
  32. * @var array PSR-4 的加载目录
  33. */
  34. private static $prefixDirsPsr4 = [];
  35. /**
  36. * @var array PSR-4 加载失败的回退目录
  37. */
  38. private static $fallbackDirsPsr4 = [];
  39. /**
  40. * @var array PSR-0 命名空间前缀映射
  41. */
  42. private static $prefixesPsr0 = [];
  43. /**
  44. * @var array PSR-0 加载失败的回退目录
  45. */
  46. private static $fallbackDirsPsr0 = [];
  47. /**
  48. * @var array 自动加载的文件
  49. */
  50. private static $autoloadFiles = [];
  51. /**
  52. * 自动加载
  53. * @access public
  54. * @param string $class 类名
  55. * @return bool
  56. */
  57. public static function autoload($class)
  58. {
  59. // 检测命名空间别名
  60. if (!empty(self::$namespaceAlias)) {
  61. $namespace = dirname($class);
  62. if (isset(self::$namespaceAlias[$namespace])) {
  63. $original = self::$namespaceAlias[$namespace] . '\\' . basename($class);
  64. if (class_exists($original)) {
  65. return class_alias($original, $class, false);
  66. }
  67. }
  68. }
  69. if ($file = self::findFile($class)) {
  70. // 非 Win 环境不严格区分大小写
  71. if (!IS_WIN || pathinfo($file, PATHINFO_FILENAME) == pathinfo(realpath($file), PATHINFO_FILENAME)) {
  72. __include_file($file);
  73. return true;
  74. }
  75. }
  76. return false;
  77. }
  78. /**
  79. * 查找文件
  80. * @access private
  81. * @param string $class 类名
  82. * @return bool|string
  83. */
  84. private static function findFile($class)
  85. {
  86. // 类库映射
  87. if (!empty(self::$map[$class])) {
  88. return self::$map[$class];
  89. }
  90. // 查找 PSR-4
  91. $logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
  92. $first = $class[0];
  93. if (isset(self::$prefixLengthsPsr4[$first])) {
  94. foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
  95. if (0 === strpos($class, $prefix)) {
  96. foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
  97. if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) {
  98. return $file;
  99. }
  100. }
  101. }
  102. }
  103. }
  104. // 查找 PSR-4 fallback dirs
  105. foreach (self::$fallbackDirsPsr4 as $dir) {
  106. if (is_file($file = $dir . DS . $logicalPathPsr4)) {
  107. return $file;
  108. }
  109. }
  110. // 查找 PSR-0
  111. if (false !== $pos = strrpos($class, '\\')) {
  112. // namespace class name
  113. $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
  114. . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
  115. } else {
  116. // PEAR-like class name
  117. $logicalPathPsr0 = strtr($class, '_', DS) . EXT;
  118. }
  119. if (isset(self::$prefixesPsr0[$first])) {
  120. foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
  121. if (0 === strpos($class, $prefix)) {
  122. foreach ($dirs as $dir) {
  123. if (is_file($file = $dir . DS . $logicalPathPsr0)) {
  124. return $file;
  125. }
  126. }
  127. }
  128. }
  129. }
  130. // 查找 PSR-0 fallback dirs
  131. foreach (self::$fallbackDirsPsr0 as $dir) {
  132. if (is_file($file = $dir . DS . $logicalPathPsr0)) {
  133. return $file;
  134. }
  135. }
  136. // 找不到则设置映射为 false 并返回
  137. return self::$map[$class] = false;
  138. }
  139. /**
  140. * 注册 classmap
  141. * @access public
  142. * @param string|array $class 类名
  143. * @param string $map 映射
  144. * @return void
  145. */
  146. public static function addClassMap($class, $map = '')
  147. {
  148. if (is_array($class)) {
  149. self::$map = array_merge(self::$map, $class);
  150. } else {
  151. self::$map[$class] = $map;
  152. }
  153. }
  154. /**
  155. * 注册命名空间
  156. * @access public
  157. * @param string|array $namespace 命名空间
  158. * @param string $path 路径
  159. * @return void
  160. */
  161. public static function addNamespace($namespace, $path = '')
  162. {
  163. if (is_array($namespace)) {
  164. foreach ($namespace as $prefix => $paths) {
  165. self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
  166. }
  167. } else {
  168. self::addPsr4($namespace . '\\', rtrim($path, DS), true);
  169. }
  170. }
  171. /**
  172. * 添加 PSR-0 命名空间
  173. * @access private
  174. * @param array|string $prefix 空间前缀
  175. * @param array $paths 路径
  176. * @param bool $prepend 预先设置的优先级更高
  177. * @return void
  178. */
  179. private static function addPsr0($prefix, $paths, $prepend = false)
  180. {
  181. if (!$prefix) {
  182. self::$fallbackDirsPsr0 = $prepend ?
  183. array_merge((array) $paths, self::$fallbackDirsPsr0) :
  184. array_merge(self::$fallbackDirsPsr0, (array) $paths);
  185. } else {
  186. $first = $prefix[0];
  187. if (!isset(self::$prefixesPsr0[$first][$prefix])) {
  188. self::$prefixesPsr0[$first][$prefix] = (array) $paths;
  189. } else {
  190. self::$prefixesPsr0[$first][$prefix] = $prepend ?
  191. array_merge((array) $paths, self::$prefixesPsr0[$first][$prefix]) :
  192. array_merge(self::$prefixesPsr0[$first][$prefix], (array) $paths);
  193. }
  194. }
  195. }
  196. /**
  197. * 添加 PSR-4 空间
  198. * @access private
  199. * @param array|string $prefix 空间前缀
  200. * @param string $paths 路径
  201. * @param bool $prepend 预先设置的优先级更高
  202. * @return void
  203. */
  204. private static function addPsr4($prefix, $paths, $prepend = false)
  205. {
  206. if (!$prefix) {
  207. // Register directories for the root namespace.
  208. self::$fallbackDirsPsr4 = $prepend ?
  209. array_merge((array) $paths, self::$fallbackDirsPsr4) :
  210. array_merge(self::$fallbackDirsPsr4, (array) $paths);
  211. } elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
  212. // Register directories for a new namespace.
  213. $length = strlen($prefix);
  214. if ('\\' !== $prefix[$length - 1]) {
  215. throw new \InvalidArgumentException(
  216. "A non-empty PSR-4 prefix must end with a namespace separator."
  217. );
  218. }
  219. self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
  220. self::$prefixDirsPsr4[$prefix] = (array) $paths;
  221. } else {
  222. self::$prefixDirsPsr4[$prefix] = $prepend ?
  223. // Prepend directories for an already registered namespace.
  224. array_merge((array) $paths, self::$prefixDirsPsr4[$prefix]) :
  225. // Append directories for an already registered namespace.
  226. array_merge(self::$prefixDirsPsr4[$prefix], (array) $paths);
  227. }
  228. }
  229. /**
  230. * 注册命名空间别名
  231. * @access public
  232. * @param array|string $namespace 命名空间
  233. * @param string $original 源文件
  234. * @return void
  235. */
  236. public static function addNamespaceAlias($namespace, $original = '')
  237. {
  238. if (is_array($namespace)) {
  239. self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace);
  240. } else {
  241. self::$namespaceAlias[$namespace] = $original;
  242. }
  243. }
  244. /**
  245. * 注册自动加载机制
  246. * @access public
  247. * @param callable $autoload 自动加载处理方法
  248. * @return void
  249. */
  250. public static function register($autoload = null)
  251. {
  252. // 注册系统自动加载
  253. spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
  254. // 注册命名空间定义
  255. self::addNamespace([
  256. 'think' => LIB_PATH . 'think' . DS,
  257. 'behavior' => LIB_PATH . 'behavior' . DS,
  258. 'traits' => LIB_PATH . 'traits' . DS,
  259. ]);
  260. // 加载类库映射文件
  261. if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
  262. self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
  263. }
  264. // Composer 自动加载支持
  265. if (is_dir(VENDOR_PATH . 'composer')) {
  266. self::registerComposerLoader();
  267. }
  268. // 自动加载 extend 目录
  269. self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
  270. }
  271. /**
  272. * 注册 composer 自动加载
  273. * @access private
  274. * @return void
  275. */
  276. private static function registerComposerLoader()
  277. {
  278. if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
  279. $map = require VENDOR_PATH . 'composer/autoload_namespaces.php';
  280. foreach ($map as $namespace => $path) {
  281. self::addPsr0($namespace, $path);
  282. }
  283. }
  284. if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) {
  285. $map = require VENDOR_PATH . 'composer/autoload_psr4.php';
  286. foreach ($map as $namespace => $path) {
  287. self::addPsr4($namespace, $path);
  288. }
  289. }
  290. if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) {
  291. $classMap = require VENDOR_PATH . 'composer/autoload_classmap.php';
  292. if ($classMap) {
  293. self::addClassMap($classMap);
  294. }
  295. }
  296. if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
  297. $includeFiles = require VENDOR_PATH . 'composer/autoload_files.php';
  298. foreach ($includeFiles as $fileIdentifier => $file) {
  299. if (empty(self::$autoloadFiles[$fileIdentifier])) {
  300. __require_file($file);
  301. self::$autoloadFiles[$fileIdentifier] = true;
  302. }
  303. }
  304. }
  305. }
  306. /**
  307. * 导入所需的类库 同 Java 的 Import 本函数有缓存功能
  308. * @access public
  309. * @param string $class 类库命名空间字符串
  310. * @param string $baseUrl 起始路径
  311. * @param string $ext 导入的文件扩展名
  312. * @return bool
  313. */
  314. public static function import($class, $baseUrl = '', $ext = EXT)
  315. {
  316. static $_file = [];
  317. $key = $class . $baseUrl;
  318. $class = str_replace(['.', '#'], [DS, '.'], $class);
  319. if (isset($_file[$key])) {
  320. return true;
  321. }
  322. if (empty($baseUrl)) {
  323. list($name, $class) = explode(DS, $class, 2);
  324. if (isset(self::$prefixDirsPsr4[$name . '\\'])) {
  325. // 注册的命名空间
  326. $baseUrl = self::$prefixDirsPsr4[$name . '\\'];
  327. } elseif ('@' == $name) {
  328. // 加载当前模块应用类库
  329. $baseUrl = App::$modulePath;
  330. } elseif (is_dir(EXTEND_PATH . $name)) {
  331. $baseUrl = EXTEND_PATH . $name . DS;
  332. } else {
  333. // 加载其它模块的类库
  334. $baseUrl = APP_PATH . $name . DS;
  335. }
  336. } elseif (substr($baseUrl, -1) != DS) {
  337. $baseUrl .= DS;
  338. }
  339. // 如果类存在则导入类库文件
  340. if (is_array($baseUrl)) {
  341. foreach ($baseUrl as $path) {
  342. if (is_file($filename = $path . DS . $class . $ext)) {
  343. break;
  344. }
  345. }
  346. } else {
  347. $filename = $baseUrl . $class . $ext;
  348. }
  349. if (!empty($filename) &&
  350. is_file($filename) &&
  351. (!IS_WIN || pathinfo($filename, PATHINFO_FILENAME) == pathinfo(realpath($filename), PATHINFO_FILENAME))
  352. ) {
  353. __include_file($filename);
  354. $_file[$key] = true;
  355. return true;
  356. }
  357. return false;
  358. }
  359. /**
  360. * 实例化(分层)模型
  361. * @access public
  362. * @param string $name Model名称
  363. * @param string $layer 业务层名称
  364. * @param bool $appendSuffix 是否添加类名后缀
  365. * @param string $common 公共模块名
  366. * @return object
  367. * @throws ClassNotFoundException
  368. */
  369. public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
  370. {
  371. $uid = $name . $layer;
  372. if (isset(self::$instance[$uid])) {
  373. return self::$instance[$uid];
  374. }
  375. list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
  376. if (class_exists($class)) {
  377. $model = new $class();
  378. } else {
  379. $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
  380. if (class_exists($class)) {
  381. $model = new $class();
  382. } else {
  383. throw new ClassNotFoundException('class not exists:' . $class, $class);
  384. }
  385. }
  386. return self::$instance[$uid] = $model;
  387. }
  388. /**
  389. * 实例化(分层)控制器 格式:[模块名/]控制器名
  390. * @access public
  391. * @param string $name 资源地址
  392. * @param string $layer 控制层名称
  393. * @param bool $appendSuffix 是否添加类名后缀
  394. * @param string $empty 空控制器名称
  395. * @return object
  396. * @throws ClassNotFoundException
  397. */
  398. public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
  399. {
  400. list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
  401. if (class_exists($class)) {
  402. return App::invokeClass($class);
  403. }
  404. if ($empty) {
  405. $emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);
  406. if (class_exists($emptyClass)) {
  407. return new $emptyClass(Request::instance());
  408. }
  409. }
  410. throw new ClassNotFoundException('class not exists:' . $class, $class);
  411. }
  412. /**
  413. * 实例化验证类 格式:[模块名/]验证器名
  414. * @access public
  415. * @param string $name 资源地址
  416. * @param string $layer 验证层名称
  417. * @param bool $appendSuffix 是否添加类名后缀
  418. * @param string $common 公共模块名
  419. * @return object|false
  420. * @throws ClassNotFoundException
  421. */
  422. public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
  423. {
  424. $name = $name ?: Config::get('default_validate');
  425. if (empty($name)) {
  426. return new Validate;
  427. }
  428. $uid = $name . $layer;
  429. if (isset(self::$instance[$uid])) {
  430. return self::$instance[$uid];
  431. }
  432. list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
  433. if (class_exists($class)) {
  434. $validate = new $class;
  435. } else {
  436. $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
  437. if (class_exists($class)) {
  438. $validate = new $class;
  439. } else {
  440. throw new ClassNotFoundException('class not exists:' . $class, $class);
  441. }
  442. }
  443. return self::$instance[$uid] = $validate;
  444. }
  445. /**
  446. * 解析模块和类名
  447. * @access protected
  448. * @param string $name 资源地址
  449. * @param string $layer 验证层名称
  450. * @param bool $appendSuffix 是否添加类名后缀
  451. * @return array
  452. */
  453. protected static function getModuleAndClass($name, $layer, $appendSuffix)
  454. {
  455. if (false !== strpos($name, '\\')) {
  456. $module = Request::instance()->module();
  457. $class = $name;
  458. } else {
  459. if (strpos($name, '/')) {
  460. list($module, $name) = explode('/', $name, 2);
  461. } else {
  462. $module = Request::instance()->module();
  463. }
  464. $class = self::parseClass($module, $layer, $name, $appendSuffix);
  465. }
  466. return [$module, $class];
  467. }
  468. /**
  469. * 数据库初始化 并取得数据库类实例
  470. * @access public
  471. * @param mixed $config 数据库配置
  472. * @param bool|string $name 连接标识 true 强制重新连接
  473. * @return \think\db\Connection
  474. */
  475. public static function db($config = [], $name = false)
  476. {
  477. return Db::connect($config, $name);
  478. }
  479. /**
  480. * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
  481. * @access public
  482. * @param string $url 调用地址
  483. * @param string|array $vars 调用参数 支持字符串和数组
  484. * @param string $layer 要调用的控制层名称
  485. * @param bool $appendSuffix 是否添加类名后缀
  486. * @return mixed
  487. */
  488. public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
  489. {
  490. $info = pathinfo($url);
  491. $action = $info['basename'];
  492. $module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller();
  493. $class = self::controller($module, $layer, $appendSuffix);
  494. if ($class) {
  495. if (is_scalar($vars)) {
  496. if (strpos($vars, '=')) {
  497. parse_str($vars, $vars);
  498. } else {
  499. $vars = [$vars];
  500. }
  501. }
  502. return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars);
  503. }
  504. return false;
  505. }
  506. /**
  507. * 字符串命名风格转换
  508. * type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格
  509. * @access public
  510. * @param string $name 字符串
  511. * @param integer $type 转换类型
  512. * @param bool $ucfirst 首字母是否大写(驼峰规则)
  513. * @return string
  514. */
  515. public static function parseName($name, $type = 0, $ucfirst = true)
  516. {
  517. if ($type) {
  518. $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
  519. return strtoupper($match[1]);
  520. }, $name);
  521. return $ucfirst ? ucfirst($name) : lcfirst($name);
  522. }
  523. return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
  524. }
  525. /**
  526. * 解析应用类的类名
  527. * @access public
  528. * @param string $module 模块名
  529. * @param string $layer 层名 controller model ...
  530. * @param string $name 类名
  531. * @param bool $appendSuffix 是否添加类名后缀
  532. * @return string
  533. */
  534. public static function parseClass($module, $layer, $name, $appendSuffix = false)
  535. {
  536. $array = explode('\\', str_replace(['/', '.'], '\\', $name));
  537. $class = self::parseName(array_pop($array), 1);
  538. $class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
  539. $path = $array ? implode('\\', $array) . '\\' : '';
  540. return App::$namespace . '\\' .
  541. ($module ? $module . '\\' : '') .
  542. $layer . '\\' . $path . $class;
  543. }
  544. /**
  545. * 初始化类的实例
  546. * @access public
  547. * @return void
  548. */
  549. public static function clearInstance()
  550. {
  551. self::$instance = [];
  552. }
  553. }
  554. // 作用范围隔离
  555. /**
  556. * include
  557. * @param string $file 文件路径
  558. * @return mixed
  559. */
  560. function __include_file($file)
  561. {
  562. return include $file;
  563. }
  564. /**
  565. * require
  566. * @param string $file 文件路径
  567. * @return mixed
  568. */
  569. function __require_file($file)
  570. {
  571. return require $file;
  572. }