為什麼Composer在生產環境要使用dumpautoload

  • 2019 年 12 月 9 日
  • 筆記

Composer 作為現代 phper 的春天,遠離重複造輪子的時代,大部分擴展包遵循 psr-4 規範,使得擴展更加輕鬆,減輕了工作的部分壓力

這篇文章來說一下為什麼在生產環境下使用 Composer 載入包後要再使用 dumpautoload 呢?

composer dump-autoload (-o)  composer dumpautoload (-o)

這個就要看一下 vendor/composer 目錄下的文件了,先看一下 autoload_real.php

類名為 ComposerAutoloaderInit440563a888dcb3a8c02b3ef8400e84e8,ComposerAutoloaderInit 後為一段 hash 值

這個也是為了避免命名衝突,每次 composer install 都會生成不一樣的值

再往下看 getLoader 這個方法

public static function getLoader()  {      if (null !== self::$loader) {          return self::$loader;      }        spl_autoload_register(array('ComposerAutoloaderInit440563a888dcb3a8c02b3ef8400e84e8', 'loadClassLoader'), true, true);      self::$loader = $loader = new ComposerAutoloadClassLoader();      spl_autoload_unregister(array('ComposerAutoloaderInit440563a888dcb3a8c02b3ef8400e84e8', '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(ComposerAutoloadComposerStaticInit440563a888dcb3a8c02b3ef8400e84e8::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);        return $loader;  }

這個方法是獲取 ComposerClassLoader,如果不存在就是生成一個實例放在 ComposerAutoloaderInit440563a888dcb3a8c02b3ef8400e84e8 中

將 Composer 生成的各種 autoload_psr4、autoload_classmap、autoload_namespaces 全都註冊到 ComposerClassLoader 中

然後 register 註冊文件

了解了 autoload.php 是如何工作的,以後那麼我們看一下composer dump-atoload -o有什麼用

autoload_classmap.php 在未執行命名之前 return 了一個空數組

在執行之後會發現所有的擴展包類的 namespace 和 classname 生成成一個 key => value 的數組

這時我們需要分析一下 ClassLoader 這個類的源碼

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;  }

我們可以看到會先去查找 autoload_classmap 中所有生成的註冊類,如果沒有才會載入 psr-4 和 psr-0

所以使用 dumpautoload 後會優先載入需要的類並提前返回,不然的話 compoesr 只能去動態讀取 psr-4 和 prs-0 的內容,這樣大大減少了 IO 操作和深層次的循環,提升部分性能問題

沈唁志,一個PHPer的成長之路! 任何個人或團體,未經允許禁止轉載本文:《為什麼Composer在生產環境要使用dumpautoload》,謝謝合作!

Tags: