Composer自动加载机制源码剖析

1、autoload.php
要使用Composer的自动加载,首先需要引入该文件



// autoload.php @generated by Composer

// 引入autoload_real.php
require_once __DIR__ . '/composer' . '/autoload_real.php';

// 下面一大长串是在我们安装composer时由Composer自动生成的,我们需要关注的是它调用的autoload_real.php里的静态方法 getLoader()
return ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041::getLoader();

2、autoload_real.php



// autoload_real.php @generated by Composer

class ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041
{
    private static $loader;

    // 在实例化ClassLoader时调用该函数
    public static function loadClassLoader($class)
    {   
    // 该函数在getLoader方法里被注册为__autoload的实现,在实例化类时,如果类不存在,会自动调用该方法
    // 如果实例化的函数是Composer\Autoload\ClassLoader 则引入该类

        if ('Composer\Autoload\ClassLoader' === $class) {

            require __DIR__ . '/ClassLoader.php';
        }
    }

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

// 注册 loadClassLoader函数作为 __autoload 的实现   spl_autoload_register(array('ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041', 'loadClassLoader'), true, true);

        // 实例化该方法时会自动调用上述方法注册的函数,
        // loadClassLoader函数里引入 ClassLoader类
    self::$loader = $loader = new \Composer\Autoload\ClassLoader();

        spl_autoload_unregister(array('ComposerAutoloaderInit85b4dbf6b714d62ec745fcf3dd1a5041', 'loadClassLoader'));

        // PSR-0 的规则
        $map = require __DIR__ . '/autoload_namespaces.php';
        foreach ($map as $namespace => $path) {
            $loader->set($namespace, $path);
        }

    // PSR-4 的规则
        $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);
        }

    // 注册给定的函数作为 __autoload 的实现,具体参考下文
        // Class-Map部分
        $loader->register(true);

    // Files方式 直接加载需要访问的文件
        $includeFiles = require __DIR__ . '/autoload_files.php';
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire85b4dbf6b714d62ec745fcf3dd1a5041($fileIdentifier, $file);
        }

        return $loader;
    }
}

function composerRequire85b4dbf6b714d62ec745fcf3dd1a5041($fileIdentifier, $file)
{
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        // 加载需要调用的文件
        require $file;

        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    }
}

3、ClassLoader.php (部分核心代码)

① PSR-0 规则

     /**
     * Registers a set of PSR-0 directories for a given prefix,
     * replacing any others previously set for this prefix.
     *
     * @param string       $prefix The prefix
     * @param array|string $paths  The PSR-0 base directories
     */

    // $prefix、$paths分别相当于autoload_namespaces.php里返回数组的key和value值

    public function set($prefix, $paths)
    {   
        if (!$prefix) {
            $this->fallbackDirsPsr0 = (array) $paths;
        } else {
        // $prefix[0]取得是首个字符
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
        }
    }

② PSR-4规则

    /**
     * Registers a set of PSR-4 directories for a given namespace,
     * replacing any others previously set for this namespace.
     *
     * @param string       $prefix The prefix/namespace, with trailing '\\'
     * @param array|string $paths  The PSR-4 base directories
     *
     * @throws \InvalidArgumentException
     */
    public function setPsr4($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr4 = (array) $paths;
        } else {
            $length = strlen($prefix);

            // PSR-4 命名空间以 \ 结尾,如果不是,则不符合规则
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;

        }
    }

③ Class-map方式

    /**
     * @param array $classMap Class to filename map
     */
    public function addClassMap(array $classMap)
    {
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap, $classMap);
        } else {
            $this->classMap = $classMap;
        }
    }

 /**
     * Registers this instance as an autoloader.
     *
     * @param bool $prepend Whether to prepend the autoloader or not
     */
    public function register($prepend = false)
    {   // $prepend为true时,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。 (具体请参考php文档) 
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }

    /**
     * Loads the given class or interface.
     *
     * @param  string    $class The name of the class
     * @return bool|null True if loaded, null otherwise
     */
    public function loadClass($class)
    {   
        if ($file = $this->findFile($class)) {
            includeFile($file);

            return true;
        }
    }

什么时候才会去调用上述注册的loadClass函数呢?举例来说

 
require './vendor/autoload.php';
// 当我们实例化TestController的时候,就会自动调用spl_autoload_register注册的函数loadClass,在loadClass中又会去调用findFile方法去查找类文件所在的位置,然后require引入

$test = new TestController;

echo $test->show();

至此Composer自动加载流程已讲述完毕。具体的代码还需各位自己去研究。不得不惊叹,有了Composer真是太方便了!

你可能感兴趣的:(Composer探索)