Composer自动加载机制源码剖析

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

<?php // 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

<?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函数呢?举例来说

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

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

你可能感兴趣的:(Composer)