成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

Composer的Autoload源碼實(shí)現(xiàn)——啟動(dòng)與初始化

MarvinZhang / 2296人閱讀

摘要:任務(wù)是加載類(lèi)的初始化頂級(jí)命名空間與文件路徑映射初始化和注冊(cè)。在實(shí)際情況下可能會(huì)出現(xiàn)這樣的情況。值得注意的是這個(gè)函數(shù)返回的是一個(gè)匿名函數(shù),為什么呢原因就是類(lèi)中的等等都是的。。。關(guān)于匿名函數(shù)的綁定功能。

前言

在開(kāi)始之前,歡迎關(guān)注我自己的博客:www.leoyang90.cn

上一篇文章,我們討論了 PHP 的自動(dòng)加載原理、PHP 的命名空間、PHP 的 PSR0 與 PSR4 標(biāo)準(zhǔn),有了這些知識(shí),其實(shí)我們就可以按照 PSR4 標(biāo)準(zhǔn)寫(xiě)出可以自動(dòng)加載的程序了。然而我們?yōu)槭裁匆约簩?xiě)呢?尤其是有 Composer 這神一樣的包管理器的情況下?

Composer 自動(dòng)加載概論
簡(jiǎn)介

Composer 是 PHP 的一個(gè)依賴(lài)管理工具。它允許你申明項(xiàng)目所依賴(lài)的代碼庫(kù),它會(huì)在你的項(xiàng)目中為你安裝他們。詳細(xì)內(nèi)容可以查看 Composer 中文網(wǎng)。
Composer Composer 將這樣為你解決問(wèn)題:

你有一個(gè)項(xiàng)目依賴(lài)于若干個(gè)庫(kù)。

其中一些庫(kù)依賴(lài)于其他庫(kù)。

你聲明你所依賴(lài)的東西。

Composer 會(huì)找出哪個(gè)版本的包需要安裝,并安裝它們(將它們下載到你的項(xiàng)目中)。

例如,你正在創(chuàng)建一個(gè)項(xiàng)目,你需要一個(gè)庫(kù)來(lái)做日志記錄。你決定使用 monolog。為了將它添加到你的項(xiàng)目中,你所需要做的就是創(chuàng)建一個(gè) composer.json 文件,其中描述了項(xiàng)目的依賴(lài)關(guān)系。

{
  "require": {
    "monolog/monolog": "1.2.*"
  }
}

然后我們只要在項(xiàng)目里面直接use MonologLogger即可,神奇吧!
簡(jiǎn)單的說(shuō),Composer 幫助我們下載好了符合 PSR0 或 PSR4 標(biāo)準(zhǔn)的第三方庫(kù),并把文件放在相應(yīng)位置;幫我們寫(xiě)了 _autoload() 函數(shù),注冊(cè)到了 spl_register() 函數(shù),當(dāng)我們想用第三方庫(kù)的時(shí)候直接使用命名空間即可。
??
那么當(dāng)我們想要寫(xiě)自己的命名空間的時(shí)候,該怎么辦呢?很簡(jiǎn)單,我們只要按照 PSR4 標(biāo)準(zhǔn)命名我們的命名空間,放置我們的文件,然后在 composer 里面寫(xiě)好頂級(jí)域名與具體目錄的映射,就可以享用 composer 的便利了。
當(dāng)然如果有一個(gè)非常棒的框架,我們會(huì)驚喜地發(fā)現(xiàn),在 composer 里面寫(xiě)頂級(jí)域名映射這事我們也不用做了,框架已經(jīng)幫我們寫(xiě)好了頂級(jí)域名映射了,我們只需要在框架里面新建文件,在新建的文件中寫(xiě)好命名空間,就可以在任何地方 use 我們的命名空間了。
下面我們就以 laravel 框架為例,講一講 composer 是如何實(shí)現(xiàn) PSR0 和 PSR4 標(biāo)準(zhǔn)的自動(dòng)加載功能。

Composer 自動(dòng)加載文件

首先,我們先大致了解一下 Composer 自動(dòng)加載所用到的源文件。

autoload_real.php:自動(dòng)加載功能的引導(dǎo)類(lèi)。任務(wù)是 composer 加載類(lèi)的初始化(頂級(jí)命名空間與文件路徑映射初始化)和注冊(cè)( spl_autoload_register() )。

ClassLoader.php:composer 加載類(lèi)。composer 自動(dòng)加載功能的核心類(lèi)。

autoload_static.php:頂級(jí)命名空間初始化類(lèi),用于給核心類(lèi)初始化頂級(jí)命名空間。

autoload_classmap.php:自動(dòng)加載的最簡(jiǎn)單形式,有完整的命名空間和文件目錄的映射;

autoload_files.php:用于加載全局函數(shù)的文件,存放各個(gè)全局函數(shù)所在的文件路徑名;

autoload_namespaces.php:符合 PSR0 標(biāo)準(zhǔn)的自動(dòng)加載文件,存放著頂級(jí)命名空間與文件的映射;

autoload_psr4.php:符合 PSR4 標(biāo)準(zhǔn)的自動(dòng)加載文件,存放著頂級(jí)命名空間與文件的映射;

啟動(dòng)

laravel 框架的初始化是需要 composer 自動(dòng)加載協(xié)助的,所以 laravel 的入口文件 index.php 第一句就是利用 composer 來(lái)實(shí)現(xiàn)自動(dòng)加載功能。

  require __DIR__."/../bootstrap/autoload.php";

咱們接著去看 bootstrap 目錄下的 autoload.php:

  define("LARAVEL_START", microtime(true));

  require __DIR__."/../vendor/autoload.php";

再去vendor目錄下的autoload.php:

  require_once __DIR__ . "/composer" . "/autoload_real.php";

  return ComposerAutoloaderInit
  832ea71bfb9a4128da8660baedaac82e::getLoader();

為什么框架要在 bootstrap/autoload.php 轉(zhuǎn)一下?個(gè)人理解,laravel 這樣設(shè)計(jì)有利于支持或擴(kuò)展任意有自動(dòng)加載的第三方庫(kù)。
好了,我們終于要看到了 Composer 真正要顯威的地方了。autoload_real 里面就是一個(gè)自動(dòng)加載功能的引導(dǎo)類(lèi),這個(gè)類(lèi)不負(fù)責(zé)具體功能邏輯,只做了兩件事:初始化自動(dòng)加載類(lèi)、注冊(cè)自動(dòng)加載類(lèi)。
到 autoload_real 這個(gè)文件里面去看,發(fā)現(xiàn)這個(gè)引導(dǎo)類(lèi)的名字叫 ComposerAutoloaderInit832ea71bfb9a4128da8660baedaac82e,為什么要叫這么古怪的名字呢?因?yàn)檫@是防止用戶(hù)自定義類(lèi)名跟這個(gè)類(lèi)重復(fù)沖突了,所以在類(lèi)名上加了一個(gè) hash 值。
其實(shí)還有一個(gè)原因,那就是composer運(yùn)行加載多個(gè)ComposerAutoloaderInit類(lèi)。在實(shí)際情況下可能會(huì)出現(xiàn)這樣的情況:vendor/modelA/vendor/composer。也就是說(shuō)第三方庫(kù)中也存在著一個(gè)composer,他有著自己所依賴(lài)的各種庫(kù),也是通過(guò)composer來(lái)加載。這樣的話就會(huì)有兩個(gè)ComposerAutoloaderInit類(lèi),那么就會(huì)觸發(fā)redeclare的錯(cuò)誤。給ComposerAutoloaderInit加上一個(gè)hash,那么就可以實(shí)現(xiàn)多個(gè)class loader 的加載。

autoload_real 引導(dǎo)類(lèi)

在 vendor 目錄下的 autoload.php 文件中我們可以看出,程序主要調(diào)用了引導(dǎo)類(lèi)的靜態(tài)方法 getLoader(),我們接著看看這個(gè)函數(shù)。

public static function getLoader()
{
  /***************************經(jīng)典單例模式********************/
  if (null !== self::$loader) {
      return self::$loader;
  }

  /***********************獲得自動(dòng)加載核心類(lèi)對(duì)象********************/
  spl_autoload_register(array("ComposerAutoloaderInit
  832ea71bfb9a4128da8660baedaac82e", "loadClassLoader"), true, true);

  self::$loader = $loader = new ComposerAutoloadClassLoader();

  spl_autoload_unregister(array("ComposerAutoloaderInit
  832ea71bfb9a4128da8660baedaac82e", "loadClassLoader"));

  /***********************初始化自動(dòng)加載核心類(lèi)對(duì)象********************/
  $useStaticLoader = PHP_VERSION_ID >= 50600 &&
  !defined("HHVM_VERSION");

  if ($useStaticLoader) {
      require_once __DIR__ . "/autoload_static.php";

      call_user_func(ComposerAutoloadComposerStaticInit
      832ea71bfb9a4128da8660baedaac82e::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);
      }
  }

  /***********************注冊(cè)自動(dòng)加載核心類(lèi)對(duì)象********************/
  $loader->register(true);

  /***********************自動(dòng)加載全局函數(shù)********************/
  if ($useStaticLoader) {
      $includeFiles = ComposerAutoloadComposerStaticInit
                      832ea71bfb9a4128da8660baedaac82e::$files;
  } else {
      $includeFiles = require __DIR__ . "/autoload_files.php";
  }

  foreach ($includeFiles as $fileIdentifier => $file) {
      composerRequire
      832ea71bfb9a4128da8660baedaac82e($fileIdentifier, $file);
  }

  return $loader;
}

從上面可以看出,我把自動(dòng)加載引導(dǎo)類(lèi)分為5個(gè)部分。

第一部分——單例

第一部分很簡(jiǎn)單,就是個(gè)最經(jīng)典的單例模式,自動(dòng)加載類(lèi)只能有一個(gè),多次加載影響效率,可能會(huì)引起重復(fù)require同一個(gè)文件。

if (null !== self::$loader) {
  return self::$loader;
}
第二部分——構(gòu)造 ClassLoader 核心類(lèi)

第二部分 new 一個(gè)自動(dòng)加載的核心類(lèi)對(duì)象。

  /***********************獲得自動(dòng)加載核心類(lèi)對(duì)象********************/
  spl_autoload_register(array("ComposerAutoloaderInit
  832ea71bfb9a4128da8660baedaac82e", "loadClassLoader"), true, true);

  self::$loader = $loader = new ComposerAutoloadClassLoader();

  spl_autoload_unregister(array("ComposerAutoloaderInit
               832ea71bfb9a4128da8660baedaac82e", "loadClassLoader"));

loadClassLoader() 函數(shù):

public static function loadClassLoader($class)
{
if ("ComposerAutoloadClassLoader" === $class) {
    require __DIR__ . "/ClassLoader.php";
}
}

從程序里面我們可以看出,composer 先向 PHP 自動(dòng)加載機(jī)制注冊(cè)了一個(gè)函數(shù),這個(gè)函數(shù) require 了 ClassLoader 文件。成功 new 出該文件中核心類(lèi) ClassLoader() 后,又銷(xiāo)毀了該函數(shù)。
為什么不直接 require,而要這么麻煩?原因和ComposerAutoloaderInit加上hash一樣,如果直接require,那么會(huì)造成ClassLoader類(lèi)的重復(fù)定義。所以有人建議這樣:

if (!class_exists("ComposerAutoloadClassLoader", false)) {
  require __DIR__ . "/ClassLoader.php";
}
static::$loader = $loader = new ComposerAutoloadClassLoader();

其實(shí)這樣可以更加直觀。但是class_exists有個(gè)缺點(diǎn),那就是opcache緩存有個(gè)bug,class_exists即使為真,程序仍然會(huì)進(jìn)入if條件進(jìn)行require,這樣仍然造成了重復(fù)定義的問(wèn)題。
那為什么不跟引導(dǎo)類(lèi)一樣用個(gè) hash 呢?這樣就可以多次定義這個(gè)ClassLoader類(lèi)了。原因就是這個(gè)類(lèi)是可以復(fù)用的,框架允許用戶(hù)使用這個(gè)類(lèi),如果用hash用戶(hù)就完全沒(méi)辦法用ClassLoader了。
所以最終的解決方案就是利用spl_autoload_register來(lái)加載,這樣只要ClassLoader只要被聲明過(guò),spl_autoload_register就不會(huì)調(diào)用,也就不會(huì)require。
可見(jiàn)這簡(jiǎn)單的幾行代碼其實(shí)內(nèi)幕很深的。詳細(xì)可見(jiàn)
github 的相關(guān) issue:Unable to run tests with phpunit and composer installed globally #1248
github 相關(guān)解決方案 PR : Allow loading of multiple composer autoloaders concurrently, fixes #1248 #1313

第三部分——初始化核心類(lèi)對(duì)象
  /***********************初始化自動(dòng)加載核心類(lèi)對(duì)象********************/
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined("HHVM_VERSION");
  if ($useStaticLoader) {
      require_once __DIR__ . "/autoload_static.php";

      call_user_func(ComposerAutoloadComposerStaticInit
      832ea71bfb9a4128da8660baedaac82e::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);
  }
}

這一部分就是對(duì)自動(dòng)加載類(lèi)的初始化,主要是給自動(dòng)加載核心類(lèi)初始化頂級(jí)命名空間映射。初始化的方法有兩種:(1)使用 autoload_static 進(jìn)行靜態(tài)初始化;(2)調(diào)用核心類(lèi)接口初始化。

autoload_static 靜態(tài)初始化

靜態(tài)初始化只支持 PHP5.6 以上版本并且不支持 HHVM 虛擬機(jī)。為什么要多帶帶要求 php5.6 版本以上呢?原因就是這種靜態(tài)加載加速機(jī)制是 opcache 緩存針對(duì)靜態(tài)數(shù)組優(yōu)化的,只支持 php5.6 以上的版本。hhvm 是 php 另一個(gè)虛擬機(jī),當(dāng)然沒(méi)有辦法支持 opcache 緩存。
github相關(guān) PR: Speedup autoloading on PHP 5.6 & 7.0+ using static arrays
我們深入 autoload_static.php 這個(gè)文件發(fā)現(xiàn)這個(gè)文件定義了一個(gè)用于靜態(tài)初始化的類(lèi),名字叫 ComposerStaticInit832ea71bfb9a4128da8660baedaac82e,仍然為了避免沖突加了 hash 值和多次復(fù)用。這個(gè)類(lèi)很簡(jiǎn)單:

class ComposerStaticInit832ea71bfb9a4128da8660baedaac82e{
    public static $files = array(...);
    public static $prefixLengthsPsr4 = array(...);
    public static $prefixDirsPsr4 = array(...);
    public static $prefixesPsr0 = array(...);
    public static $classMap = array (...);

    public static function getInitializer(ClassLoader $loader)
    {
        return Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 =   ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$prefixDirsPsr4;
            $loader->prefixesPsr0 = ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$prefixesPsr0;
            $loader->classMap = ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$classMap;

        }, null, ClassLoader::class);
    }
}

這個(gè)靜態(tài)初始化類(lèi)的核心就是 getInitializer() 函數(shù),它將自己類(lèi)中的頂級(jí)命名空間映射給了 ClassLoader 類(lèi)。值得注意的是這個(gè)函數(shù)返回的是一個(gè)匿名函數(shù),為什么呢?原因就是 ClassLoader 類(lèi)中的 prefixLengthsPsr4、prefixDirsPsr4 等等都是 private的。。。普通的函數(shù)沒(méi)辦法給類(lèi)的 private 成員變量賦值。利用匿名函數(shù)的綁定功能就可以將把匿名函數(shù)轉(zhuǎn)為 ClassLoader 類(lèi)的成員函數(shù)。
關(guān)于匿名函數(shù)的 綁定功能。
接下來(lái)就是頂級(jí)命名空間初始化的關(guān)鍵了。

最簡(jiǎn)單的classMap:
public static $classMap = array (
    "AppConsoleKernel" => __DIR__ . "/../.." . "/app/Console/Kernel.php",
    "AppExceptionsHandler" => __DIR__ . "/../.." . "/app/Exceptions/Handler.php",
    "AppHttpControllersAuthForgotPasswordController" => __DIR__ . "/../.." . "/app/Http/Controllers/Auth/ForgotPasswordController.php",
    "AppHttpControllersAuthLoginController" => __DIR__ . "/../.." . "/app/Http/Controllers/Auth/LoginController.php",
    "AppHttpControllersAuthRegisterController" => __DIR__ . "/../.." . "/app/Http/Controllers/Auth/RegisterController.php",
...
)

簡(jiǎn)單吧,直接命名空間全名與目錄的映射,沒(méi)有頂級(jí)命名空間。。。簡(jiǎn)單粗暴,也導(dǎo)致這個(gè)數(shù)組相當(dāng)?shù)拇蟆?/p> PSR0頂級(jí)命名空間映射:

public static $prefixesPsr0 = array (
  "P" =>
  array (
    "Prophecy" =>
    array (
      0 => __DIR__ . "/.." . "/phpspec/prophecy/src",
    ),
    "Parsedown" =>
    array (
      0 => __DIR__ . "/.." . "/erusev/parsedown",
    ),
  ),
  "M" =>
  array (
    "Mockery" =>
    array (
      0 => __DIR__ . "/.." . "/mockery/mockery/library",
    ),
  ),
  "J" =>
  array (
    "JakubOnderkaPhpConsoleHighlighter" =>
    array (
      0 => __DIR__ . "/.." . "/jakub-onderka/php-console-highlighter/src",
    ),
    "JakubOnderkaPhpConsoleColor" =>
    array (
      0 => __DIR__ . "/.." . "/jakub-onderka/php-console-color/src",
    ),
  ),
  "D" =>
  array (
    "DoctrineCommonInflector" =>
    array (
      0 => __DIR__ . "/.." . "/doctrine/inflector/lib",
    ),
  ),
);

為了快速找到頂級(jí)命名空間,我們這里使用命名空間第一個(gè)字母作為前綴索引。這個(gè)映射的用法比較明顯,假如我們有 Parsedown/example 這樣的命名空間,首先通過(guò)首字母 P,找到

  "P" =>
  array (
    "Prophecy" =>
    array (
      0 => __DIR__ . "/.." . "/phpspec/prophecy/src",
    ),
    "Parsedown" =>
    array (
      0 => __DIR__ . "/.." . "/erusev/parsedown",
    ),
  )

這個(gè)數(shù)組,然后我們就會(huì)遍歷這個(gè)數(shù)組來(lái)和 Parsedown/example 比較,發(fā)現(xiàn)第一個(gè) Prophecy 不符合,第二個(gè) Parsedown 符合,然后得到了映射目錄:(映射目錄可能不止一個(gè))

array (
  0 => __DIR__ . "/.." . "/erusev/parsedown",
)

我們會(huì)接著遍歷這個(gè)數(shù)組,嘗試 _DIR_ ."/.." . "/erusev/parsedown/Parsedown/example.php 是否存在,如果不存在接著遍歷數(shù)組(這個(gè)例子數(shù)組只有一個(gè)元素),如果數(shù)組遍歷完都沒(méi)有,就會(huì)加載失敗。

PSR4標(biāo)準(zhǔn)頂級(jí)命名空間映射數(shù)組:
public static $prefixLengthsPsr4 = array(
  "p" =>
  array (
    "phpDocumentorReflection" => 25,
  ),
  "S" =>
  array (
    "SymfonyPolyfillMbstring" => 26,
    "SymfonyComponentYaml" => 23,
    "SymfonyComponentVarDumper" => 28,
    ...
  ),
  ...
);

public static $prefixDirsPsr4 = array (
  "phpDocumentorReflection" =>
  array (
    0 => __DIR__ . "/.." . "/phpdocumentor/reflection-common/src",
    1 => __DIR__ . "/.." . "/phpdocumentor/type-resolver/src",
    2 => __DIR__ . "/.." . "/phpdocumentor/reflection-docblock/src",
  ),
  "SymfonyPolyfillMbstring" =>
  array (
    0 => __DIR__ . "/.." . "/symfony/polyfill-mbstring",
  ),
  "SymfonyComponentYaml" =>
  array (
    0 => __DIR__ . "/.." . "/symfony/yaml",
  ),
  ...
)

PSR4 標(biāo)準(zhǔn)頂級(jí)命名空間映射用了兩個(gè)數(shù)組,第一個(gè)和 PSR0 一樣用命名空間第一個(gè)字母作為前綴索引,然后是頂級(jí)命名空間,但是最終并不是文件路徑,而是頂級(jí)命名空間的長(zhǎng)度。為什么呢?因?yàn)榍耙黄?文章 我們說(shuō)過(guò),PSR4 標(biāo)準(zhǔn)的文件目錄更加靈活,更加簡(jiǎn)潔。PSR0 中頂級(jí)命名空間目錄直接加到命名空間前面就可以得到路徑 (Parsedown/example => _DIR_ ."/.." . "/erusev/parsedown/Parsedown/example.php),而 PSR4 標(biāo)準(zhǔn)卻是用頂級(jí)命名空間目錄替換頂級(jí)命名空間(Parsedown/example => _DIR_ ."/.." . "/erusev/parsedown/example.php),所以獲得頂級(jí)命名空間的長(zhǎng)度很重要。
具體的用法:假如我們找 SymfonyPolyfillMbstringexample 這個(gè)命名空間,和 PSR0 一樣通過(guò)前綴索引和字符串匹配我們得到了

  "SymfonyPolyfillMbstring" => 26,

這條記錄,鍵是頂級(jí)命名空間,值是命名空間的長(zhǎng)度。拿到頂級(jí)命名空間后去 $prefixDirsPsr4 數(shù)組獲取它的映射目錄數(shù)組:(注意映射目錄可能不止一條)

"SymfonyPolyfillMbstring" =>
array (
  0 => __DIR__ . "/.." . "/symfony/polyfill-mbstring",
)

然后我們就可以將命名空間 SymfonyPolyfillMbstringexample 前26個(gè)字符替換成目錄 _DIR_ . "/.." . "/symfony/polyfill-mbstring,我們就得到了 _DIR_ . "/.." . "/symfony/polyfill-mbstring/example.php,先驗(yàn)證磁盤(pán)上這個(gè)文件是否存在,如果不存在接著遍歷。如果遍歷后沒(méi)有找到,則加載失敗。
??
自動(dòng)加載核心類(lèi) ClassLoader 的靜態(tài)初始化完成?。。?/p> ClassLoader 接口初始化


如果PHP版本低于5.6或者使用HHVM虛擬機(jī)環(huán)境,那么就要使用核心類(lèi)的接口進(jìn)行初始化。

  //PSR0標(biāo)準(zhǔn)
  $map = require __DIR__ . "/autoload_namespaces.php";
  foreach ($map as $namespace => $path) {
      $loader->set($namespace, $path);
  }

  //PSR4標(biāo)準(zhǔn)
  $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);
  }
PSR0 標(biāo)準(zhǔn)

autoload_namespaces:

return array(
  "Prophecy" => array($vendorDir . "/phpspec/prophecy/src"),
  "Parsedown" => array($vendorDir . "/erusev/parsedown"),
  "Mockery" => array($vendorDir . "/mockery/mockery/library"),
  "JakubOnderkaPhpConsoleHighlighter" => array($vendorDir . "/jakub-onderka/php-console-highlighter/src"),
  "JakubOnderkaPhpConsoleColor" => array($vendorDir . "/jakub-onderka/php-console-color/src"),
  "DoctrineCommonInflector" => array($vendorDir . "/doctrine/inflector/lib"),
);

PSR0 標(biāo)準(zhǔn)的初始化接口:

public function set($prefix, $paths)
{
  if (!$prefix) {
    $this->fallbackDirsPsr0 = (array) $paths;
  } else {
    $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
  }
}

很簡(jiǎn)單,PSR0 標(biāo)準(zhǔn)取出命名空間的第一個(gè)字母作為索引,一個(gè)索引對(duì)應(yīng)多個(gè)頂級(jí)命名空間,一個(gè)頂級(jí)命名空間對(duì)應(yīng)多個(gè)目錄路徑,具體形式可以查看上面我們講的 autoload_static 的 $prefixesPsr0。如果沒(méi)有頂級(jí)命名空間,就只存儲(chǔ)一個(gè)路徑名,以便在后面嘗試加載。

PSR4標(biāo)準(zhǔn)

autoload_psr4

return array(
  "XdgBaseDir" => array($vendorDir . "/dnoegel/php-xdg-base-dir/src"),
  "WebmozartAssert" => array($vendorDir . "/webmozart/assert/src"),
  "TijsVerkoyenCssToInlineStyles" => array($vendorDir . "/tijsverkoyen/css-to-inline-styles/src"),
  "Tests" => array($baseDir . "/tests"),
  "SymfonyPolyfillMbstring" => array($vendorDir . "/symfony/polyfill-mbstring"),
  ...
)

PSR4 標(biāo)準(zhǔn)的初始化接口:

public function setPsr4($prefix, $paths)
{
  if (!$prefix) {
    $this->fallbackDirsPsr4 = (array) $paths;
  } else {
    $length = strlen($prefix);
    if ("" !== $prefix[$length - 1]) {
      throw new InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
    }
    
    $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
    $this->prefixDirsPsr4[$prefix] = (array) $paths;
  }
}

PSR4 初始化接口也很簡(jiǎn)單。如果沒(méi)有頂級(jí)命名空間,就直接保存目錄。如果有命名空間的話,要保證頂級(jí)命名空間最后是,然后分別保存(前綴=》頂級(jí)命名空間,頂級(jí)命名空間=》頂級(jí)命名空間長(zhǎng)度),(頂級(jí)命名空間=》目錄)這兩個(gè)映射數(shù)組。具體形式可以查看上面我們講的 autoload_static的prefixLengthsPsr4、 $prefixDirsPsr4。

傻瓜式命名空間映射

autoload_classmap:

public static $classMap = array (
  "AppConsoleKernel" => __DIR__ . "/../.." . "/app/Console/Kernel.php",
  "AppExceptionsHandler" => __DIR__ . "/../.." . "/app/Exceptions/Handler.php",
  ...
)

addClassMap:

public function addClassMap(array $classMap)
{
  if ($this->classMap) {
    $this->classMap = array_merge($this->classMap, $classMap);
  } else {
    $this->classMap = $classMap;
  }
}

這個(gè)最簡(jiǎn)單,就是整個(gè)命名空間與目錄之間的映射。

結(jié)語(yǔ)

其實(shí)我很想接著寫(xiě)下下去,但是這樣會(huì)造成篇幅過(guò)長(zhǎng),所以我就把自動(dòng)加載的注冊(cè)和運(yùn)行放到下一篇文章了。我們回顧一下,這篇文章主要講了:(1)框架如何啟動(dòng) composer 自動(dòng)加載;(2)composer 自動(dòng)加載分為5部分;
其實(shí)說(shuō)是5部分,真正重要的就兩部分——初始化與注冊(cè)。初始化負(fù)責(zé)頂層命名空間的目錄映射,注冊(cè)負(fù)責(zé)實(shí)現(xiàn)頂層以下的命名空間映射規(guī)則。

Written with StackEdit.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/22928.html

相關(guān)文章

  • ComposerAutoload源碼實(shí)現(xiàn)——注冊(cè)運(yùn)行

    前言 在開(kāi)始之前,歡迎關(guān)注我自己的博客:www.leoyang90.cn上一篇 文章我們講到了 Composer 自動(dòng)加載功能的啟動(dòng)與初始化,經(jīng)過(guò)啟動(dòng)與初始化,自動(dòng)加載核心類(lèi)對(duì)象已經(jīng)獲得了頂級(jí)命名空間與相應(yīng)目錄的映射,換句話說(shuō),如果有命名空間 AppConsoleKernel,我們已經(jīng)知道了 App 對(duì)應(yīng)的目錄,接下來(lái)我們就要解決下面的就是 ConsoleKernel這一段。 注冊(cè) 我們先回顧...

    wanghui 評(píng)論0 收藏0
  • Symfony2.8 源碼分析之類(lèi)加載

    摘要:今天來(lái)寫(xiě)寫(xiě)這個(gè)框架的類(lèi)加載機(jī)制版本原理在項(xiàng)目啟動(dòng)時(shí),通過(guò)注冊(cè)了要使用的類(lèi)的自動(dòng)加載處理方法,在類(lèi)第一次被使用的時(shí)候,類(lèi)文件通過(guò)該方法被引入,然后類(lèi)才得以使用源碼分析在的入口文件,我們找到我們隨著這個(gè)路徑我們找打了這個(gè)主要內(nèi)容如下其中是為了注 今天來(lái)寫(xiě)寫(xiě)Symfony2.8 這個(gè)框架的類(lèi)加載機(jī)制 版本 Symfony 2.8 原理 在項(xiàng)目啟動(dòng)時(shí),Symfony 通過(guò)spl_autoloa...

    Blackjun 評(píng)論0 收藏0
  • 解讀 thinkphp5 源碼(一):自動(dòng)加載

    摘要:索性讀一下它的源碼。行載入類(lèi)載入類(lèi),這個(gè)類(lèi)比較重要,實(shí)現(xiàn)了自動(dòng)加載。注冊(cè)錯(cuò)誤和異常處理機(jī)制加載慣例配置文件接下來(lái)我們看一下自動(dòng)加載的實(shí)現(xiàn)方法。所以借助此函數(shù)可以達(dá)到自動(dòng)加載。博客鏈接解讀源碼一自動(dòng)加載 聽(tīng)說(shuō) TP5 已經(jīng) RC4 了,曾經(jīng)在 RC3 的時(shí)候用它寫(xiě)過(guò)一個(gè)小東西。官方說(shuō)從 RC4 以后改動(dòng)不是太大。索性讀一下它的源碼。然后順便記錄一下,如有錯(cuò)漏,請(qǐng)路過(guò)大神多多指正! 入口 ...

    stormzhang 評(píng)論0 收藏0
  • thinkphp源碼分析(三)—自動(dòng)加載篇(Loader分析)

    摘要:源碼分析自動(dòng)加載系統(tǒng)會(huì)調(diào)用方法注冊(cè)自動(dòng)加載,在這一步完成后,所有符合規(guī)范的類(lèi)庫(kù)包括依賴(lài)加載的第三方類(lèi)庫(kù)都將自動(dòng)加載。是通過(guò)加載對(duì)應(yīng)的文件進(jìn)行注冊(cè)加載的。 源碼分析 自動(dòng)加載 系統(tǒng)會(huì)調(diào)用 Loader::register()方法注冊(cè)自動(dòng)加載,在這一步完成后,所有符合規(guī)范的類(lèi)庫(kù)(包括Composer依賴(lài)加載的第三方類(lèi)庫(kù))都將自動(dòng)加載。 系統(tǒng)的自動(dòng)加載由下面主要部分組成: 1. 注冊(cè)系統(tǒng)的自...

    Pandaaa 評(píng)論0 收藏0
  • ThinkPHP5.1 源碼淺析(二)自動(dòng)加載機(jī)制

    摘要:如果遍歷后沒(méi)有找到,則加載失敗。在之后碰到了之后直接拿來(lái)用,提高系統(tǒng)自動(dòng)加載的性能。這里我們就講完了注冊(cè)自動(dòng)加載。使用自動(dòng)加載我們?cè)谥卸x了我們自動(dòng)加載函數(shù)式方法。 繼 生命周期的第二篇,大家盡可放心,不會(huì)隨便鴿文章的 第一篇中,我們提到了入口腳本,也說(shuō)了,里面注冊(cè)了自動(dòng)加載的功能 本文默認(rèn)你有自動(dòng)加載和命名空間的基礎(chǔ)。如果沒(méi)有請(qǐng) 看此篇文章 php 類(lèi)的自動(dòng)加載與命名空間 自動(dòng)加載...

    mudiyouyou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<