Laravel第三方包报class not found的解决方法(2)
ClassLoader的register方法
public function register($prepend = false) { // 调用ClassLoader类的loadClass方法 spl_autoload_register(array($this, 'loadClass'), true, $prepend); }
ClassLoader类的loadClass方法
public function loadClass($class) { // 查找文件,如果查找到文件,则加载文件 if ($file = $this->findFile($class)) { includeFile($file); return true; } }
ClassLoader类的findFile方法
public function findFile($class) { // class map lookup // class map加载方式,我的理解:是通过将类与对应路径生成一个对应表 // 该方式优点:加载速度快,相当于查询字典; // 缺点:无法实现自动加载,添加新类后,需要对应维护class map if (isset($this->classMap[$class])) { return $this->classMap[$class]; } // $classMapAuthoritative默认值为false,流程到目前,没有设置过该值 // $missingClasses通过查看该方法最后几行,发现作用是记录自动加载过程中不存在的文件 // 所以这里第一次加载会返回false if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } // APCu 是老牌 PHP 字节码和对象缓存,缓存器 APC 的分支(PS:我也是查的,不懂呀~大家感兴趣可以自己深研究) // 经测试,$this->apcuPrefix=null if (null !== $this->apcuPrefix) { $file = apcu_fetch($this->apcuPrefix.$class, $hit); if ($hit) { return $file; } } // 最后一层方法(保证是最后一个方法) $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if (null !== $this->apcuPrefix) { apcu_add($this->apcuPrefix.$class, $file); } // 记录无法找到的类,方便再次加载直接返回 if (false === $file) { // Remember that this class does not exist. $this->missingClasses[$class] = true; } return $file; }
ClassLoader类中findFileWithExtension方法
private function findFileWithExtension($class, $ext) { // 终于看到加载psr-4了 // PSR-4 lookup // 对路径中的\转换为文件系统中对应路径分隔符并+后缀, // 比如wan\test类,最后处理为wan/test.php(linux下) $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; // 获得类名中第一个字母,主要用于在ClassLoader中prefixLengthsPsr4快速检索包,并找到对应包前缀长度,后面截取时使用 // 对比autoload_static.php中的$prefixLengthsPsr4即可明白作用 $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { // 从右往左一层层循环类名中的路径 $subPath = substr($subPath, 0, $lastPos); $search = $subPath.'\\'; // 找到对应composer包前缀后,取出对应路径,将包前缀截取后,替换成对应的目录路径,即为class所对应文件 if (isset($this->prefixDirsPsr4[$search])) { foreach ($this->prefixDirsPsr4[$search] as $dir) { $length = $this->prefixLengthsPsr4[$first][$search]; if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } // 到这里psr-4文件就加载完了,后面是psr-0等其他文件加载,这里就不分析了。 // 这里分析一下为什么是第三方包psr-4格式错误 // 比如包名为wan/lib,即composer安装命令对应composer require wan/lib // 第三方包中autoload psr-4配置为 "psr-4" : { "wan\\" : "src" } // (**警告:上面是错误配置,为了举例说明;正确应该是"psr-4" : { "wan\\lib\\" : "src" }) // 最终生成的$prefixLengthsPsr4为{'w' =>array ('wan\\' => 5,),} // 生成$prefixDirsPsr4为'wan\\' => array (0 => __DIR__ . '/..' . '/wan/lib/src',), // 对应上面代码,在最后$file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length) // $file拼接出来的路径是vendor/wan/lib/src/lib/$className.php,导致最后无法拼接出正确路径 // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } // PSR-0 include paths. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } return false; }
内容版权声明:除非注明,否则皆为本站原创文章。