autoload之composer分析

来源:互联网 发布:tvp动画软件 编辑:程序博客网 时间:2024/06/06 00:18

这里要介绍的不是composer.phar,而且由php composer.phar install生成的整一个composer文件下的autoload的结构。

想在项目总引入composer进行包管理,都会说将需要的包填到composer.json文件,然后执行composer install,会在vender文件夹下生成autoload.php,项目中引入这个文件就可以实现类的自动加载了。

那引入autoload.php之后到底做了帮我们做了什么,我们一起来看看。


首先查看autoload.php:

<?php// autoload.php @generated by Composerrequire_once __DIR__ . '/composer' . '/autoload_real.php';return ComposerAutoloaderInit621850f9ac0e7f3f7c90ee4affc93eee::getLoader();

这里是require了一个compsoer/autoload_real.php文件,然后返回了一个getLoader()静态方法,看来我们要进去这个方法里看一下。

(ps: autoload_real.php 这个名字中也能看出,这才是真正实现autoload机制的地方)

<?php// autoload_real.php @generated by Composerclass ComposerAutoloaderInit621850f9ac0e7f3f7c90ee4affc93eee{    private static $loader;    public static function loadClassLoader($class)    {        if ('Composer\Autoload\ClassLoader' === $class) {            require __DIR__ . '/ClassLoader.php';        }    }    public static function getLoader()    {        //如果成员$loader已经赋值就直接返回,典型的单例模式        if (null !== self::$loader) {            return self::$loader;        }        //将ComposerAutoloaderInit621850f9ac0e7f3f7c90ee4affc93eee类中的loadClassLoader方法注册为自动类加载处理函数        spl_autoload_register(array('ComposerAutoloaderInit621850f9ac0e7f3f7c90ee4affc93eee', 'loadClassLoader'), true, true);        //使用新注册的loadClassLoader方法加载\Composer\Autoload\ClassLoader类,并实例化一个对象        self::$loader = $loader = new \Composer\Autoload\ClassLoader();        //注销loadClassLoader        spl_autoload_unregister(array('ComposerAutoloaderInit621850f9ac0e7f3f7c90ee4affc93eee', 'loadClassLoader'));        //如果php版本大于5.6并且未定义HHVM_VERSION常量,返回true        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');        if ($useStaticLoader) {            //autoload_static.php 记录了namespace+类名与类所在文件的映射关系            require_once __DIR__ . '/autoload_static.php';            //降映射关系加载到$loader里            call_user_func(\Composer\Autoload\ComposerStaticInit621850f9ac0e7f3f7c90ee4affc93eee::getInitializer($loader));        } else {            //设置单独namespace对于的文件夹位置的映射关系            $map = require __DIR__ . '/autoload_namespaces.php';            foreach ($map as $namespace => $path) {                $loader->set($namespace, $path);            }            //符合psr4命名规范的namespace对于的文件夹位置的映射关系            $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);        //需要预先require的文件        if ($useStaticLoader) {            $includeFiles = Composer\Autoload\ComposerStaticInit621850f9ac0e7f3f7c90ee4affc93eee::$files;        } else {            $includeFiles = require __DIR__ . '/autoload_files.php';        }        foreach ($includeFiles as $fileIdentifier => $file) {            composerRequire621850f9ac0e7f3f7c90ee4affc93eee($fileIdentifier, $file);        }        return $loader;    }}function composerRequire621850f9ac0e7f3f7c90ee4affc93eee($fileIdentifier, $file){    //循环导入文件,并记录到$GLOBALS['__composer_autoload_files']变量中    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {        require $file;        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;    }}

到这里,对autoload的初始化就完成了,接下来是对一个类的查找工作。

在composer/ClassLoader.php 

public function register($prepend = false)    {        spl_autoload_register(array($this, 'loadClass'), true, $prepend);    }
使用本类中的loadClass()方法来处理未找到的类:

public function loadClass($class)    {        if ($file = $this->findFile($class)) {            includeFile($file);            return true;        }    }
这方法就是先查找该类是否存在,存在则include进来。再看看findFile()方法是如何查找并返回类的:

/**     * Finds the path to the file where the class is defined.     *     * @param string $class The name of the class     *     * @return string|false The path if found, false otherwise     */    public function findFile($class)    {        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731        //如果类名以\开头,则从第二字符开始截取类名        if ('\\' == $class[0]) {            $class = substr($class, 1);        }        // class map lookup        //在$this->classMap中查找$class,找到就返回文件名        if (isset($this->classMap[$class])) {            return $this->classMap[$class];        }        //是否有权利访问classMap        if ($this->classMapAuthoritative) {            return false;        }        //上面没匹配到,这里再匹配        $file = $this->findFileWithExtension($class, '.php');        // Search for Hack files if we are running on HHVM        if ($file === null && defined('HHVM_VERSION')) {            $file = $this->findFileWithExtension($class, '.hh');        }        if ($file === null) {            // Remember that this class does not exist.            return $this->classMap[$class] = false;        }        return $file;    }    private function findFileWithExtension($class, $ext)    {        // PSR-4 lookup        //将\\替换成/        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;        //取类的首字母        $first = $class[0];        //prefixLengthsPsr4中根据首字母进行了分类,可查看autoload_static.php中的prefixLengthsPsr4成员变量        if (isset($this->prefixLengthsPsr4[$first])) {            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {                if (0 === strpos($class, $prefix)) {                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {                            return $file;                        }                    }                }            }        }        // PSR-4 fallback dirs        //自定义的psr4规则        foreach ($this->fallbackDirsPsr4 as $dir) {            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {                return $file;            }        }        // PSR-0 lookup        //如果是符合psr0规则的,则替换_为/        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;        }        //prefixesPsr0中根据首字母进行了分类,可查看autoload_static.php中的prefixesPsr0成员变量        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        //用户添加符合psr0的规则        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;        }    }

顺序是先查classMap,因为大部分类的映射关系都在这个变量里,然后下面findFileWithExtension()方法里是以一些其他维度进行查询。

整体class的autoload的init和find就是这样,好像主要的工作还是在php composer.phar install的时候已经完成了,这个时候就已经理出了类名和类所在文件的映射关系。







0 0