以Yii 2.0風格加載自定義類或命名空間 [配置使用Yii autoloader] [ 2.0 版本 ]

Yii 2.0最顯著的特徵之一就是引入了命名空間,因此對於自定義類的引入方式也同之前有所不同。這篇文章討論一下如何利用Yii 2.0的自動加載機制,向系統中引入自定義類和命名空間。本文旨在拋磚引玉,如果有理解不當敬請指正,歡迎大家把自己的方法拿出來分享。
我們希望被引入的類應該達成一下兩點:

  • 在應用中的任意位置可以使用該類名或命名空間,而不用顯式調用require()/include()。
  • 利用Yii的autoloader,僅在類被調用時加載,以遵循Yii按需加載的原則,節省資源。

我們使用Yii 2.0基礎模板作爲演示環境,項目根目錄命名爲basic(後文中會寫成/),這是根目錄結構:

basic
├── assets
├── commands
├── config
├── controllers
├── mail
├── models
├── runtime
├── tests
├── vendor
├── views
└── web

加載自定義類

I. 定義類文件

建立目錄 /libs 並建立文件Freedom.php。

<?php

class Freedom
{
    public static function yell()
    {
        echo "I am FREE!";
    }
}

II. 向Yii::$classMap添加映射

打開配置文件/config/web.php,在文件頭部向Yii::$classMap屬性添加類映射。

<?php
...

Yii::$classMap['Freedom'] = '@app/libs/Freedom.php';
...
$config = [
...
];
return $config;

注意: 不要對Yii::$classMap使用=直接賦值,因爲該屬性中定義了Yii的一些核心類映射,直接賦值會導致這些映射丟失而Yii autoloader加載不到核心類。No zuo no die don't try.

III. 使用自定義類

見證奇蹟的時刻。在系統中嘗試調用這個類,我們使用SiteController::actionIndex()爲例。

<?php
...
use Freedom; // 別忘了導入這個類,或者在後面調用的時候使用"\Freedom"。

class SiteController extends Controller
    ...
    public function actionIndex()
    {
        Freedom::yell();
    }

}

好了,刷新一下試試。

IV. 如果你還關心爲什麼

那現在我們來需要介紹一下Yii::$classMap究竟是個毛。這貨實際上是一個關聯數組,數組鍵爲“去掉前導反斜線的完全權限定類名”,對應值爲定義了該類的文件路徑,其中文件路徑支持路徑別名。在代碼中調用到尚未加載的類時,Yii的audoloader會掃描這個屬性以獲得需要加載的類文件名。

所以,我們把剛剛定義的類加入到這個映射數組中,它就可以被Yii延遲加載了。事實上我們可以在任何位置添加這個映射,只要在目標類被調用之前就可以。應用的主配置文件是一個比較理想的位置,因爲配置文件加載在Yii.php之後,可以在其中訪問到Yii類(有興趣的同學可以去看一下入口腳本),而且配置數據可以集中在一個文件裏。

另外,由於我們定義的類在根命名空間下,所以“去掉前導反斜線的完全權限定類名”就只剩下Freedom了。如果你的類使用了命名空間,只需要在數組鍵裏寫上完全限定名稱就行了(e.g. ['custom\classes\Freedom'])。


加載整個命名空間

有時候我們需要寫一組相互關聯的類,如果這些類存在依賴關係的話像上面這樣給每個類配置映射會……非(jue)常(b)不(gao)體(si)面(ren)。如果你定義命名空間下的類時遵循 PSR-4 標準,我們可以一次引入整個命名空間。
這次我們要使用的屬性是\yii\base\Application::$aliases。它也是一個關聯數組,將一個路徑別名映射到一個目錄或者另一個已經存在的路徑別名。其中數組鍵是要指定的別名,對應值是目標路徑。
我們只需要在建立一個命名空間別名,把它映射到保存這個命名空間下所有類的根目錄,就可以了。當然這個根目錄以下的文件結構和類定義要遵循PSR-4,不然autoloader是找不到對應文件的。
試一下:

I. 定義命名空間和類文件

我們決定在/libs/vendors目錄下定義一組以命名空間組織的類,其根目錄命名爲free-classes,這組類的全部在命名空間free_classes下。注意這裏我故意使根目錄名與根命名空間名不一致以表示映射根目錄不一定要和命名空間同名。
創建文件/libs/vendors/free-classes/persons/Slave.php,沒有目錄請自行創建。

<?php
namespace free_classes\persons;

class Slave
{
    public static function isFree()
    {
        var_dump("I'm FREE now, thank you!");
    }
}

創建文件/libs/vendors/free-classes/vehicles/cars/Porsche.php

<?php
namespace free_classes\vehicles\cars;

class Porsche
{
    public static function isFree()
    {
        var_dump('Are you kidding?!');
    }
}

注意: free-classes以下的目錄名和結構都要遵循PSR-4標準。

II. 配置\yii\web\Application::$aliases

這裏要說一下,如果我們的命名空間爲namespace\subnamespace,那麼我們應該設置的路徑別名就是@namespace/subnamespace(詳解參照PSR-4 )。
打開配置文件/config/web.php,配置Application的aliaes屬性:

<?php

Yii::$classMap['Freedom'] = '@app/libs/Freedom.php';
...
$config = [
    'id' => 'basic',
    ...
    'aliases' => [
        '@free_classes' => '@app/libs/vendors/free-classes'
    ],
    ...
];
return $config;

III. 使用命名空間下的類

又要見證奇蹟了,還是選在SiteController::actionIndex()裏。

<?php
...
use free_classes\persons\Slave; // 還是別忘了導入
use free_classes\vehicles\cars\Porsche;

class SiteController extends Controller
    ...
    public function actionIndex()
    {
        // Freedom::yell();
        Slave::isFree();
        Porsche::isFree();
    }

}

刷新一下;-)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章