這篇文章主要講解了“PHP命名空間與類自動加載的實現(xiàn)方法”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“PHP命名空間與類自動加載的實現(xiàn)方法”吧!
將樂網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,將樂網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為將樂上1000+提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的將樂做網(wǎng)站的公司定做!
在 PHP 5.3 之前,要在一個 PHP 腳本中引入另一個 PHP 腳本中定義的代碼(通常是函數(shù)或者類),需要借助 include、require、include_once、require_once 等語句,include 和 require 都可以通過指定路徑引入一個 PHP 腳本,區(qū)別是 include 沒有找到對應(yīng)路徑腳本時發(fā)出警告(E_WARNING),而 require 會拋出致命錯誤(E_COMPILE_ERROR),include_once/require_once 也是用于引入指定路徑 PHP 腳本,與 include/require 的區(qū)別是如果指定路徑已經(jīng)包含過,不會再次包含,換言之,只會包含一次同一路徑腳本,include_once 和 require_once 的區(qū)別與 include/require 一樣。
所以從性能角度說,使用 include_once/require_once 性能更好一些,至于使用 include_once 還是 require_once,取決于你對指定路徑 PHP 腳本不存在的預(yù)期處理。
在前面的作業(yè)中,我們已經(jīng)多次使用過它們來引入其他 PHP 腳本文件,比如在博客項目入口文件 index.php
中,我們通過如下代碼引入 bootstrap.php
以便引入初始化函數(shù) bootApp
進行調(diào)用:
require_once 'bootstrap.php';
// 新增一個 IoC 容器,通過依賴注入獲取對象實例
$container = Container::getInstance();
bootApp($container);
...
然后在 bootstrap.php
中,又通過如下代碼引入 Container
類定義:
require_once 'core/Container.php';
...
對于類文件的引入,如果你覺得反復(fù)編寫 require_once
/include_once
語句太麻煩,還可以借助 spl_auto_register 函數(shù)注冊自動加載器,實現(xiàn)系統(tǒng)未定義類或接口的自動加載。
比如我們將上述 bootstrap.php
中的通過 require_once
引入 Container
類代碼調(diào)整為通過 spl_autoload_register
函數(shù)自動注冊:
spl_autoload_register(function ($className) {
require_once 'core/' . $className. '.php';
});
這樣,我們只需要通過 spl_autoload_register
全局注冊這個匿名函數(shù)即可,當 Container
類找不到時,會根據(jù)這個自動加載器進行加載。
結(jié)合 require_once
/include_once
和 spl_autoload_register
,已經(jīng)可以很好地解決多個 PHP 腳本之間引入和組合的問題,從而構(gòu)建出復(fù)雜系統(tǒng),比如 Web 開發(fā)框架,或者第三方庫等,事實上,在 PHP 5.3 之前,第三方框架和庫就是這么做的,不過,細心的同學可能已經(jīng)看出來,spl_autoload_register
這種自動類加載機制存在一個問題,那就是不同庫/組件類名沖突問題,因此,從 PHP 5.3 開始,引入了命名空間的概念,通過命名空間,可以很好的解決這個問題,而且相較于前者,代碼可讀性更好。
在 PHP 中,通過 namespace
關(guān)鍵字聲明當前腳本所在的命名空間,通常,一個 PHP 腳本文件歸屬于一個命名空間。我們在 php_learning
目錄下新建一個 ns
子目錄存放本篇教程代碼,然后在 ns
目錄下創(chuàng)建一個 Test.php
文件,編寫一段簡單的測試代碼如下:
namespace App;
class Test
{
public static function print ()
{
printf("這是一個測試類: %s\n", __CLASS__);
}
}
我們需要在 PHP 腳本的第一行代碼聲明代碼所屬的命名空間(必須是第一行,否則會報錯):
namespace App;
表明這段腳本中的所有 PHP 常量、變量、類、函數(shù)都歸屬于這個命名空間,然后我們在這個命名空間中聲明了一個 Test
類,以及一個靜態(tài)方法 print
來打印類名。
接下來,我們在同一目錄下創(chuàng)建一個 App.php
腳本來調(diào)用 Test::print()
方法:
App.php
和 Test.php
歸屬于同一個目錄,所以聲明了相同的命名空間,實際開發(fā)過程中,我們通常就是根據(jù)目錄來組織并管理命名空間的。調(diào)用同一個命名空間中的類和函數(shù),可以像上面代碼這樣直接調(diào)用,如果是不同命名空間的類和函數(shù),則需要通過 use
關(guān)鍵字引入,我們在 ns
目錄下新建一個 testing
子目錄,并在該子目錄下新建一個 Test.php
,在這個 PHP 腳本中,我們定義了一個繼承自上級目錄中定義的 Test
父類的同名子類:
這里,我們將該子類所屬命名空間聲明為 App\Testing
(同一個命名空間下不允許出現(xiàn)重名的類和函數(shù)),然后通過 use
關(guān)鍵字引入上級命名空間中的 Test
類,由于該類名與子類名同名,所以通過 as
關(guān)鍵字為其設(shè)置一個別名 BaseTest
,接下來,就可以通過 BaseTest
引用 Test
父類。
在 Test
子類中,我們重寫了父類 BaseTest
的 print
方法。
最后,我們可以在 App.php
中這樣調(diào)用這個子類:
namespace App;
use App\Testing\Test as SubTest;
Test::print();
SubTest::print();
如果不存在類名沖突,則不需要設(shè)置別名:
namespace App;
use App\Testing\Test;
Test::print();
此外,還可以不使用 use
關(guān)鍵字,直接引用包含完整命名空間的類名:
namespace App;
Test::print();
\App\Testing\Test::print();
或者這樣,使用部分命名空間:
namespace App;
use App\Testing;
Test::print();
Testing\Test::print();
但是,我們這個系列教程約定通過 use
引入完整命名空間,以避免代碼的冗長,提高可讀性。
注:學院君這里只是拋磚引玉,簡單介紹了 PHP 命名空間的基本使用,更多細節(jié)請參考官方文檔 或者現(xiàn)代 PHP 新特性系列(一) —— 命名空間這篇教程(鏈接地址:https://xueyuanjun.com/post/4221)。
當然,現(xiàn)在調(diào)用 php App.php
會報錯,不論是 App\Test
還是 App\Testing\Test
類都提示找不到:
要解決這個問題,可以借助上面提到的 spl_autoload_register
函數(shù),將類名所屬命名空間解析為對應(yīng)的目錄路徑(這就是為什么要根據(jù)目錄來組織命名空間),然后把通過 require_once
/include_once
引入,我們在 App.php
中加入如下這段代碼:
namespace App;
use App\Testing\Test as SubTest;
spl_autoload_register(function ($className) {
$path = explode('\\', $className);
if ($path[0] == 'App') {
$base = __DIR__;
}
$filename = $path[count($path) - 1] . '.php';
$filepath = $base;
foreach ($path as $key => $val)
{
if ($key == 0 || $key == count($path) - 1) {
continue;
}
$filepath .= DIRECTORY_SEPARATOR . strtolower($val);
}
$filepath .= DIRECTORY_SEPARATOR . $filename;
require_once $filepath;
});
Test::print();
SubTest::print();
這樣,我們就可以正常調(diào)用這段代碼了:
實際項目開發(fā)時,手動編寫這段 spl_autoload_register
代碼有點麻煩,尤其是項目除了自己編寫的代碼外,還要引入各種第三方庫,我們可以借助 PHP 的包管理工具 Composer 幫我們管理這種命名空間與目錄路徑的映射,在此之前,我們已經(jīng)在 PHP 環(huán)境搭建篇中在本地系統(tǒng)中安裝好了 Composer,因此,只需要在 ns
目錄下運行 composer init
初始化 Composer 設(shè)置即可,按照向?qū)б宦吠伦呒纯?,最后會在項目根目錄下生成一個 composer.json
配置文件:
如果項目有第三方庫依賴,可以在 require
中進行配置,這里是一個測試項目,暫時還沒有任何依賴,然后我們在其中配置 autoload
選項來設(shè)置類自動加載機制:
{
"name": "php/test",
"description": "A php namespace test project",
"type": "project",
"license": "Apache",
"authors": [
{
"name": "xueyuanjun",
"email": "yaojinbu@outlook.com"
}
],
"minimum-stability": "dev",
"require": {},
"autoload": {
"classmap": [
"."
]
}
}
這里,我們通過在 classmap
數(shù)組中添加 .
表示當前根目錄作為類自動加載的入口目錄,Composer 會從這里開始讀取所有命名空間并建立目錄映射關(guān)系。接下來執(zhí)行 composer install
初始化依賴庫和類自動加載設(shè)置:
初始化過程中,會在根目錄下創(chuàng)建 vendor
用來存放第三方依賴包和類自動加載相關(guān)文件。初始化完成后,可以看到 vendor/composer/autoload_static.php
中已經(jīng)包含了 App
及其子命名空間的目錄映射了:
該文件會被 autoload_real.php
引用,autoload_real.php
又會被 vendor/autoload.php
引用:
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit991075cd5c3b2a6d389bb443802f7669::getLoader();
autoload_php
是所有 Composer 管理類自動加載的入口文件,所以我們只需要在代碼中引入這個文件即可通過 Composer 來管理所有類的自動加載,在 App.php
中,修改示例代碼如下:
include_once 'vendor/autoload.php';
use App\Test;
use App\Testing\Test as SubTest;
Test::print();
SubTest::print();
比起之前手動編寫 spl_autoload_register
進行類自動加載,現(xiàn)在的代碼更加簡單清晰,執(zhí)行 php App.php
,運行結(jié)果如下:
感謝各位的閱讀,以上就是“PHP命名空間與類自動加載的實現(xiàn)方法”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對PHP命名空間與類自動加載的實現(xiàn)方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!