[PHP8] 使用autoload autoload+ namespace +use

URL Link //n.sfs.tw/16033

2022-12-29 16:01:47 By 張○○

我覺得php7之後的php應該就是namespace的世界。

此篇簡單整理autoload和namespace的結合使用。

過去的做法

過去,我們主程式index.php載入類別時myclass.php,一般會寫成這樣:

index.php

<?php
@include_once( 'myclass.php');

最前面的@是忽略錯誤的符號。假加有很多的類別,且放在不同的目錄下,我們都要寫很多行並且加上目錄路徑,例如寫成這樣:

index.php

<?php
@include_once( 'myclass1.php');
@include_once( '/var/www/html/class/myclass2.php');  <== 絕對路徑表示法
@include_once( 'class/myclass3.php');  <== 使用相對路徑
@include_once( __DIR__ .'/myclass4.php');  <== 本目錄下的檔案

引入後 再new還是幹嘛的都可以。

這樣的做法對於要引入很多檔案的情況,會寫長長的一串,所以我們可以使用 php5開始提供的autoload 功能,在php8以前autoload的實作方式是在程式最開始加入一個__autoload()函數,並把autoload的規則寫在此函數裡,這個函數像是黑手一樣的存在,可以自動載入你叫用的類別。很可惜這個方法在php8之後就被廢棄並報錯:

Fatal error: __autoload() is no longer supported, use spl_autoload_register() instead in xxx

 

php8 之後的autoload

php8之後,可以採用 spl_ 相關的函數來實現autoload,並且能和 namespace 結合,spl是 Standard PHP Library 的縮寫,主要的函數只有一個

spl_autoload_register()

先看看檔案架構

.
├── module
│   └── shop
│       └── taipei.php
└── index.php

這裡只有兩個檔案,一個是索引用的 index.php 和類別檔 taipei.php

在傳統作法中,只要 include('module/shop/taipei.php) 然後就可以想幹嘛就幹嘛。

類別taipei沒做什麼,只有一個函數sell(),印出販賣的商品是蘋果:

taipei.php

<?php
namespace module\shop;

class taipei {
  public function sell(){
    return "蘋果";
  }
}

但是想用autoload方法來實作的話,程式寫成這樣就可以了:

index.php

<?php
spl_autoload_register();

$myshop = new module\shop\taipei;
print $myshop->sell(); //蘋果

可以看到不過加了第2行的函數spl_autoload_register(),什麼路徑都不必指定,也不用叫用 include什麼的,他自然而然的會去路徑 module/shop/ 中找到 taipei.php 的這個類別並載入。

這簡直太神奇?!

到底spl_autoload_register()做了什麼事才會這麼神奇?

眼尖的人可能有發現,index.php第4行中的new 後面指定了namespace,namespace在此發揮了巨大的作用。

我們能猜測他內部是把namespace轉成路徑,並區分最後一個部分當成類別名。

同時要注意,類別中的namespace宣告要和初始化的一致(taipei.php第2行),這樣就漂亮的整合了namespace和autoload

 

自定義的autoload

如果上面預設的autoload規則不符合需求的話,只能自己來訂定,以下範例檔案架構變成這樣,我們增加一個system的目錄,假設裡面會放一些和公司本身規定有關的程式碼。

.
├── module
│   └── shop
│       └── taipei.php
├── index.php
├── autoload.php
└── system
    └── promote
        └── year2023.php

這裡多了一個類別 year2023.php,以及後面才會加入的autotload.php

我們可以用上面的做法,直接初始化

$activity = new system\promote\year2023;

就能自動載入該類別。

但如果 promote/ 目錄下的活動很多,我們想全部都編成core這個namespace的話,就要改一下autoload的規則。先看程式碼:

year2023.php

<?php
namespace core ;

class year2023 {
  public function anniversary(){
    return "3/31-4/5";
  } 
} 

注意上面第2行的namespace改成core。

index.php

<?php 
spl_autoload_register(
  function ($myclass){
    $segment= explode("\\",$myclass,2);
    if( $segment[0]==="core"){
      @include_once(__DIR__ . '/system/promote/' . str_replace('\\', '/', $segment[1]). '.php');
    }else 
      @include_once(__DIR__ . '/' . str_replace('\\', '/', $myclass) . '.php');
  });
$activity= new core\year2023;
print $activity->anniversary();  //3/31-4/5

上面在 spl_autoload_register()中放入了一個函數,第3行$myclass就是所有被叫用的namespace+類別。

第4行把字串$myclass拆開分成2段,第一個字節和剩下的其它部分。

第5-6行 namespace的第一個字節如果是core開頭的,就去/system/promote/裡面找類別

第7-8行 除此之外就是和原本的autoload一樣。

接下來第9行引用時,namespace直接可以用core來引用,假如這行寫成

$activity= new system\promote\year2023;

可不可以?當然不行,因為一個類別只能有一個namespace。

我們可以把自定義的部分拆出來,新增一個autoload.php程式,爾後所有的程式都可以叫用同樣的規則。

autoload.php

<?php
    spl_autoload_register(
        function ($myclass){
            $segment= explode("\\",$myclass,2);
            if( $segment[0]==="core"){
    @include_once(__DIR__ . '/system/promote/' . str_replace('\\', '/', $segment[1]). '.php');
            }else
    @include_once(__DIR__ . '/' . str_replace('\\', '/', $myclass) . '.php');
        });

index.php

<?php
include_once(__DIR__ ."/autoload.php");

$activity= new core\year2023;
print $activity->anniversary(); //3/31-4/5

 

use? 怎麼use?

如果你討厭每個函數之前都要帶著長長的namespace,你可以使用use

index.php

<?php
include_once(__DIR__ ."/autoload.php");

use module\shop\taipei;
use core\year2023;

$myshop = new taipei;
print $myshop->sell(); //蘋果

$activity= new year2023;
print $activity->anniversary(); //3/31-4/5

第4-5指定了use,在第7和第10行就不必帶上namespace了。

這部分可參考2016末寫的文章[PHP] 命名空間 namespace及 use@新精讚至今剛好滿6年。

 

結論

1. namespace+ autoload是好兄弟,php8以上請採用這種寫法。

2. __autoload 方法已經在php8作廢。

3. 目錄不要使用php的關鍵字來取名,例如你不要把目錄叫作class或是use

4. 自訂義的autoload很方便。

 

參考資料

[1] https://justericgg.logdown.com/posts/196891-php-series-autoload

[2] https://ithelp.ithome.com.tw/articles/10134247