[精讚] [會員登入]
180

[PHP8] 使用autoload autoload+ namespace +use

到了php7之後,namespace和use越來越重要,此篇整理autoload和namespace、use的結合使用。

分享此文連結 //n.sfs.tw/16033

分享連結 [PHP8] 使用autoload autoload+ namespace +use@新精讚
(文章歡迎轉載,務必尊重版權註明連結來源)
2022-12-30 12:30:48 最後編修
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

 

END

你可能感興趣的文章

[PHP] 使用FTP PHP 上使用 FTP 的寫法

[PHP] 將UTF8中文字轉成10進位或16進位數值 原本為了處理 preg_match 中文字的問題[2],用php把中文字轉換成10進位和6進位的數值編碼

[PHP] 輸出EXCEL的最簡易方法 輸出EXCEL最簡易方法,就沒要求太多了

[PHP] 如何寫callback function 召回函數(回呼函數) PHP如何寫召回函數或回呼函數(callback function)?

[PHP] 讀取作業系統程式執行結果 PHP讀取作業系統程式執行結果

[CodeIgniter 3] 自寫找不到頁面(page404)的方法 使用CI3框架中如果找不到頁面,就會導到一個自定的404頁面,該怎麼做?

我有話要說

>>

限制:留言最高字數1000字。 限制:未登入訪客,每則留言間隔需超過10分鐘,每日最多5則留言。

訪客留言

[無留言]

隨機好文

使用Google尋找你的手機 這近發現google竟然可以用來找android的手機,而且不需要經過什麼設定或安裝軟體。

[jQuery] textarea 的取值和給值 HTML 的 TEXTAREA 標籤若要用 jquery 取值,不能使用 .text() 或 .html() ,使用 .

[HP DL380G7] 生效啟動第3,4片網卡/開啟或關閉內建的網卡 HP DL380G7 預設第3,4片網卡裝完系統後找不到,難道是壞了?要怎麼辦?

[Win7] 燒錄 iso 檔 在Windows7 中內建燒錄程式,可以直接把檔案拉到光碟機裡,再執行燒錄。

[PHP] 檢查IP是否在某個網段內 mtachcidr 要檢查IP是否在某個網段內,要寫幾行?10行?5行? 不用,只要2行。以下是我寫的 code /** * matchCI