Heim  >  Artikel  >  Entwicklungswerkzeuge  >  Detaillierte Erläuterung des automatischen Lademechanismus des Composers

Detaillierte Erläuterung des automatischen Lademechanismus des Composers

藏色散人
藏色散人nach vorne
2020-07-06 13:34:353396Durchsuche

Die folgende Tutorial-Kolumne von Composer wird den automatischen Lademechanismus des Composers von flach bis tief vorstellen. Ich hoffe, dass er Freunden in Not hilfreich sein wird!

Detaillierte Erläuterung des automatischen Lademechanismus des Composers

Vorwort

Denn für den Komponisten automatisch Der einzige verbleibende Speicher im Lademechanismus ist „spl_auto???“ und „Ableiten des Dateipfads basierend auf dem Namespace“. . . Noch unvollständig. .

Ich wollte eine ausführliche Erklärung online speichern, konnte aber keinen „Vom Einfachen zum Tiefgründigen“-Artikel finden, der meiner Meinung entspricht.
Hier ist also diese Notiz.

Die folgenden Wissenspunkte folgen in Kürze:
1. Erfahren Sie mehr über spl_autoload_register
2. Die Geschichte des Composer-Updates

Text

1. Erfahren Sie mehr über spl_autoload_register

Schauen Sie sich zunächst das offizielle PHP-Handbuch an:


Detaillierte Erläuterung des automatischen Lademechanismus des Composers(Sie können sich einfach den roten Teil ansehen, wenn Sie faul sind)

Ist das richtig? Scheinbar unwissend?

Übersetzen wir es in die Landessprache:

Wenn wir eine neue Klasse erstellen, müssen wir zuerst die Klassendatei anfordern oder einschließen. Wenn sie nicht geladen wird, wird ein Fehler gemeldet. Dies führt zu einem Problem: In diesem Fall ist der Header der Datei voller Anforderungen und Includes, was offensichtlich nicht dem Bedürfnis des Programmierers entspricht, „faul“ zu sein.

Um eine neue Klasse normal zu erstellen, ohne dass Klassendateien erforderlich sind oder eingeschlossen werden, wurde ein automatischer Lademechanismus entwickelt. Die Funktion spl_autoload_register ist speziell dafür konzipiert.

Aus dem Screenshot wissen wir, dass diese Funktion drei Parameter hat:

来一波代码,印象深刻一些:

//文件 testClass.php ,即将new的类
class TestClass{
    public function __construct() {
        echo '你已经成功new了我了';
    }
}

//文件autoloadDemo.php文件
spl_autoload_register('autoLoad_function', true, true);
function autoLoad_function($class_name){
    echo "所有的require或者include文件工作都交给我吧!\r\n";
    $class_filename = "./{$class_name}.php";
    echo "我来加载{$class_filename}文件\r\n";
    require_once("./{$class_name}.php");
}
$obj_demo = new TestClass();

输出:

所有的require或者include文件工作都交给我吧!
我来加载testClass.php文件
你已经成功new了我了

明白了这个加载的原理,看下文就顺利多了。

2.composer update发生的故事

将自动加载之前,必须要先说一下composer update,这里头承载了自动加载的前提。

composer项目都包含一个composer.json的配置文件。
Detaillierte Erläuterung des automatischen Lademechanismus des Composers
这里头有一个关键的字段"autoload",包含psr-4和files两个字段。

psr-4:说明是基于psr-4规范的类库,都支持自动加载,只要在后面的对象中以**“命名空间:路径”**的方式写入自己的类库信息即可。
files:这就就更直接了,写入路径就自动加载。

按照以上配置每回composer update之后呢,都会更新一个很重要的文件:./vender/composer/autoload_psr4.php。
Detaillierte Erläuterung des automatischen Lademechanismus des Composers

这个文件只做了一件事情:把命名空间和文件路径对应起来,这样后续自动加载就有映射根据了。

3.追踪一下composer的自动加载

composer的故事从唯一的一个require说起:

require '../vendor/autoload.php'

这个脚本执行了一个函数:

ComposerAutoloaderInitd9b31141b114fcbee3cf55d0e97b7f87::getLoader()

继续跟getloader函数做了什么?

public static function getLoader() {
   if (null !== self::$loader) {
        return self::$loader;
    }

    spl_autoload_register(array('ComposerAutoloaderInitd9b31141b114fcbee3cf55d0e97b7f87', 'loadClassLoader'), true, true);
    self::$loader = $loader = new \Composer\Autoload\ClassLoader();
    spl_autoload_unregister(array('ComposerAutoloaderInitd9b31141b114fcbee3cf55d0e97b7f87', 'loadClassLoader'));

    $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
    if ($useStaticLoader) {
        require_once __DIR__ . '/autoload_static.php';

        call_user_func(\Composer\Autoload\ComposerStaticInitd9b31141b114fcbee3cf55d0e97b7f87::getInitializer($loader));
    } else {
        $map = require __DIR__ . '/autoload_namespaces.php';
        foreach ($map as $namespace => $path) {
            $loader->set($namespace, $path);
        }

        $map = require __DIR__ . '/autoload_psr4.php';
        foreach ($map as $namespace => $path) {
            $loader->setPsr4($namespace, $path);
        }

        $classMap = require __DIR__ . '/autoload_classmap.php';
        if ($classMap) {
            $loader->addClassMap($classMap);
        }
    }

    $loader->register(true);

    if ($useStaticLoader) {
        $includeFiles = Composer\Autoload\ComposerStaticInitd9b31141b114fcbee3cf55d0e97b7f87::$files;
    } else {
        $includeFiles = require __DIR__ . '/autoload_files.php';
    }
    foreach ($includeFiles as $fileIdentifier => $file) {
        composerRequired9b31141b114fcbee3cf55d0e97b7f87($fileIdentifier, $file);
    }

    return $loader;
}

这个函数主要做了两件事情:
1.将各种存有命名空间和文件映射关系的文件autoload_xxx.php加载了进来,并作了一些处理(比如:setPsr4将相关映射加载了进去,这个留意下,下文会有呼应。)。
2.注册了函数register

继续跟踪register做了什么:

public function register($prepend = false) {
   spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}

原来调用了spl_autoload_register函数,当类没加载的时候使用loadClass来加载类。(这个前文讲的很清楚了,应该很熟了)

继续跟踪loadClass实现:

public function loadClass($class) {
	if ($file = $this->findFile($class)) {
		includeFile($file);
		return true;
	}
}

大概可以看出,是做了文件的include。
继续跟踪下是怎么查找文件的,看findFile函数:

public function findFile($class) {
    // class map lookup
    if (isset($this->classMap[$class])) {
        return $this->classMap[$class];
    }
    if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
        return false;
    }
    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;
}

这个函数做了一件事:就是寻找类从上文的autoload_xxx.php初始化的数据中来寻找映射的文件路径。
其中这个函数findFileWithExtension,适用于寻找psr-4规范的文件的映射信息的。

继续跟踪findFileWithExtension:

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

这个函数做了件事:将命名空间\类这样的类名,转换成目录名/类名.php这样的路径,再从前文setPsr4设置的映射信息中寻找映射信息,然后完成返回路径。

至此composer的自动加载机制结束。

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des automatischen Lademechanismus des Composers. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen