1

This is the implementation of composer PSR-4 autoload:

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;
                    }
                }
            }
        }
    }

And this is the example from PSR-4 document:

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;
}

You will find that the implementation from Composer has an additional judgement, namely:

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

I can't understand why should add the judgement. Can someone tell me?

rob006
  • 21,383
  • 5
  • 53
  • 74

1 Answers1

0

First, these two functions are taken out of context and do quite different things - even if you compare function parameters you can see that they do not correspond to each other, so comparing them does not make much sense.

But this additional condition in Composer's implementation ensures that more precise namespace takes precedence over more general definition. Because in general packages often share the same root namespace. For example in Yii 2 Framework:

  • yii2 package with core framework uses yii as root namespace for all classes, and source code is located in vendor/yiisoft/yii2.
  • yii2-redis extension uses yii\redis namespace, and source code is located in vendor/yiisoft/yii2-redis.

In this case if you want to resolve file with yii\redis\Connection class definiton you have two options:

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

The second one is correct. And thanks to this additional condition in Composer's implementation it will be used as first choice, since definition for yii\redis namespace is more precise as for yii namespace. In this way you can increase performance of autoloader (however it is irrelevant if you're using optimized autoloader), make it more predictable and allows you to override some classes (you just need to use more precise namespace for file with new implementation).

rob006
  • 21,383
  • 5
  • 53
  • 74