zoukankan      html  css  js  c++  java
  • composer源码简单分析(一)

    composer分析(一)

    本文内容

    • 基于PSR-4规范的自动加载 请结合文档和下面的代码注释
    • spl_autoload_register
    • php闭包Closure简单用法(大体使用情景: 生成回调提供使用, 使用闭包绑定类中的成员属性和方法)

    1. 框架引入composer自动加载器

      require __DIR__ . '/../vendor/autoload.php';
      
    2. autoload.php文件内容

      <?php
      
      // autoload.php @generated by Composer
      
      require_once __DIR__ . '/composer/autoload_real.php';
      
      // 返回自动加载器 object(ComposerAutoloadClassLoader)
      return ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a::getLoader();
      
    3. getLoader方法

      /**
           * @return ComposerAutoloadClassLoader
           */
      public static function getLoader()
      {
          // 单例
          if (null !== self::$loader) {
              return self::$loader;
          }
      
          // spl_autoload_register方法第一个参数为用户自定义的自动加载器,为一个callable,接收的参数为要加载类名
          // 如self::$loader = $loader = new ComposerAutoloadClassLoader(); 此脚本中并没有ClassLoader这个类,由于注册了自动加载器loadClassLoader回调,
          // 此方法就会接收到ComposerAutoloadClassLoader这个字符串 手动require到本文件中
      
          // 第二个参数true表示注册加载器失败时抛出异常
          // 第三个参数true表示 将此自动加载器放到自动加载队列的首部,laravel中还使用此函数进行了类别名注册
          spl_autoload_register(array('ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a', 'loadClassLoader'), true, true);
          // 给loader赋值
          self::$loader = $loader = new ComposerAutoloadClassLoader();
          // 卸载注册的自动加载回调
          spl_autoload_unregister(array('ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a', 'loadClassLoader'));
      
          // 正常情况使用高版本php 此值恒为true
          $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
          if ($useStaticLoader) {
              // require此静态加载文件 包含了大量供PSR-4使用的映射关系 后面会讲解composer包下各文件的用处
              require_once __DIR__ . '/autoload_static.php';
              // 重点在此回调的调用!!!
              // 调用返回的闭包 注册映射关系到loader中
              call_user_func(ComposerAutoloadComposerStaticInit251f259405d38ea713d5c2b4f861292a::getInitializer($loader));
          } else {
              // 如果$useStaticLoader为false 调用loader的set方法 手动注册映射关系到loader中 供后续加载时直接查找require文件
              $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中存在映射关系后 注册真正的自动加载器 loadClass方法
          $loader->register(true);
      
          if ($useStaticLoader) {
              // 找到composer要自动加载的全局函数文件
              $includeFiles = ComposerAutoloadComposerStaticInit251f259405d38ea713d5c2b4f861292a::$files;
          } else {
              // 依然是找到composer提供的全局函数 和上面的功能一致
              $includeFiles = require __DIR__ . '/autoload_files.php';
          }
          foreach ($includeFiles as $fileIdentifier => $file) {
              composerRequire251f259405d38ea713d5c2b4f861292a($fileIdentifier, $file);
          }
      
          return $loader;
      }
      
    4. autoload_static.php

      public static function getInitializer(ClassLoader $loader)
      {   
          // Closure bind方法返回的是一个闭包
          return Closure::bind(function () use ($loader) {
              // 绑定映射关系到loader中
              $loader->prefixLengthsPsr4 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixLengthsPsr4;
              $loader->prefixDirsPsr4 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixDirsPsr4;
              $loader->prefixesPsr0 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixesPsr0;
              $loader->classMap = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$classMap;
      
          }, null, ClassLoader::class);
      }
      
    5. ClassLoader.php

      /**
           * Registers this instance as an autoloader.
           *
           * @param bool $prepend Whether to prepend the autoloader or not
           */
      public function register($prepend = false)
      {
          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;
          }
      }
      
      
      /**
       * Scope isolated include.
       *
       * Prevents access to $this/self from included files.
       */
      function includeFile($file)
      {
          // 加载文件
          include $file;
      }
      
      
      /**
           * 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)
      {
          // class map lookup
          // classmap中保存的是类名和其绝对路径的映射关系
          if (isset($this->classMap[$class])) {
              // var_dump($class); // IlluminateFoundationApplication
              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
          // 处理文件名
          // IlluminateFoundationApplication 假设路径映射中不存在此类名
          $logicalPathPsr4 = strtr($class, '\', DIRECTORY_SEPARATOR) . $ext;
          // 拿到文件名的第一个字母 依然去找映射关系
          $first = $class[0];
          if (isset($this->prefixLengthsPsr4[$first])) {
              $subPath = $class; // IlluminateFoundationApplication
              // 通过此步骤拿到类名的所有层级的命名空间  IlluminateFoundation
              while (false !== $lastPos = strrpos($subPath, '\')) {
                  $subPath = substr($subPath, 0, $lastPos); // IlluminateFoundation
                  $search = $subPath . '\';
                  // prefixDirsPsr4顶级命名空间前缀
                  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;
              }
          }
      
        	...
      
          return false;
      }
      

    Closure使用简单示例

    <?php
    
    $a = function () {
        echo '123';
        var_dump($this);
    };
    // var_dump($a);
    
    // $a(); 报错
    
    $abc = function () {
        $this->privateVar = 456;
        echo $this->privateVar, PHP_EOL;
    };
    
    $cba = function () {
        echo $this->privateVar, PHP_EOL;
    };
    
    class MyClousre
    {
        public $callback;
        private $privateVar = 123;
    
        // 在类内部可以直接使用 $this
        public function zbc(callable $callable)
        {
            // var_dump($callable);
            $this->callback = $callable->bindTo($this, __CLASS__); // bindTo的作用是 将闭包绑定到指定的类 打通内部状态 是闭包中可以访问类的成员
            // var_dump($callable);
        }
    
        public function zbcc()
        {
            call_user_func($this->callback);
            ($this->callback)();
        }
    }
    
    $mc = new MyClousre();
    $mc->zbc($abc);
    // $mc->zbc([new Test(), 'aaa']); // 测试结果是 bindTo只能是用在Closure对象上
    $mc->zbcc();
    
    // class Test
    // {
    //     public static function aaa()
    //     {
    //         echo $this->privateVar, PHP_EOL;
    //     }
    // }
    
    
    // 可以达到的效果 在闭包中使用对象中的各种成员
    // 在类的外部 不能直接绑定$this 要先new  
    $cba = $cba->bindTo(new MyClousre(), MyClousre::class);
    $cba();
    
    
    // bind的使用
    $foo = function () {
        echo $this->zbc() . PHP_EOL;
    };
    
    class Haiyoushui
    {
        private function zbc()
        {
            return 'bayehelie';
        }
    }
    
    $bar = Closure::bind($foo, new Haiyoushui(), Haiyoushui::class);
    $bar();
    
    

    发现错误欢迎指正,感谢~~

    下期预告:composer下的各个文件内容的生成和意义及PSR-4示例讲解

  • 相关阅读:
    提前期分类
    物料属性,MRP/MPS属性
    ASP.NET刷新页面的一些方法
    Nothing 和 Is
    三层架构与MVC
    ADO.NET
    软件工程之数据流程图(DFD Data Flow Diagram)
    VB.NET小结
    推荐开发人员看的具有影响力的书籍
    C++考试
  • 原文地址:https://www.cnblogs.com/alwayslinger/p/13341178.html
Copyright © 2011-2022 走看看