Loader.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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. * 类名映射信息
  17. * @var array
  18. */
  19. protected static $classMap = [];
  20. /**
  21. * 类库别名
  22. * @var array
  23. */
  24. protected static $classAlias = [];
  25. /**
  26. * PSR-4
  27. * @var array
  28. */
  29. private static $prefixLengthsPsr4 = [];
  30. private static $prefixDirsPsr4 = [];
  31. private static $fallbackDirsPsr4 = [];
  32. /**
  33. * PSR-0
  34. * @var array
  35. */
  36. private static $prefixesPsr0 = [];
  37. private static $fallbackDirsPsr0 = [];
  38. /**
  39. * 需要加载的文件
  40. * @var array
  41. */
  42. private static $files = [];
  43. /**
  44. * Composer安装路径
  45. * @var string
  46. */
  47. private static $composerPath;
  48. // 获取应用根目录
  49. public static function getRootPath()
  50. {
  51. if ('cli' == PHP_SAPI) {
  52. $scriptName = realpath($_SERVER['argv'][0]);
  53. } else {
  54. $scriptName = $_SERVER['SCRIPT_FILENAME'];
  55. }
  56. $path = realpath(dirname($scriptName));
  57. if (!is_file($path . DIRECTORY_SEPARATOR . 'think')) {
  58. $path = dirname($path);
  59. }
  60. return $path . DIRECTORY_SEPARATOR;
  61. }
  62. // 注册自动加载机制
  63. public static function register($autoload = '')
  64. {
  65. // 注册系统自动加载
  66. spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
  67. $rootPath = self::getRootPath();
  68. self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;
  69. // Composer自动加载支持
  70. if (is_dir(self::$composerPath)) {
  71. if (is_file(self::$composerPath . 'autoload_static.php')) {
  72. require self::$composerPath . 'autoload_static.php';
  73. $declaredClass = get_declared_classes();
  74. $composerClass = array_pop($declaredClass);
  75. foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
  76. if (property_exists($composerClass, $attr)) {
  77. self::${$attr} = $composerClass::${$attr};
  78. }
  79. }
  80. } else {
  81. self::registerComposerLoader(self::$composerPath);
  82. }
  83. }
  84. // 注册命名空间定义
  85. self::addNamespace([
  86. 'think' => __DIR__,
  87. 'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
  88. ]);
  89. // 加载类库映射文件
  90. if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
  91. self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
  92. }
  93. // 自动加载extend目录
  94. self::addAutoLoadDir($rootPath . 'extend');
  95. }
  96. // 自动加载
  97. public static function autoload($class)
  98. {
  99. if (isset(self::$classAlias[$class])) {
  100. return class_alias(self::$classAlias[$class], $class);
  101. }
  102. if ($file = self::findFile($class)) {
  103. // Win环境严格区分大小写
  104. if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
  105. return false;
  106. }
  107. __include_file($file);
  108. return true;
  109. }
  110. }
  111. /**
  112. * 查找文件
  113. * @access private
  114. * @param string $class
  115. * @return string|false
  116. */
  117. private static function findFile($class)
  118. {
  119. if (!empty(self::$classMap[$class])) {
  120. // 类库映射
  121. return self::$classMap[$class];
  122. }
  123. // 查找 PSR-4
  124. $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php';
  125. $first = $class[0];
  126. if (isset(self::$prefixLengthsPsr4[$first])) {
  127. foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
  128. if (0 === strpos($class, $prefix)) {
  129. foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
  130. if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
  131. return $file;
  132. }
  133. }
  134. }
  135. }
  136. }
  137. // 查找 PSR-4 fallback dirs
  138. foreach (self::$fallbackDirsPsr4 as $dir) {
  139. if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
  140. return $file;
  141. }
  142. }
  143. // 查找 PSR-0
  144. if (false !== $pos = strrpos($class, '\\')) {
  145. // namespaced class name
  146. $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
  147. . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
  148. } else {
  149. // PEAR-like class name
  150. $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php';
  151. }
  152. if (isset(self::$prefixesPsr0[$first])) {
  153. foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
  154. if (0 === strpos($class, $prefix)) {
  155. foreach ($dirs as $dir) {
  156. if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
  157. return $file;
  158. }
  159. }
  160. }
  161. }
  162. }
  163. // 查找 PSR-0 fallback dirs
  164. foreach (self::$fallbackDirsPsr0 as $dir) {
  165. if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
  166. return $file;
  167. }
  168. }
  169. return self::$classMap[$class] = false;
  170. }
  171. // 注册classmap
  172. public static function addClassMap($class, $map = '')
  173. {
  174. if (is_array($class)) {
  175. self::$classMap = array_merge(self::$classMap, $class);
  176. } else {
  177. self::$classMap[$class] = $map;
  178. }
  179. }
  180. // 注册命名空间
  181. public static function addNamespace($namespace, $path = '')
  182. {
  183. if (is_array($namespace)) {
  184. foreach ($namespace as $prefix => $paths) {
  185. self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true);
  186. }
  187. } else {
  188. self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true);
  189. }
  190. }
  191. // 添加Ps0空间
  192. private static function addPsr0($prefix, $paths, $prepend = false)
  193. {
  194. if (!$prefix) {
  195. if ($prepend) {
  196. self::$fallbackDirsPsr0 = array_merge(
  197. (array) $paths,
  198. self::$fallbackDirsPsr0
  199. );
  200. } else {
  201. self::$fallbackDirsPsr0 = array_merge(
  202. self::$fallbackDirsPsr0,
  203. (array) $paths
  204. );
  205. }
  206. return;
  207. }
  208. $first = $prefix[0];
  209. if (!isset(self::$prefixesPsr0[$first][$prefix])) {
  210. self::$prefixesPsr0[$first][$prefix] = (array) $paths;
  211. return;
  212. }
  213. if ($prepend) {
  214. self::$prefixesPsr0[$first][$prefix] = array_merge(
  215. (array) $paths,
  216. self::$prefixesPsr0[$first][$prefix]
  217. );
  218. } else {
  219. self::$prefixesPsr0[$first][$prefix] = array_merge(
  220. self::$prefixesPsr0[$first][$prefix],
  221. (array) $paths
  222. );
  223. }
  224. }
  225. // 添加Psr4空间
  226. private static function addPsr4($prefix, $paths, $prepend = false)
  227. {
  228. if (!$prefix) {
  229. // Register directories for the root namespace.
  230. if ($prepend) {
  231. self::$fallbackDirsPsr4 = array_merge(
  232. (array) $paths,
  233. self::$fallbackDirsPsr4
  234. );
  235. } else {
  236. self::$fallbackDirsPsr4 = array_merge(
  237. self::$fallbackDirsPsr4,
  238. (array) $paths
  239. );
  240. }
  241. } elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
  242. // Register directories for a new namespace.
  243. $length = strlen($prefix);
  244. if ('\\' !== $prefix[$length - 1]) {
  245. throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
  246. }
  247. self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
  248. self::$prefixDirsPsr4[$prefix] = (array) $paths;
  249. } elseif ($prepend) {
  250. // Prepend directories for an already registered namespace.
  251. self::$prefixDirsPsr4[$prefix] = array_merge(
  252. (array) $paths,
  253. self::$prefixDirsPsr4[$prefix]
  254. );
  255. } else {
  256. // Append directories for an already registered namespace.
  257. self::$prefixDirsPsr4[$prefix] = array_merge(
  258. self::$prefixDirsPsr4[$prefix],
  259. (array) $paths
  260. );
  261. }
  262. }
  263. // 注册自动加载类库目录
  264. public static function addAutoLoadDir($path)
  265. {
  266. self::$fallbackDirsPsr4[] = $path;
  267. }
  268. // 注册类别名
  269. public static function addClassAlias($alias, $class = null)
  270. {
  271. if (is_array($alias)) {
  272. self::$classAlias = array_merge(self::$classAlias, $alias);
  273. } else {
  274. self::$classAlias[$alias] = $class;
  275. }
  276. }
  277. // 注册composer自动加载
  278. public static function registerComposerLoader($composerPath)
  279. {
  280. if (is_file($composerPath . 'autoload_namespaces.php')) {
  281. $map = require $composerPath . 'autoload_namespaces.php';
  282. foreach ($map as $namespace => $path) {
  283. self::addPsr0($namespace, $path);
  284. }
  285. }
  286. if (is_file($composerPath . 'autoload_psr4.php')) {
  287. $map = require $composerPath . 'autoload_psr4.php';
  288. foreach ($map as $namespace => $path) {
  289. self::addPsr4($namespace, $path);
  290. }
  291. }
  292. if (is_file($composerPath . 'autoload_classmap.php')) {
  293. $classMap = require $composerPath . 'autoload_classmap.php';
  294. if ($classMap) {
  295. self::addClassMap($classMap);
  296. }
  297. }
  298. if (is_file($composerPath . 'autoload_files.php')) {
  299. self::$files = require $composerPath . 'autoload_files.php';
  300. }
  301. }
  302. // 加载composer autofile文件
  303. public static function loadComposerAutoloadFiles()
  304. {
  305. foreach (self::$files as $fileIdentifier => $file) {
  306. if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
  307. __require_file($file);
  308. $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
  309. }
  310. }
  311. }
  312. /**
  313. * 字符串命名风格转换
  314. * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
  315. * @access public
  316. * @param string $name 字符串
  317. * @param integer $type 转换类型
  318. * @param bool $ucfirst 首字母是否大写(驼峰规则)
  319. * @return string
  320. */
  321. public static function parseName($name, $type = 0, $ucfirst = true)
  322. {
  323. if ($type) {
  324. $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
  325. return strtoupper($match[1]);
  326. }, $name);
  327. return $ucfirst ? ucfirst($name) : lcfirst($name);
  328. }
  329. return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
  330. }
  331. /**
  332. * 创建工厂对象实例
  333. * @access public
  334. * @param string $name 工厂类名
  335. * @param string $namespace 默认命名空间
  336. * @return mixed
  337. */
  338. public static function factory($name, $namespace = '', ...$args)
  339. {
  340. $class = false !== strpos($name, '\\') ? $name : $namespace . ucwords($name);
  341. if (class_exists($class)) {
  342. return Container::getInstance()->invokeClass($class, $args);
  343. } else {
  344. throw new ClassNotFoundException('class not exists:' . $class, $class);
  345. }
  346. }
  347. }
  348. /**
  349. * 作用范围隔离
  350. *
  351. * @param $file
  352. * @return mixed
  353. */
  354. function __include_file($file)
  355. {
  356. return include $file;
  357. }
  358. function __require_file($file)
  359. {
  360. return require $file;
  361. }