Loader.php 19 KB

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