亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

PHP?Composer自動(dòng)加載使用實(shí)戰(zhàn)

 更新時(shí)間:2023年09月08日 09:06:39   作者:菜皮日記  
這篇文章主要為大家介紹了PHP?Composer自動(dòng)加載使用實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一、沒有 composer 時(shí) PHP 是怎么做的

PHP 的 autoload 機(jī)制,可以在使用一個(gè)未導(dǎo)入的類時(shí)動(dòng)態(tài)加載該類,從而實(shí)現(xiàn)延遲加載和管理依賴類文件的目的。

__autoload 自動(dòng)加載器

PHP 中想要使用一個(gè)類,必須通過 require (指代 require_once, include_once 等) 的方式在文件開頭聲明要使用的類。當(dāng)項(xiàng)目中類較多時(shí),一個(gè)個(gè)聲明加載顯然不可行。

在 PHP5 版本,PHP 支持通過 __autoload 定義一個(gè)自動(dòng)加載器,嘗試加載未定義的類。 如:

// we've writen this code where we need
function __autoload($classname) {
    $filename = "./". $classname .".php";
    include_once($filename);
}
// we've called a class ***
$obj = new myClass();

但 __autoload 函數(shù)缺點(diǎn)比較明顯:他只能定義一次,這樣就會(huì)耦合所有依賴的類的自動(dòng)加載邏輯,統(tǒng)統(tǒng)寫到這個(gè)方法里,這時(shí)候就需要用到 spl_autoload_register 函數(shù)了。

使用 spl_autoload_register 注冊(cè)多個(gè)自動(dòng)加載器

spl 是 standard php library 的縮寫。spl_autoload_register 最大的特點(diǎn)是支持注冊(cè)多個(gè)自動(dòng)加載器,這樣就能實(shí)現(xiàn)將各個(gè)類庫的自動(dòng)加載邏輯分開,自己處理自己的加載邏輯。

function my_autoloader($class) {
    var_dump("my_autoloader", $class);
}
spl_autoload_register('my_autoloader');
// 靜態(tài)方法
class MyClass1 {
    public static function autoload($className) {
        var_dump("MyClass1 autoload", $className);
    }
}
spl_autoload_register(array('MyClass1', 'autoload'));
// 非靜態(tài)方法
class MyClass2 {
    public function autoload($className) {
        var_dump("MyClass2 autoload", $className);
    }
}
$instance = new MyClass2();
spl_autoload_register(array($instance, 'autoload'));
new \NotDefineClassName();
/*
輸出
string(32) "my_autoloader NotDefineClassName"
string(36) "MyClass1 autoload NotDefineClassName"
string(36) "MyClass2 autoload NotDefineClassName"
*/

二、PSR 規(guī)范

PSR 即 PHP Standards Recommendation 是一個(gè)社區(qū)組織:https://www.php-fig.org/psr/,聲明一系列規(guī)范來統(tǒng)一開發(fā)風(fēng)格,減少互不兼容的困擾。規(guī)范中的 PSR-4 代表:Autoloading Standard,即自動(dòng)加載規(guī)范。

PSR-4

其中規(guī)定:一個(gè)類的完整類名應(yīng)該遵循一下規(guī)范:

\<命名空間>(\<子命名空間>)*\<類名>

即:

  • 完整的類名必須要有一個(gè)頂級(jí)命名空間,被稱為 “vendor namespace”;
  • 完整的類名可以有一個(gè)或多個(gè)子命名空間;
  • 完整的類名必須有一個(gè)最終的類名;
  • 完整的類名中任意一部分中的下滑線都是沒有特殊含義的;
  • 完整的類名可以由任意大小寫字母組成;
  • 所有類名都必須是大小寫敏感的。

看看例子:

應(yīng)用的效果簡單來說就是:將命名空間前綴 Namespace Prefix 替換成 Base Directory 目錄,并將 \ 替換成 / 。一句話,命名空間可以表明類具體的存放位置。

三、Composer 自動(dòng)加載的過程

結(jié)合 spl_auto_register 和 PSR-4 的命名空間規(guī)范,可以想象,我們可以通過類的命名空間,來找到具體類的存放位置,然后通過 require 將其加載進(jìn)來生效,composer 就是這么干的。

接下來我們分兩步看 composer 是怎么做的。

第一步,建立類的命名空間和類存放位置的映射關(guān)系

首先看 vendor 目錄下的 autoload.php 文件,所有項(xiàng)目啟動(dòng)必然要先 require 這個(gè)文件。

// autoload.php @generated by Composer
// vendor/autoload.php
require_once __DIR__ . '/composer/autoload_real.php';
// 返回了autoload_real文件中的類方法
return ComposerAutoloaderInit7e421c277f7e8f810a19524f0d771cdb::getLoader();
/* ------------- */
// vendor/composer/autoload_real.php
public static function getLoader()
{
    if (null !== self::$loader) {
        return self::$loader;
    }
    // P0 初始化ClassLoader
    spl_autoload_register(array('ComposerAutoloaderInit7e421c277f7e8f810a19524f0d771cdb', 'loadClassLoader'), true, true);
    self::$loader = $loader = new \Composer\Autoload\ClassLoader();
    spl_autoload_unregister(array('ComposerAutoloaderInit7e421c277f7e8f810a19524f0d771cdb', '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';
        // P1 向ClassLoader中set命名空間和文件路徑映射關(guān)系
        call_user_func(\Composer\Autoload\ComposerStaticInit7e421c277f7e8f810a19524f0d771cdb::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);
        }
    }
    // P2 將ClassLoader中的loadClass方法,注冊(cè)為加載器
    $loader->register(true);
    if ($useStaticLoader) {
        $includeFiles = Composer\Autoload\ComposerStaticInit7e421c277f7e8f810a19524f0d771cdb::$files;
    } else {
        $includeFiles = require __DIR__ . '/autoload_files.php';
    }
    foreach ($includeFiles as $fileIdentifier => $file) {
        composerRequire7e421c277f7e8f810a19524f0d771cdb($fileIdentifier, $file);
    }
    return $loader;
}

在代碼 P0 處,上來先實(shí)例化一個(gè) \Composer\Autoload\ClassLoader 類,這個(gè)類里面維護(hù)了所有命名空間到類具體存放位置的映射關(guān)系。

接下來在 P1 處,根據(jù) PHP 版本和運(yùn)行環(huán)境,如是否運(yùn)行在 HHVM 環(huán)境下,來區(qū)分如何向 ClassLoader 中載入映射關(guān)系。

autoload_static.php 文件中定義的映射關(guān)系有三種:

public static $prefixLengthsPsr4 = array (
    'p' => 
    array (
        'phpDocumentor\\Reflection\\' => 25,
    ),
    'W' => 
    array (
        'Webmozart\\Assert\\' => 17,
    ),
    'S' => 
    array (
        'Symfony\\Polyfill\\Ctype\\' => 23,
    ),
    'R' => 
    array (
        'RefactoringGuru\\' => 16,
    ),
    'P' => 
    array (
        'Prophecy\\' => 9,
    ),
    'D' => 
    array (
        'Doctrine\\Instantiator\\' => 22,
        'DeepCopy\\' => 9,
    ),
);
public static $prefixDirsPsr4 = array (
    'phpDocumentor\\Reflection\\' => 
    array (
        0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
        1 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',
        2 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
    ),
    'Webmozart\\Assert\\' => 
    array (
        0 => __DIR__ . '/..' . '/webmozart/assert/src',
    ),
    'Symfony\\Polyfill\\Ctype\\' => 
    array (
        0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
    ),
    'RefactoringGuru\\' => 
    array (
        0 => __DIR__ . '/../..' . '/',
    ),
    'Prophecy\\' => 
    array (
        0 => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy',
    ),
    'Doctrine\\Instantiator\\' => 
    array (
        0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator',
    ),
    'DeepCopy\\' => 
    array (
        0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy',
    ),
);
public static $classMap = array (
        'File_Iterator' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Iterator.php',
        'File_Iterator_Facade' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Facade.php',
        'File_Iterator_Factory' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Factory.php',
        'PHPUnit\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php',
    ...
);

classMap 是完整映射關(guān)系,prefixLengthsPsr4 和 prefixDirsPsr4 是當(dāng)通過完整命名空間找不到時(shí),通過在目標(biāo)類名后加上 .php 再次尋找用。

到此,建立命名空間到類存放路徑的關(guān)系已經(jīng)完成了。

第二步,如何找到類并加載

在上面代碼中,將 ClassLoader 的 loadClass 方法注冊(cè)成加載器:

public function loadClass($class)
{
    if ($file = $this->findFile($class)) {
        includeFile($file);
        return true;
    }
}
function includeFile($file)
{
    include $file;
}

其中 findFile 方法,就是通過類名,去尋找文件實(shí)際的位置,如果找到了,就通過 includeFile 將文件加載進(jìn)來。主要看看 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;
}

對(duì)于類的加載十分簡單,直接去 classmap 中取。如果取不到,則將目標(biāo)類名追加 .php 后綴,去$prefixLengthsPsr4 和 $prefixDirsPsr4 中查找。

第三步,如何加載全局函數(shù)

if ($useStaticLoader) {
    $includeFiles = Composer\Autoload\ComposerStaticInit7e421c277f7e8f810a19524f0d771cdb::$files;
} else {
    $includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
    composerRequire7e421c277f7e8f810a19524f0d771cdb($fileIdentifier, $file);
}
return $loader;

還是通過 autoload_static.php 中定義的數(shù)據(jù)去加載:

// autoload_static.php
public static $files = array (
    '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
    '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
);
// vendor/symfony/polyfill-ctype/bootstrap.php
if (!function_exists('ctype_alnum')) {
    function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
}
if (!function_exists('ctype_alpha')) {
    function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
}

至此 composer 自動(dòng)加載的邏輯基本就過了一遍。

composer 的 ClassLoader 中的 classMap 是怎么生成出來的?

答案就在 composer 的源碼中:

https://github.com/composer/composer/blob/d0aac44ed210e13ec4a4370908a5b36553a2f16c/src/Composer/Autoload/AutoloadGenerator.php

掃描所有包中的類,然后生成一個(gè) php 文件,例如:getStaticFile 方法

參考:http://chabaoo.cn/program/297620u9q.htm

PHP類自動(dòng)加載的說明:http://chabaoo.cn/article/188078.htm

以上就是PHP Composer自動(dòng)加載使用實(shí)戰(zhàn)的詳細(xì)內(nèi)容,更多關(guān)于PHP Composer自動(dòng)加載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • PHP環(huán)境搭建(php+Apache+mysql)

    PHP環(huán)境搭建(php+Apache+mysql)

    這篇文章主要為大家詳細(xì)介紹了PHP環(huán)境搭建,包括php、Apache、mysql環(huán)境安裝,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Laravel利用gulp如何構(gòu)建前端資源詳解

    Laravel利用gulp如何構(gòu)建前端資源詳解

    Gulp 是一個(gè)自動(dòng)化工具,前端開發(fā)者可以使用它來處理常見任務(wù),下面這篇文章主要給大家介紹了關(guān)于Laravel利用gulp如何構(gòu)建前端資源的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧
    2018-06-06
  • CodeIgniter中使用Smarty3基本配置

    CodeIgniter中使用Smarty3基本配置

    這篇文章主要介紹了CodeIgniter中使用Smarty3基本配置,本文給出了創(chuàng)建類庫的方法和控制器中調(diào)用實(shí)例,這樣就可以完整的使用Smarty了,需要的朋友可以參考下
    2015-06-06
  • Yii框架where查詢用法實(shí)例分析

    Yii框架where查詢用法實(shí)例分析

    這篇文章主要介紹了Yii框架where查詢用法,結(jié)合實(shí)例形式分析了Yii框架查詢中where的相關(guān)使用技巧與操作注意事項(xiàng),需要的朋友可以參考下
    2019-10-10
  • PHP實(shí)現(xiàn)基于文本的摩斯電碼生成器

    PHP實(shí)現(xiàn)基于文本的摩斯電碼生成器

    這篇文章主要介紹了PHP實(shí)現(xiàn)基于文本的摩斯電碼生成器的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • smarty中改進(jìn)truncate使其支持中文的方法

    smarty中改進(jìn)truncate使其支持中文的方法

    這篇文章主要介紹了smarty中改進(jìn)truncate使其支持中文的方法,涉及針對(duì)Smarty源碼中truncate源文件進(jìn)行函數(shù)功能擴(kuò)展的相關(guān)技巧,需要的朋友可以參考下
    2016-05-05
  • PHP 繪制網(wǎng)站登錄首頁圖片驗(yàn)證碼

    PHP 繪制網(wǎng)站登錄首頁圖片驗(yàn)證碼

    幾乎所有的網(wǎng)站登錄頁都會(huì)有驗(yàn)證碼,驗(yàn)證碼是一種安全保護(hù)機(jī)制,用于防止垃圾注冊(cè)機(jī)大量注冊(cè)用戶賬號(hào)占用服務(wù)器內(nèi)存從而使服務(wù)器癱瘓。接下來通過本文給大家介紹PHP 繪制網(wǎng)站登錄首頁圖片驗(yàn)證碼,需要的朋友參考下
    2016-04-04
  • PHP正則提取不包含指定網(wǎng)址的圖片地址的例子

    PHP正則提取不包含指定網(wǎng)址的圖片地址的例子

    今天在做圖片提取的時(shí)候,遇到了這個(gè)情況,一篇文章中的圖片,提取出來一張作為文章的封面,這樣在排版的時(shí)候圖文模式看著比較舒服
    2014-04-04
  • thinkphp3.2.3框架動(dòng)態(tài)切換多數(shù)據(jù)庫的方法分析

    thinkphp3.2.3框架動(dòng)態(tài)切換多數(shù)據(jù)庫的方法分析

    這篇文章主要介紹了thinkphp3.2.3框架動(dòng)態(tài)切換多數(shù)據(jù)庫的方法,結(jié)合實(shí)例形式分析了thinkPHP3.2.3框架多數(shù)據(jù)庫切換的配置、使用相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2020-01-01
  • PHP房貸計(jì)算器實(shí)例代碼,等額本息,等額本金

    PHP房貸計(jì)算器實(shí)例代碼,等額本息,等額本金

    下面小編就為大家?guī)硪黄狿HP房貸計(jì)算器實(shí)例代碼,等額本息,等額本金。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04

最新評(píng)論