首页 > 解决方案 > Composer PSR-4自动加载的实现与PSR-4文档中的示例有什么区别

问题描述

这是 composer PSR-4 自动加载的实现:

private function findFileWithExtension($class, $ext)
{
    // PSR-4 lookup
    $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

    $first = $class[0];

    if (isset($this->prefixLengthsPsr4[$first])) {
        $subPath = $class;
        while (false !== $lastPos = strrpos($subPath, '\\')) {
            $subPath = substr($subPath, 0, $lastPos);
            $search = $subPath . '\\';
            if (isset($this->prefixDirsPsr4[$search])) {
                $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                foreach ($this->prefixDirsPsr4[$search] as $dir) {
                    if (file_exists($file = $dir . $pathEnd)) {
                        return $file;
                    }
                }
            }
        }
    }

这是 PSR-4 文档中的示例:

protected function loadMappedFile($prefix, $relative_class)
{
    // are there any base directories for this namespace prefix?
    if (isset($this->prefixes[$prefix]) === false) {
        return false;
    }

    // look through base directories for this namespace prefix
    foreach ($this->prefixes[$prefix] as $base_dir) {

        // replace the namespace prefix with the base directory,
        // replace namespace separators with directory separators
        // in the relative class name, append with .php
        $file = $base_dir
              . str_replace('\\', '/', $relative_class)
              . '.php';

        // if the mapped file exists, require it
        if ($this->requireFile($file)) {
            // yes, we're done
            return $file;
        }
    }

    // never found it
    return false;
}

你会发现Composer的实现多了一个判断,即:

if (isset($this->prefixLengthsPsr4[$first])) {
    // ...
}

我不明白为什么要添加判断。有人可以告诉我吗?

标签: composer-phppsr-4

解决方案


首先,这两个函数脱离了上下文,做了完全不同的事情——即使你比较函数参数,你也可以看到它们并不对应,所以比较它们没有多大意义。

但是 Composer 实现中的这个附加条件确保了更精确的命名空间优先于更一般的定义。因为通常包通常共享相同的根命名空间。例如在 Yii 2 框架中:

  • yii2具有核心框架的包yii用作所有类的根命名空间,源代码位于vendor/yiisoft/yii2.
  • yii2-redis扩展使用yii\redis命名空间,源代码位于vendor/yiisoft/yii2-redis.

在这种情况下,如果您想使用yii\redis\Connection类定义解析文件,您有两个选择:

  1. vendor/yiisoft/yii2/redis/Connection,
  2. vendor/yiisoft/yii2-redis/Connection.

第二个是正确的。并且由于 Composer 实现中的这个附加条件,它将被用作首选,因为命名空间的定义yii\redis比命名空间更精确yii。通过这种方式,您可以提高自动加载器的性能(但如果您使用优化的自动加载器则无关紧要),使其更可预测并允许您覆盖某些类(您只需要为具有新实现的文件使用更精确的命名空间)。


推荐阅读