简介

一般在框架中都会用到composer工具,用它来管理依赖。其中composer有类的自动加载机制,可以加载composer下载的库中的所有的类文件。那么composer的自动加载机制是怎么实现的呢?

composer 自动加载原理

以在Laravel框架中为例:

  1. 首先在入口文件(/public/index.php)中引入了autoload.php

    require __DIR__.'/../vendor/autoload.php';
  2. 我们看看autoload.php的内容

    require_once __DIR__ . '/composer/autoload_real.php';
    return ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader();
  3. 我们再看看 autoload_real.php的内容

    <?php
    
    // autoload_real.php @generated by Composer
    
    class ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273
    {
    private static $loader; public static function loadClassLoader($class)
    {
    if ('Composer\Autoload\ClassLoader' === $class) {
    require __DIR__ . '/ClassLoader.php';
    }
    } public static function getLoader()
    {
    if (null !== self::$loader) {
    return self::$loader;
    }
    spl_autoload_register(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', 'loadClassLoader'), true, true);
    self::$loader = $loader = new \Composer\Autoload\ClassLoader();
    spl_autoload_unregister(array('ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273', '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\ComposerStaticInit1215780529014c2b50a6fca7ce889273::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\ComposerStaticInit1215780529014c2b50a6fca7ce889273::$files;
    } else {
    $includeFiles = require __DIR__ . '/autoload_files.php';
    }
    foreach ($includeFiles as $fileIdentifier => $file) {
    composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier, $file);
    } return $loader;
    }
    } function composerRequire1215780529014c2b50a6fca7ce889273($fileIdentifier, $file)
    {
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
    require $file; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    }
    }

可以看出这一段是composer自动加载的重点,首先在 autoload.php中调用ComposerAutoloaderInit1215780529014c2b50a6fca7ce889273::getLoader()方法,getLoader()首先判断当前\(loader是不是null,如果不为null就直接返回,否则就初始化一个ClassLoader类给赋值给\)loader,接着将autoload_namespaces.php、autoload_psr4.php、autoload_classmap.php文件中的内容加入到$loader中对应的数组中,然后给注册loadClass函数,将autoload_files.php中的所有路径所示的文件都包含进来,当在new一个类的时候如果没有找到相关的类就会触发这个loadClass函数,在loadClass()又调用了findFile()去查找相应的文件,找到相应文件后就会返回该文件,然后loadClass调用includeFile()方法将该文件include进去,否则findFile返回false,这样就完成了自动加载

  1. 下面来看一下 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;
    } 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;
    }

    findFile()函数先在classMap中查找,如果找不到的话就会尝试在apcu缓存中查找,如果还是找不到的话就会调用findFileWithExtension()函数查找,如果找到了就会将该文件加到apcu缓存,如果找不到的话就会在missingClasses数组中设一个标记表示识这个类找不到

    findFileWithExtension()方法根据之前通过\(loader->set(\)namespace, \(path)和\)loader->setPsr4($namespace, $path)方法设置的信息找出类文件的路径信息

  2. 在上面有的地方用到了 spl_autoload_registerspl_autoload_unregister函数

    1. spl_autoload_register函数

      spl_autoload_register — 注册给定的函数作为 __autoload 的实现,

      1. bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )

      2. prepend

        如果是 true,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。

      3. 如果在你的程序中已经实现了__autoload()函数,它必须显式注册到__autoload()队列中。因为 spl_autoload_register()函数会将Zend Engine中的__autoload()函数取代为spl_autoload()或spl_autoload_call()

        例:

        function __autoload($name)
        {
        require 'class/'.$name.'.php';
        echo '1';
        }
        function autoload_test($name)
        {
        echo '2';
        }
        spl_autoload_register('autoload_test');
        spl_autoload_register('__autoload');
        $ca=new Ca();
    2. spl_autoload_unregister函数

      spl_autoload_unregister — 注销已注册的__autoload()函数,如果该函数队列处于激活状态,并且在给定函数注销后该队列变为空,则该函数队列将会变为无效。如果该函数注销后使得自动装载函数队列无效,即使存在有__autoload函数它也不会自动激活。

      1. bool spl_autoload_unregister ( mixed $autoload_function )

因本人水平有限,有些地方可能不对,欢迎留言。

如需转载请注明出处:http://www.cnblogs.com/zhuchenglin/p/8954364.html

composer 实现自动加载原理的更多相关文章

  1. 深入解析 composer 的自动加载原理 (转)

    深入解析 composer 的自动加载原理 转自:https://segmentfault.com/a/1190000014948542 前言 PHP 自5.3的版本之后,已经重焕新生,命名空间.性状 ...

  2. 深入解析 composer 的自动加载原理

    PHP 自5.3的版本之后,已经重焕新生,命名空间.性状(trait).闭包.接口.PSR 规范.以及 composer 的出现已经让 PHP 变成了一门现代化的脚本语言.PHP 的生态系统也一直在演 ...

  3. Composer 的自动加载机制

    Composer 的自动加载机制 Composer 提供了四种自动加载方式,分别是 PSR-0.PSR-4.生成 classmap 以及之间包含 files. PSR-0 方式 PSR-0 方式要求目 ...

  4. 详解composer的自动加载机制

    composer是一个用PHP开发的用来管理项目依赖的工具,当你在项目中声明了依赖关系后,composer可以自动帮你下载和安装这些依赖库,并实现自动加载代码. 安装composer composer ...

  5. composer的自动加载机制(autoload)

    composer的出现真是让人们眼前一亮,web开发从此变成了一件很『好玩』的事情,开发一个CMS就像在搭积木,从packagist中取出『积木』搭建在自己的代码中,一点一点搭建出一个属于自己的王国. ...

  6. 使用PSR-4配合composer autoload 自动加载文件夹

    require 文件很麻烦,使用PSR-4搭配composer一次加载,终生受用. 感觉类似java中的import了,自己先记录一下最近理解的. 用composer管理自己的包吧 安装compose ...

  7. 使用composer 实现自动加载

    准备工作:提前安装好composer 1.创建项目目录OOP 2.OOP目录下新建composer.json文件,composer.json是一个空json文件,代码如下: { } 3.打开控制台,进 ...

  8. php自动加载类文件探讨,spl_autoload_register自动加载原理

    spl_autoload_register函数是实现自动加载未定义类功能的的重要方法,所谓的自动加载意思就是 我们的new 一个类的时候必须先include或者require的类文件,如果没有incl ...

  9. composer 自动加载原理

    核心当然是php5加入来的_autoload函数,当实例化一个不存在的类时,在报错之前,如果定义了_autoload函数,会进行调用此函数,此函数就可以执行相关的include操作. <?php ...

随机推荐

  1. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

  2. CentOS 安装 Docker

    前言:其实安装步骤Docker官网很详细,如果有些人英文不好看的比较慢的话就可以直接看我的,我也是摘自官网,具体步骤如下 1. 安装依赖包 $ sudo yum install -y yum-util ...

  3. spring-mvc访问本地html文件

    项目中要用到在线预览word文档,刚开始考虑是要将word转成pdf文件,然后再直接在浏览器打开pdf文档即可, 但是项目部署在Linux下,在网上搜了一下没有找到合适的方法, 后来项目组讨论用POI ...

  4. docker bulid命令

    使用当前目录的 Dockerfile 创建镜像,标签为 runoob/ubuntu:v1. docker build -t runoob/ubuntu:v1 . 使用URL github.com/cr ...

  5. Linux计划任务及压缩归档

    计划任务介绍 自动执行,备份数据. crontab    和   at : at:它是一个可以处理仅执行一次就结束的指令 crontab:它是会把你指定的工作或任务,比如:脚本等,按照你设定的周期一直 ...

  6. html_之css

    css 有三种形式的写法: 直接在标签里写入style样式 在<head></head>里写入<style></style>样式 直接创建.css 文件 ...

  7. Invitation Cards POJ - 1511 (双向单源最短路)

    In the age of television, not many people attend theater performances. Antique Comedians of Malidine ...

  8. 用Sklearn画一颗决策树

    小伙伴们大家好~o( ̄▽ ̄)ブ,首先声明一下,我的开发环境是Jupyter lab,所用的库和版本大家参考: Python 3.7.1(你的版本至少要3.4以上 Scikit-learn 0.20.0 ...

  9. 微信小程序视频学习笔记

    [清华大学]学做小程序 https://www.bilibili.com/video/av21987398 2.2创建项目和文件结构 小程序包含一个描述整体程序的app和多个描述各自页面的page 配 ...

  10. linux服务器用ssh 公钥下载代码 创建公钥,添加

    ssh-keygen -t rsa -C "8....@qq.com"cd ~/.sshcat id_rsa.pub 粘贴到对应的 https://github.com/setti ...