Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 
 

678 righe
21 KiB

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