前言
在 PHP 中引入外部 PHP 命令稿不僅可用來引入其他 PHP 模板,也可以引入第三方 PHP 命令稿。本文以實際範例來展示引入外部 PHP 命令稿的方式。
四種引入外部 PHP 命令稿的指令
根入引入的方式,可再細分為以下四種:
- 選擇性引入:未正確地引入時不會引發錯誤
include
:可重覆引入多次include_once
:僅會引入一次
- 必要性引入:未正確地引入時會引發錯誤
require
:可重覆引入多次require_once
:僅會引入一次
實際使用時,要根據情境來選擇適合的指令。
引入社群套件時,未正確引入即無法使用特定功能,而且函式不能重覆宣告,故使用 require_once
。
引入模板時,未正確引入僅影響部分外觀,但不致於無法觀看頁面,而且有可能會重覆引入,故使用 include
。
(範例) 循環引入特定目錄內所有的 PHP 命令稿
本節所要執行的任務是循環載入特定目錄內所有的 PHP 命令稿。雖然 Composer 可以自動提供這項功能,但不一定所有的 PHP 命令稿都適合寫成 Composer 套件,所以本節展示不依賴 Composer 或其他外部套件的方式。
以下是完整的程式碼。我們會在下文說明其實作過程:
<?php /* 1 */
# A home-made autoload.php script. /* 2 */
# Get current working directory of the script. /* 3 */
$cwd = __DIR__; /* 4 */
# Create a queue for unvisited directories. /* 5 */
$dirs = array(); /* 6 */
# Push current working dirctory into the queue. /* 7 */
array_push($dirs, $cwd); /* 8 */
while (count($dirs) > 0) { /* 9 */
# Pop out a directory. /* 10 */
$dir = array_shift($dirs); /* 11 */
# Scan all files in the directory. /* 12 */
$libraries = scandir($dir); /* 13 */
# Iterate over the layer of directories and files. /* 14 */
foreach ($libraries as $library) { /* 15 */
# Skip private directories and files. /* 16 */
if ("." == substr($library, 0, 1)) { /* 17 */
continue; /* 18 */
} /* 19 */
else if ("_" == substr($library, 0, 1)) { /* 20 */
continue; /* 21 */
} /* 22 */
$sep = DIRECTORY_SEPARATOR; /* 23 */
$path = $dir . $sep . $library; /* 24 */
# Skip the script itself. /* 25 */
if (__FILE__ == $path) { /* 26 */
continue; /* 27 */
} /* 28 */
# Push a subdirectory into the queue. /* 29 */
if (is_dir($path)) { /* 30 */
array_push($dirs, $path); /* 31 */
} /* 32 */
# Load a PHP script. /* 33 */
else if ("php" == pathinfo($path)["extension"]) { /* 34 */
require_once $path; /* 35 */
} /* 36 */
} /* 37 */
} /* 38 */
這段程式關鍵的運算是循環走訪目錄和檔案。循環走訪特定目錄的運算方式如下:
- 將目標目錄加入佇列 (Q)
- 從佇列取出目錄 (D)
- 掃描 D,若得到子目錄 (d),將 d 加入 Q
- 當 Q 不為空,重覆步驟 2.
- 直到 Q 為空時結束運算
本節任務是循環掃描自身所在的目錄,所以先取得當前位置 $cwd
(第 4 行),將該位置加入佇列 $dirs
(第 8 行)。
當佇列不為空時,迭代一輪程式碼 (第 9 行)。從佇列中取出目錄 $dir
(第 11 行)。利用 PHP 內建變數掃描該目錄 (第 13 行)。
接著,逐一走訪掃描結果 (第 15 行)。當結果是 .
開頭的檔案或目錄即略過 (第 17 至 19 行)。因為特殊目錄 .
(本層目錄) 和 ..
(上層目錄) 也會掃描到,不排除的話會變無窮迴圈。此外,將 _
開頭的檔案或目錄也略過 (第 20 行至 22 行)。透過這種方式,就可以將私有函式寫在裡面。
在掃描過程中,有可能掃到命令稿本身。這時候就將命令稿自身排除掉 (第 23 至 28 行)。
當掃到子目錄時,將子目錄加入佇列,待下一輪迭代繼續掃描 (第 30 至 32 行)。若掃到 PHP 命令稿,則用 require_once
引入該命令稿 (第 34 至 36 行)。除此之外,則不進行動作。
本節任務預期引入的是函式宣告,故使用 require_once
來引入。
(範例) 簡易的外掛載入程式
本節任務是實作簡易的外掛載入程式。有時候在專案中有些功能是選擇性的,不適合寫在核心中,就可以將其寫成外掛,視需求來載入。日後也可以加入其他開發者寫的外掛,讓專案功能更豐富。
以下是完整的程式碼。我們會在下文說明其實作過程:
<?php /* 1 */
# The main loader for plugin(s). /* 2 */
$sep = DIRECTORY_SEPARATOR; /* 3 */
# Get root path. /* 4 */
$rootDirectory = __DIR__ . $sep . ".."; /* 5 */
# Load global settings. /* 6 */
require_once $rootDirectory . $sep . "setting.php"; /* 7 */
# Scan all files in the directory. /* 8 */
$libraries = scandir(__DIR__); /* 9 */
# We only scan top layer of this directory. /* 10 */
foreach ($libraries as $library) { /* 11 */
# Skip private directories and files. /* 12 */
if ("." == substr($library, 0, 1)) { /* 13 */
continue; /* 14 */
} /* 15 */
else if ("_" == substr($library, 0, 1)) { /* 16 */
continue; /* 17 */
} /* 18 */
# Pass plugin in the black list. /* 19 */
if (in_array($library, PLUGIN_BLACKLIST)) { /* 20 */
continue; /* 21 */
} /* 22 */
$path = __DIR__ . $sep . $library; /* 23 */
# Skip the script itself. /* 24 */
if (__FILE__ == $path) { /* 25 */
continue; /* 26 */
} /* 27 */
if (is_dir($path)) { /* 28 */
# autoload.php at the root path of a plugin /* 29 */
# is mandatory. /* 30 */
$loader = $path . $sep . "autoload.php"; /* 31 */
# Load the plugin. /* 32 */
require_once $loader; /* 33 */
} /* 34 */
} /* 35 */
載入外掛時,不需要自行循環載入所有的 PHP 命令稿,只要載入每個外掛的 autoload.php 命令稿即可。我們將載入外掛命令稿的責任委由 autoload.php 來完成,不介入實際載入命令稿的過程。
這裡的目錄階層採用扁平的排列方式。每個外掛使用一個目錄。外掛應該要在其根目錄提供 autoload.php 。除此之外,本程式不介入實際載入 PHP 命令稿的過程。
先取得專案的設置 (第 3 至 7 行)。這是為了取得外掛的排除名單 PLUGIN_BLACKLIST
(第 20 行)。該常數是一個陣列,將要排除的外掛寫進設置。
掃描當前目錄以取得所有的子目錄 (第 9 行)。接著,走訪這些目錄和檔案 (第 11 行)。如同先前的範例,要排除特殊目錄和私有目錄 (第 13 至 18 行)。
如果該外掛在排除清單中,則略過該次迭代 (第 20 至 22 行)。如同前例,掃描時會掃到此程式本身,也要排除 (第 23 至 27 行)。
當掃到子目錄時,代表掃到某個外掛。這時引入該目錄內的 autoload.php (第 28 至 34 行)。除此之外,則不進行動作。