PHP PSR-4 Autoloader 機制

PSR-4 Autoloader

寫 PHP 最麻煩的就是 require/includ 檔案了。如今只要照著 PSR-4 的規範實作,就可以使用 Composer 提供的 autoload,自動載入相對應的檔案。

介紹

PSR-4 Autoloader 規範描述了如何架構專案的目錄結構及如何使用命名空間,遵循這個規範並搭配 Composer 提供的 autoload 就可以完成自動載入的動作。要使用 PSR-4 Autoloader 必須使用正確的命名空間。

這裡的類別泛指類別(classes)、介面(interfaces)、特性(traits)等等相似的結構。

一個完整的類別名稱的形式如下:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
  1. 完整的類別名稱(fully qualified class name)必須有最高層級(top-level)的命名空間(namespace),通常稱這個命名空間為「供應商命名空間(vendor namespace)」。
  2. 完整的類別名稱可以有一個或多個子命名空間。
  3. 一個完整的類別名稱必須以類別名稱結束。
  4. 在完整的類別名稱中,底線沒有其他意義。
  5. 完整的類別名稱可以由英文字母大小寫組合而成。
  6. 所有的類別名稱必須是有區分大小寫的。
以「完整的類別名稱」來載入相對應的檔案:
  1. 在「完整的類別名稱」中,由一個或多個連續的「前置命名空間」及「子命名空間」組成。將「前置命名空間」中包含的分隔符號去除,表示為命名空間的前綴(prefix)名稱,它會對應到至少一個實體目錄,該目錄就稱為基底目錄(base directory)。
  2. 在「前置命名空間」之後的子命名空間,會對應到基底目錄下的子目錄。命名空間的分隔符號表示為目錄的階層,意思是子目錄必須對應到子命名空間。
  3. 最終的類別名稱,會對應到實體的 .php 檔案名稱,該檔案名稱的大小寫必須和類別名稱相同。

看幾個範例就能找出規則了。
範例 1:
完整類別名稱\Acme\Log\Writer\File_Writer
命名空間前置名稱Acme\Log\Writer
基底目錄./acme-log-writer/lib/
檔案路徑./acme-log-writer/lib/File_Writer.php

範例 2:
完整類別名稱\Aura\Web\Response\Status
命名空間前置名稱Aura\Web
基底目錄/path/to/aura-web/src/
檔案路徑/path/to/aura-web/src/Response/Status.php

範例 3:
完整類別名稱\Symfony\Core\Request
命名空間前置名稱Symfony\Core
基底目錄./vendor/Symfony/Core/
檔案路徑./vendor/Symfony/Core/Request.php

範例 4:
完整類別名稱\Zend\Acl
命名空間前置名稱Zend
基底目錄/usr/includes/Zend/
檔案路徑/usr/includes/Zend/Acl.php

規則就是,
因為:命名空間前置名稱 = 基底目錄
於是:(完整類別名稱 - 命名空間前置名稱) = (檔案路徑 - 基底目錄)
而且:完整類別名稱 最後一個分隔符號之後的名稱 = 檔案路徑 最後一個分隔符號之後的名稱.php = 類別名稱

實作

專案都依照 PSR-4 來架構後,就可以使用 Composer 來自動載入相關的檔案了。

設定

首先,假設有一個新專案叫 demo,目錄名稱就叫 demo,其下有兩個子目錄:src 及 public,結構如下:
demo
  /src
  /public
在根目錄下建立 composer.json,內容如下:
JSON{
 "autoload": {
  "psr-4": {
   "Tonycube\\": "src/Tonycube"
  }
 }
}
這裡表示要使用 PSR-4 標準的 autoload,"Tonycube" 表示「命名空間前置名稱」,對應到實體目錄的 "src/Tonycube",所以我們要在 src 目錄下建一個子目錄 Tonycube。
目前的檔案結構如下:
demo
  /src
    /Tonycube
  /public
  composer.json
這裡先確定你已經有安裝 composer。在終端機下,進入 demo 目錄,接著輸入
composer dump-autoload
成功的話,會建立一個 vendor 目錄,內含 autoload.php:
demo
  /src
    /Tonycube
  /public
  /vendor
    autoload.php
    /composer
  composer.json
之後的 PHP 檔案只要 require 這個 autoload.php 檔,就可以自動載入相對應的 PHP 檔案,不用再老是寫一堆 require 了。

程式碼

假設在 Tonycube 目錄下再建立一個目錄 Hello,然後在其中建 3 個示範檔案,如下:
src/Tonycube/Hello/World.php
PHP<?php
namespace Tonycube\Hello;

class World
{
 function __construct()
 {
  echo 'Hello World ~'.PHP_EOL;
 }
}
src/Tonycube/Hello/Tony.php
PHP<?php
namespace Tonycube\Hello;

class Tony
{
 function __construct()
 {
  echo 'Hello Tony ~'.PHP_EOL;
 }
}
src/Tonycube/Hello/Any.php
PHP<?php
namespace Tonycube\Hello;

class Any
{
 function __construct($name)
 {
  echo 'Hello '.$name.PHP_EOL;
 }
}
這樣就建立了 3 個可以被使用的類別。
接著在 public 下,建立一個 index.php :
PHP<?php
require '../vendor/autoload.php';

use \Tonycube\Hello\World as World;
use \Tonycube\Hello;

$world = new World();
$tony = new Hello\Tony();
$any = new \Tonycube\Hello\Any('Amy');
首先會 require vendor 目錄中的 autoload.php,由它來幫我們執行自動載入的工作。
這裡示範 3 種在命名空間下使用類別的方式。
  • use … as … :表示使用完整類別名稱,並且使用 as 建立別名,之後就可以直接使用這個別名。
  • use :可以使用完整類別名稱,這樣在 new 的時候,只要寫類別名稱即可。或是像這個範例,只寫到子命名空間 Hello,這樣之後在 new 的時候,就必須包含這個子命名空間。
  • 不使用 use:這樣 new 的時候就必須使用完整類別名稱。

執行

如果這個專案是放在伺服器目錄的話,可以在瀏覽器上輸入以下連結:
http://localhost/demo/public/index.php
或是在終端機,進入 public 目錄下:
php index.php
註:你必須先把 php 指令加入環境變數中。
最終檔案結構:
demo
  /src
    /Tonycube
      /Hello
        Any.php
        Tony.php
        World.php
  /public
   index.php
  /vendor
    autoload.php
    /composer
  composer.json

更新 autoload 內容

很重要,如果你有修改過 composer.json 裡的 autoload 的內容(或第一次使用),記得必須在終端機輸入:
composer dump-autoload
讓 Composer 把資料更新,至於它是更新哪個檔案的內容,你可以打開:
vendor/composer/autoload_psr4.php
這個檔案,回傳的陣列值中,就會包含我們所指定的內容。

相關影片

這部影片的說明很詳細,把舊的 require 檔案方式到使用 PSR-4 autoloader 的過程講了一遍。
本文網址:http://blog.tonycube.com/2016/09/php-psr-4-autoloader.html
Tony Blog 撰寫,轉載時請註明出處及文章連結,謝謝 😀

我要留言

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。