Loader.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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. // Composer 自动加载支持
  255. if (is_dir(VENDOR_PATH . 'composer')) {
  256. if (PHP_VERSION_ID >= 50600 && is_file(VENDOR_PATH . 'composer' . DS . 'autoload_static.php')) {
  257. require VENDOR_PATH . 'composer' . DS . 'autoload_static.php';
  258. $declaredClass = get_declared_classes();
  259. $composerClass = array_pop($declaredClass);
  260. self::$prefixLengthsPsr4 = $composerClass::$prefixLengthsPsr4;
  261. self::$prefixDirsPsr4 = property_exists($composerClass, 'prefixDirsPsr4') ? $composerClass::$prefixDirsPsr4 : [];
  262. self::$prefixesPsr0 = property_exists($composerClass, 'prefixesPsr0') ? $composerClass::$prefixesPsr0 : [];
  263. self::$map = property_exists($composerClass, 'classMap') ? $composerClass::$classMap : [];
  264. } else {
  265. self::registerComposerLoader();
  266. }
  267. }
  268. // 注册命名空间定义
  269. self::addNamespace([
  270. 'think' => LIB_PATH . 'think' . DS,
  271. 'behavior' => LIB_PATH . 'behavior' . DS,
  272. 'traits' => LIB_PATH . 'traits' . DS,
  273. ]);
  274. // 加载类库映射文件
  275. if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
  276. self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
  277. }
  278. self::loadComposerAutoloadFiles();
  279. // 自动加载 extend 目录
  280. self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
  281. }
  282. /**
  283. * 注册 composer 自动加载
  284. * @access private
  285. * @return void
  286. */
  287. private static function registerComposerLoader()
  288. {
  289. if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
  290. $map = require VENDOR_PATH . 'composer/autoload_namespaces.php';
  291. foreach ($map as $namespace => $path) {
  292. self::addPsr0($namespace, $path);
  293. }
  294. }
  295. if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) {
  296. $map = require VENDOR_PATH . 'composer/autoload_psr4.php';
  297. foreach ($map as $namespace => $path) {
  298. self::addPsr4($namespace, $path);
  299. }
  300. }
  301. if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) {
  302. $classMap = require VENDOR_PATH . 'composer/autoload_classmap.php';
  303. if ($classMap) {
  304. self::addClassMap($classMap);
  305. }
  306. }
  307. }
  308. // 加载composer autofile文件
  309. public static function loadComposerAutoloadFiles()
  310. {
  311. if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
  312. $includeFiles = require VENDOR_PATH . 'composer/autoload_files.php';
  313. foreach ($includeFiles as $fileIdentifier => $file) {
  314. if (empty(self::$autoloadFiles[$fileIdentifier])) {
  315. __require_file($file);
  316. self::$autoloadFiles[$fileIdentifier] = true;
  317. }
  318. }
  319. }
  320. }
  321. /**
  322. * 导入所需的类库 同 Java 的 Import 本函数有缓存功能
  323. * @access public
  324. * @param string $class 类库命名空间字符串
  325. * @param string $baseUrl 起始路径
  326. * @param string $ext 导入的文件扩展名
  327. * @return bool
  328. */
  329. public static function import($class, $baseUrl = '', $ext = EXT)
  330. {
  331. static $_file = [];
  332. $key = $class . $baseUrl;
  333. $class = str_replace(['.', '#'], [DS, '.'], $class);
  334. if (isset($_file[$key])) {
  335. return true;
  336. }
  337. if (empty($baseUrl)) {
  338. list($name, $class) = explode(DS, $class, 2);
  339. if (isset(self::$prefixDirsPsr4[$name . '\\'])) {
  340. // 注册的命名空间
  341. $baseUrl = self::$prefixDirsPsr4[$name . '\\'];
  342. } elseif ('@' == $name) {
  343. // 加载当前模块应用类库
  344. $baseUrl = App::$modulePath;
  345. } elseif (is_dir(EXTEND_PATH . $name)) {
  346. $baseUrl = EXTEND_PATH . $name . DS;
  347. } else {
  348. // 加载其它模块的类库
  349. $baseUrl = APP_PATH . $name . DS;
  350. }
  351. } elseif (substr($baseUrl, -1) != DS) {
  352. $baseUrl .= DS;
  353. }
  354. // 如果类存在则导入类库文件
  355. if (is_array($baseUrl)) {
  356. foreach ($baseUrl as $path) {
  357. if (is_file($filename = $path . DS . $class . $ext)) {
  358. break;
  359. }
  360. }
  361. } else {
  362. $filename = $baseUrl . $class . $ext;
  363. }
  364. if (!empty($filename) &&
  365. is_file($filename) &&
  366. (!IS_WIN || pathinfo($filename, PATHINFO_FILENAME) == pathinfo(realpath($filename), PATHINFO_FILENAME))
  367. ) {
  368. __include_file($filename);
  369. $_file[$key] = true;
  370. return true;
  371. }
  372. return false;
  373. }
  374. /**
  375. * 实例化(分层)模型
  376. * @access public
  377. * @param string $name Model名称
  378. * @param string $layer 业务层名称
  379. * @param bool $appendSuffix 是否添加类名后缀
  380. * @param string $common 公共模块名
  381. * @return object
  382. * @throws ClassNotFoundException
  383. */
  384. public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
  385. {
  386. $uid = $name . $layer;
  387. if (isset(self::$instance[$uid])) {
  388. return self::$instance[$uid];
  389. }
  390. list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
  391. if (class_exists($class)) {
  392. $model = new $class();
  393. } else {
  394. $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
  395. if (class_exists($class)) {
  396. $model = new $class();
  397. } else {
  398. throw new ClassNotFoundException('class not exists:' . $class, $class);
  399. }
  400. }
  401. return self::$instance[$uid] = $model;
  402. }
  403. /**
  404. * 实例化(分层)控制器 格式:[模块名/]控制器名
  405. * @access public
  406. * @param string $name 资源地址
  407. * @param string $layer 控制层名称
  408. * @param bool $appendSuffix 是否添加类名后缀
  409. * @param string $empty 空控制器名称
  410. * @return object
  411. * @throws ClassNotFoundException
  412. */
  413. public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
  414. {
  415. list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
  416. if (class_exists($class)) {
  417. return App::invokeClass($class);
  418. }
  419. if ($empty) {
  420. $emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);
  421. if (class_exists($emptyClass)) {
  422. return new $emptyClass(Request::instance());
  423. }
  424. }
  425. throw new ClassNotFoundException('class not exists:' . $class, $class);
  426. }
  427. /**
  428. * 实例化验证类 格式:[模块名/]验证器名
  429. * @access public
  430. * @param string $name 资源地址
  431. * @param string $layer 验证层名称
  432. * @param bool $appendSuffix 是否添加类名后缀
  433. * @param string $common 公共模块名
  434. * @return object|false
  435. * @throws ClassNotFoundException
  436. */
  437. public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')
  438. {
  439. $name = $name ?: Config::get('default_validate');
  440. if (empty($name)) {
  441. return new Validate;
  442. }
  443. $uid = $name . $layer;
  444. if (isset(self::$instance[$uid])) {
  445. return self::$instance[$uid];
  446. }
  447. list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
  448. if (class_exists($class)) {
  449. $validate = new $class;
  450. } else {
  451. $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
  452. if (class_exists($class)) {
  453. $validate = new $class;
  454. } else {
  455. throw new ClassNotFoundException('class not exists:' . $class, $class);
  456. }
  457. }
  458. return self::$instance[$uid] = $validate;
  459. }
  460. /**
  461. * 解析模块和类名
  462. * @access protected
  463. * @param string $name 资源地址
  464. * @param string $layer 验证层名称
  465. * @param bool $appendSuffix 是否添加类名后缀
  466. * @return array
  467. */
  468. protected static function getModuleAndClass($name, $layer, $appendSuffix)
  469. {
  470. if (false !== strpos($name, '\\')) {
  471. $module = Request::instance()->module();
  472. $class = $name;
  473. } else {
  474. if (strpos($name, '/')) {
  475. list($module, $name) = explode('/', $name, 2);
  476. } else {
  477. $module = Request::instance()->module();
  478. }
  479. $class = self::parseClass($module, $layer, $name, $appendSuffix);
  480. }
  481. return [$module, $class];
  482. }
  483. /**
  484. * 数据库初始化 并取得数据库类实例
  485. * @access public
  486. * @param mixed $config 数据库配置
  487. * @param bool|string $name 连接标识 true 强制重新连接
  488. * @return \think\db\Connection
  489. */
  490. public static function db($config = [], $name = false)
  491. {
  492. return Db::connect($config, $name);
  493. }
  494. /**
  495. * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
  496. * @access public
  497. * @param string $url 调用地址
  498. * @param string|array $vars 调用参数 支持字符串和数组
  499. * @param string $layer 要调用的控制层名称
  500. * @param bool $appendSuffix 是否添加类名后缀
  501. * @return mixed
  502. */
  503. public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
  504. {
  505. $info = pathinfo($url);
  506. $action = $info['basename'];
  507. $module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller();
  508. $class = self::controller($module, $layer, $appendSuffix);
  509. if ($class) {
  510. if (is_scalar($vars)) {
  511. if (strpos($vars, '=')) {
  512. parse_str($vars, $vars);
  513. } else {
  514. $vars = [$vars];
  515. }
  516. }
  517. return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars);
  518. }
  519. return false;
  520. }
  521. /**
  522. * 字符串命名风格转换
  523. * type 0 将 Java 风格转换为 C 的风格 1 将 C 风格转换为 Java 的风格
  524. * @access public
  525. * @param string $name 字符串
  526. * @param integer $type 转换类型
  527. * @param bool $ucfirst 首字母是否大写(驼峰规则)
  528. * @return string
  529. */
  530. public static function parseName($name, $type = 0, $ucfirst = true)
  531. {
  532. if ($type) {
  533. $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
  534. return strtoupper($match[1]);
  535. }, $name);
  536. return $ucfirst ? ucfirst($name) : lcfirst($name);
  537. }
  538. return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
  539. }
  540. /**
  541. * 解析应用类的类名
  542. * @access public
  543. * @param string $module 模块名
  544. * @param string $layer 层名 controller model ...
  545. * @param string $name 类名
  546. * @param bool $appendSuffix 是否添加类名后缀
  547. * @return string
  548. */
  549. public static function parseClass($module, $layer, $name, $appendSuffix = false)
  550. {
  551. $array = explode('\\', str_replace(['/', '.'], '\\', $name));
  552. $class = self::parseName(array_pop($array), 1);
  553. $class = $class . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
  554. $path = $array ? implode('\\', $array) . '\\' : '';
  555. return App::$namespace . '\\' .
  556. ($module ? $module . '\\' : '') .
  557. $layer . '\\' . $path . $class;
  558. }
  559. /**
  560. * 初始化类的实例
  561. * @access public
  562. * @return void
  563. */
  564. public static function clearInstance()
  565. {
  566. self::$instance = [];
  567. }
  568. }
  569. // 作用范围隔离
  570. /**
  571. * include
  572. * @param string $file 文件路径
  573. * @return mixed
  574. */
  575. function __include_file($file)
  576. {
  577. return include $file;
  578. }
  579. /**
  580. * require
  581. * @param string $file 文件路径
  582. * @return mixed
  583. */
  584. function __require_file($file)
  585. {
  586. return require $file;
  587. }