專案常需支援多種設定檔格式(INI、YAML、XML、PHP 陣列)。本文實作一個基底 Settings 類別與四個具體實作類別,提供統一的 load() 與 get() 介面,實現設定檔格式的無縫切換。
abstract class Settings {
protected $_settings = [];
public function get($var) {
$keys = explode('.', $var);
$result = $this->_settings;
foreach ($keys as $key) {
if (!isset($result[$key])) {
return false;
}
$result = $result[$key];
}
return $result;
}
abstract public function load($file);
}
class Settings_PHP extends Settings {
public function load($file) {
if (!file_exists($file)) return false;
include($file);
$vars = get_defined_vars();
foreach ($vars as $key => $val) {
if ($key === 'this') continue;
$this->_settings[$key] = $val;
}
}
}
設定檔範例 (config.php):
<?php $db = []; $db['name'] = 'test'; $db['host'] = 'localhost'; $db['user'] = 'root'; $db['pass'] = 'password'; ?>
class Settings_INI extends Settings {
public function load($file) {
if (!file_exists($file)) return false;
$this->_settings = parse_ini_file($file, true);
}
}
設定檔範例 (config.ini):
[db] name = test host = localhost user = root pass = password
class Settings_XML extends Settings {
public function load($file) {
if (!file_exists($file)) return false;
include('xmllib.php'); // 需第三方 XML 解析庫
$xml = file_get_contents($file);
$data = XML_unserialize($xml);
$this->_settings = $data['settings'];
}
}
設定檔範例 (config.xml):
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<db>
<name>test</name>
<host>localhost</host>
<user>root</user>
<pass>password</pass>
</db>
</settings>
class Settings_YAML extends Settings {
public function load($file) {
if (!file_exists($file)) return false;
include('spyc.php'); // 需 Spyc 庫
$this->_settings = Spyc::YAMLLoad($file);
}
}
設定檔範例 (config.yaml):
db: name: test host: localhost user: root pass: password
// PHP 格式
$settings = new Settings_PHP();
$settings->load('config.php');
echo 'PHP: ' . $settings->get('db.host');
// INI 格式
$settings = new Settings_INI();
$settings->load('config.ini');
echo 'INI: ' . $settings->get('db.host');
// XML 格式
$settings = new Settings_XML();
$settings->load('config.xml');
echo 'XML: ' . $settings->get('db.host');
// YAML 格式
$settings = new Settings_YAML();
$settings->load('config.yaml');
echo 'YAML: ' . $settings->get('db.host');
| 格式 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| PHP 陣列 | 原生支援、效能最好、可包含邏輯/常數 | 安全性風險(可執行程式碼)、非標準格式、難以跨語言共用 | 內部專用、高效能需求、動態設定 |
| INI | 簡單易讀、PHP 內建解析、輕量 | 功能有限(無巢狀深度、無型別)、標準化不一、逐漸過時 | 簡單設定、舊專案維護、PHP 專用 |
| XML | 標準化程度高、跨語言、支援屬性/命名空間/驗證、工具鏈豐富 | 冗長、解析效能較差、複雜度高 | 企業級應用、跨系統整合、需嚴格結構驗證 |
| YAML | 人類可讀性最佳、支援複雜資料結構、錨點/參考、廣泛採用(Docker、K8s、CI/CD) | 解析複雜、標準化歷程曲折、對縮排敏感、需第三方庫 | 現代化專案、設定檔由人維護、DevOps 工具鏈 |
| JSON | 原生 JS 支援、解析快、標準化、無註解 | 無註解、無尾隨逗號、嚴格語法易出錯 | API 回應、前後端共用、靜態設定 |
// 推薦:symfony/config + symfony/yaml
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Yaml\Yaml;
$locator = new FileLocator([__DIR__.'/config']);
$loader = new DelegatingLoader([
new YamlFileLoader($locator),
new PhpFileLoader($locator),
new IniFileLoader($locator),
new XmlFileLoader($locator),
], new LoaderResolver([
new YamlFileLoader($locator),
new PhpFileLoader($locator),
new IniFileLoader($locator),
new XmlFileLoader($locator),
]));
$config = $loader->load('config.yaml'); // 自動偵測格式
class CachedSettings extends Settings {
private $cacheDir;
private $ttl;
public function load($file) {
$cacheFile = $this->cacheDir . '/' . md5($file) . '.php';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $this->ttl) {
$this->_settings = include $cacheFile;
return;
}
parent::load($file);
file_put_contents($cacheFile, '
3. 環境變數覆蓋支援
public function load($file) {
parent::load($file);
// 環境變數優先:DB_HOST 覆蓋 db.host
foreach ($this->_settings as $key => $value) {
if (is_array($value)) {
$this->_settings[$key] = $this->applyEnvOverrides($value, strtoupper($key));
}
}
}
private function applyEnvOverrides(array $arr, string $prefix) {
foreach ($arr as $k => $v) {
$envKey = $prefix . '_' . strtoupper($k);
if (isset($_ENV[$envKey])) {
$arr[$k] = $_ENV[$envKey];
} elseif (is_array($v)) {
$arr[$k] = $this->applyEnvOverrides($v, $envKey);
}
}
return $arr;
}
總結
統一設定檔介面設計體現了「依賴抽象而非具體實作」的 SOLID 原則。雖然本文的手工實作具教學意義,但生產環境強烈建議使用 Symfony Config Component、Laravel Config 或 vlucas/phpdotenv 等成熟方案。選擇格式時,現代專案首推 YAML(人類可讀)或 PHP 陣列(效能最佳),配合環境變數覆蓋實現十二因子應用規範。