diff --git a/library/think/Lang.php b/library/think/Lang.php index 571921be..35c98f05 100644 --- a/library/think/Lang.php +++ b/library/think/Lang.php @@ -148,7 +148,7 @@ class Lang /** * 自动侦测设置获取语言选择 - * @return void + * @return string */ public static function detect() { diff --git a/library/think/Loader.php b/library/think/Loader.php index 97e92140..00ee74d3 100644 --- a/library/think/Loader.php +++ b/library/think/Loader.php @@ -11,28 +11,28 @@ namespace think; -use think\App; use think\exception\ClassNotFoundException; -use think\Request; class Loader { protected static $instance = []; // 类名映射 protected static $map = []; - // 加载列表 - protected static $load = []; - // 命名空间 - protected static $namespace = []; + // 命名空间别名 protected static $namespaceAlias = []; + // PSR-4 private static $prefixLengthsPsr4 = []; private static $prefixDirsPsr4 = []; + private static $fallbackDirsPsr4 = []; + // PSR-0 - private static $prefixesPsr0 = []; - // Composer自动加载 - private static $composerLoader = false; + private static $prefixesPsr0 = []; + private static $fallbackDirsPsr0 = []; + + // 自动加载的文件 + private static $autoloadFiles = []; // 自动加载 public static function autoload($class) @@ -48,55 +48,83 @@ class Loader } } - if (!empty(self::$map[$class])) { - // 类库映射 - include self::$map[$class]; - } elseif (self::$composerLoader && $file = self::findFileInComposer($class)) { - // Composer自动加载 - include $file; - } else { - // 命名空间自动加载 - if (!strpos($class, '\\')) { + if ($file = self::findFile($class)) { + + // Win环境严格区分大小写 + if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) { return false; } - $item = explode('\\', $class); - // 解析命名空间所在的路径 - if (count($item) > 2 && isset(self::$namespace[$item[0] . '\\' . $item[1]])) { - // 子命名空间定义(仅支持二级) - list($ns1, $ns2, $class) = explode('\\', $class, 3); - $path = self::$namespace[$ns1 . '\\' . $ns2]; - } elseif (isset(self::$namespace[$item[0]])) { - // 根命名空间定义 - list($name, $class) = explode('\\', $class, 2); - $path = self::$namespace[$name]; - } elseif (is_dir(EXTEND_PATH . $item[0])) { - // 扩展类库命名空间 - list($name, $class) = explode('\\', $class, 2); - $path = EXTEND_PATH . $name . DS; - } else { - return false; - } - // 定位文件 - $match = false; - foreach ((array) $path as $p) { - $filename = $p . str_replace('\\', DS, $class) . EXT; - if (is_file($filename)) { - // Win环境严格区分大小写 - if (IS_WIN && false === strpos(realpath($filename), $class . EXT)) { - continue; - } - $match = true; - break; - } - } - if ($match) { - include $filename; - } else { - return false; + includeFile($file); + return true; + } + } + + /** + * 查找文件 + * @param $class + * @return bool + */ + private static function findFile($class) + { + if (!empty(self::$map[$class])) { + // 类库映射 + return self::$map[$class]; + } + + // 查找 PSR-4 + $logicalPathPsr4 = strtr($class, '\\', DS) . EXT; + + $first = $class[0]; + if (isset(self::$prefixLengthsPsr4[$first])) { + foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach (self::$prefixDirsPsr4[$prefix] as $dir) { + if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } } } - return true; + + // 查找 PSR-4 fallback dirs + foreach (self::$fallbackDirsPsr4 as $dir) { + if (is_file($file = $dir . DS . $logicalPathPsr4)) { + return $file; + } + } + + // 查找 PSR-0 + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DS) . EXT; + } + + if (isset(self::$prefixesPsr0[$first])) { + foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (is_file($file = $dir . DS . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // 查找 PSR-0 fallback dirs + foreach (self::$fallbackDirsPsr0 as $dir) { + if (is_file($file = $dir . DS . $logicalPathPsr0)) { + return $file; + } + } + + return self::$map[$class] = false; } // 注册classmap @@ -113,12 +141,40 @@ class Loader public static function addNamespace($namespace, $path = '') { if (is_array($namespace)) { - self::$namespace = array_merge(self::$namespace, $namespace); + foreach ($namespace as $prefix => $paths) { + self::setPsr4($prefix . '\\', rtrim($paths, DS)); + } } else { - self::$namespace[$namespace] = $path; + self::setPsr4($namespace . '\\', rtrim($path, DS)); } } + // 注册Psr0空间 + private static function setPsr0($prefix, $paths) + { + if (!$prefix) { + self::$fallbackDirsPsr0 = (array)$paths; + } else { + self::$prefixesPsr0[$prefix[0]][$prefix] = (array)$paths; + } + } + + // 注册Psr4空间 + private static function setPsr4($prefix, $paths) + { + if (!$prefix) { + self::$fallbackDirsPsr4 = (array)$paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + self::$prefixDirsPsr4[$prefix] = (array)$paths; + } + } + + // 注册命名空间别名 public static function addNamespaceAlias($namespace, $original = '') { @@ -133,7 +189,7 @@ class Loader public static function register($autoload = '') { // 注册系统自动加载 - spl_autoload_register($autoload ?: 'think\\Loader::autoload'); + spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); // 注册命名空间定义 self::addNamespace([ 'think' => LIB_PATH . 'think' . DS, @@ -141,26 +197,15 @@ class Loader 'traits' => LIB_PATH . 'traits' . DS, ]); // 加载类库映射文件 - self::addClassMap(include THINK_PATH . 'classmap' . EXT); + self::addClassMap(includeFile(THINK_PATH . 'classmap' . EXT)); // Composer自动加载支持 if (is_dir(VENDOR_PATH . 'composer')) { - // 注册Composer自动加载 - self::$composerLoader = true; self::registerComposerLoader(); - } elseif (is_file(VENDOR_PATH . 'think_autoload.php')) { - // 读取Composer自动加载文件 - $autoload = include VENDOR_PATH . 'think_autoload.php'; - if (is_array($autoload)) { - // 命名空间和类库映射注册 - self::addNamespace($autoload['namespace']); - self::addClassMap($autoload['classmap']); - // 载入composer包的文件列表 - foreach ($autoload['files'] as $file) { - include $file; - } - } } + + // 自动加载extend目录 + self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS); } // 注册composer自动加载 @@ -169,19 +214,14 @@ class Loader if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) { $map = require VENDOR_PATH . 'composer/autoload_namespaces.php'; foreach ($map as $namespace => $path) { - self::$prefixesPsr0[$namespace[0]][$namespace] = (array) $path; + self::setPsr0($namespace, $path); } } if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) { $map = require VENDOR_PATH . 'composer/autoload_psr4.php'; foreach ($map as $namespace => $path) { - $length = strlen($namespace); - if ('\\' !== $namespace[$length - 1]) { - throw new \InvalidArgumentException("PSR-4 error: A non-empty PSR-4 prefix must end with a namespace separator."); - } - self::$prefixLengthsPsr4[$namespace[0]][$namespace] = $length; - self::$prefixDirsPsr4[$namespace] = (array) $path; + self::setPsr4($namespace, $path); } } @@ -195,81 +235,34 @@ class Loader if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { $includeFiles = require VENDOR_PATH . 'composer/autoload_files.php'; foreach ($includeFiles as $fileIdentifier => $file) { - self::composerRequire($fileIdentifier, $file); - } - } - } - - private static function composerRequire($fileIdentifier, $file) - { - if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - } - } - - private static function findFileInComposer($class, $ext = '.php') - { - // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DS) . $ext; - - $first = $class[0]; - if (isset(self::$prefixLengthsPsr4[$first])) { - foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) { - if (0 === strpos($class, $prefix)) { - foreach (self::$prefixDirsPsr4[$prefix] as $dir) { - if (file_exists($file = $dir . DS . substr($logicalPathPsr4, $length))) { - return $file; - } - } + if (empty(self::$autoloadFiles[$fileIdentifier])) { + requireFile($file); + self::$autoloadFiles[$fileIdentifier] = true; } } } - // PSR-0 lookup - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DS); - } else { - // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DS) . $ext; - } - - if (isset(self::$prefixesPsr0[$first])) { - foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($file = $dir . DS . $logicalPathPsr0)) { - return $file; - } - } - } - } - } - // Remember that this class does not exist. - return self::$map[$class] = false; } /** * 导入所需的类库 同java的Import 本函数有缓存功能 - * @param string $class 类库命名空间字符串 + * @param string $class 类库命名空间字符串 * @param string $baseUrl 起始路径 - * @param string $ext 导入的文件扩展名 + * @param string $ext 导入的文件扩展名 * @return boolean */ public static function import($class, $baseUrl = '', $ext = EXT) { static $_file = []; - $class = str_replace(['.', '#'], [DS, '.'], $class); + $class = str_replace(['.', '#'], [DS, '.'], $class); if (isset($_file[$class . $baseUrl])) { return true; } if (empty($baseUrl)) { list($name, $class) = explode(DS, $class, 2); - if (isset(self::$namespace[$name])) { + if (isset(self::$prefixDirsPsr4[$name])) { // 注册的命名空间 - $baseUrl = self::$namespace[$name]; + $baseUrl = self::$prefixDirsPsr4[$name]; } elseif ('@' == $name) { //加载当前模块应用类库 $baseUrl = App::$modulePath; @@ -283,13 +276,23 @@ class Loader $baseUrl .= DS; } // 如果类存在 则导入类库文件 - $filename = $baseUrl . $class . $ext; - if (is_file($filename)) { + if (is_array($baseUrl)) { + foreach ($baseUrl as $path) { + $filename = $path . DS . $class . $ext; + if (is_file($filename)) { + break; + } + } + } else { + $filename = $baseUrl . $class . $ext; + } + + if (!empty($filename) && is_file($filename)) { // 开启调试模式Win环境严格区分大小写 - if (IS_WIN && false === strpos(realpath($filename), $class . $ext)) { + if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) { return false; } - include $filename; + includeFile($filename); $_file[$class . $baseUrl] = true; return true; } @@ -298,10 +301,10 @@ class Loader /** * 实例化(分层)模型 - * @param string $name Model名称 - * @param string $layer 业务层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $common 公共模块名 + * @param string $name Model名称 + * @param string $layer 业务层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 * @return Object * @throws ClassNotFoundException */ @@ -332,10 +335,10 @@ class Loader /** * 实例化(分层)控制器 格式:[模块名/]控制器名 - * @param string $name 资源地址 - * @param string $layer 控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $empty 空控制器名称 + * @param string $name 资源地址 + * @param string $layer 控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $empty 空控制器名称 * @return Object|false * @throws ClassNotFoundException */ @@ -356,10 +359,10 @@ class Loader /** * 实例化验证类 格式:[模块名/]验证器名 - * @param string $name 资源地址 - * @param string $layer 验证层名称 - * @param bool $appendSuffix 是否添加类名后缀 - * @param string $common 公共模块名 + * @param string $name 资源地址 + * @param string $layer 验证层名称 + * @param bool $appendSuffix 是否添加类名后缀 + * @param string $common 公共模块名 * @return Object|false * @throws ClassNotFoundException */ @@ -405,10 +408,10 @@ class Loader /** * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作 - * @param string $url 调用地址 - * @param string|array $vars 调用参数 支持字符串和数组 - * @param string $layer 要调用的控制层名称 - * @param bool $appendSuffix 是否添加类名后缀 + * @param string $url 调用地址 + * @param string|array $vars 调用参数 支持字符串和数组 + * @param string $layer 要调用的控制层名称 + * @param bool $appendSuffix 是否添加类名后缀 * @return mixed */ public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false) @@ -432,14 +435,16 @@ class Loader /** * 字符串命名风格转换 * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 - * @param string $name 字符串 - * @param integer $type 转换类型 + * @param string $name 字符串 + * @param integer $type 转换类型 * @return string */ public static function parseName($name, $type = 0) { if ($type) { - return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) {return strtoupper($match[1]);}, $name)); + return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) { + return strtoupper($match[1]); + }, $name)); } else { return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } @@ -448,8 +453,9 @@ class Loader /** * 解析应用类的类名 * @param string $module 模块名 - * @param string $layer 层名 controller model ... - * @param string $name 类名 + * @param string $layer 层名 controller model ... + * @param string $name 类名 + * @param bool $appendSuffix * @return string */ public static function parseClass($module, $layer, $name, $appendSuffix = false) @@ -470,3 +476,19 @@ class Loader self::$instance = []; } } + +/** + * 作用范围隔离 + * + * @param $file + * @return mixed + */ +function includeFile($file) +{ + return include $file; +} + +function requireFile($file) +{ + return require $file; +} \ No newline at end of file