zoukankan      html  css  js  c++  java
  • composer 实现自动加载原理

    简介

    一般在框架中都会用到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 ('ComposerAutoloadClassLoader' === $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 ComposerAutoloadClassLoader();
              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(ComposerAutoloadComposerStaticInit1215780529014c2b50a6fca7ce889273::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 = ComposerAutoloadComposerStaticInit1215780529014c2b50a6fca7ce889273::$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

  • 相关阅读:
    格式化数字,将字符串格式的数字,如:1000000 改为 1 000 000 这种展示方式
    jquery图片裁剪插件
    前端开发采坑之安卓和ios的兼容问题
    页面消息提示,上下滚动
    可以使用css的方式让input不能输入文字吗?
    智慧农村“三网合一”云平台测绘 大数据 农业 信息平台 应急
    三维虚拟城市平台测绘 大数据 规划 三维 信息平台 智慧城市
    农业大数据“一张图”平台测绘 大数据 房产 国土 农业 信息平台
    应急管理管理局安全生产预警平台应急管理系统不动产登记 测绘 大数据 规划 科教 三维 信息平台
    地下综合管廊管理平台测绘 大数据 地下管线 三维 信息平台
  • 原文地址:https://www.cnblogs.com/zhuchenglin/p/8954364.html
Copyright © 2011-2022 走看看