CodeIgniter 的 third_party 目錄專門存放外部套件(如 Twig、PHPMailer、AWS SDK 等)。這些類庫功能豐富但 API 複雜,直接在控制器/模型中使用會造成:
最佳實踐:在 libraries 建立薄封裝層,暴露簡潔、符合專案慣例的介面,隱藏實作細節。
下載 Twig (via Composer 或官網),解壓至 application/third_party/Twig/。
application/libraries/Twig.php<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
require APPPATH . 'third_party/Twig/Autoloader.php';
class Twig {
protected $twig;
protected $config;
protected $data = [];
public function __construct($config) {
// 1. 合併預設配置
$defaults = [
'cache_dir' => FALSE,
'debug' => FALSE,
'auto_reload' => TRUE,
'extension' => '.tpl',
'template_dir' => APPPATH . 'views/',
];
$this->config = array_merge($defaults, $config);
// 2. 註冊自動載入器並初始化 Twig 環境
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem($this->config['template_dir']);
$this->twig = new Twig_Environment($loader, [
'cache' => $this->config['cache_dir'],
'debug' => $this->config['debug'],
'auto_reload' => $this->config['auto_reload'],
]);
// 3. 註冊 CI 輔助函數為 Twig 全域函數
$CI =& get_instance();
$CI->load->helper('url');
$this->twig->addFunction(new Twig_SimpleFunction('site_url', 'site_url'));
$this->twig->addFunction(new Twig_SimpleFunction('base_url', 'base_url'));
// 可繼續註冊其他自訂函數/過濾器/全域變數
}
/** 指派變數到模板 */
public function assign($var, $value = NULL) {
if (is_array($var)) {
foreach ($var as $key => $val) {
$this->data[$key] = $val;
}
} else {
$this->data[$var] = $value;
}
}
/** 渲染模板檔案 */
public function render($template, $data = [], $return = FALSE) {
$template = $this->twig->loadTemplate($this->getTemplateName($template));
$data = array_merge($this->data, $data);
return $return ? $template->render($data) : $template->display($data);
}
/** 渲染字串模板 */
public function parse($string, $data = [], $return = FALSE) {
$template = $this->twig->loadTemplate($string); // 從字串載入
$data = array_merge($this->data, $data);
return $return ? $template->render($data) : $template->display($data);
}
/** 處理副檔名自動補全 */
protected function getTemplateName($template) {
$ext = $this->config['extension'];
if (substr($template, -strlen($ext)) !== $ext) {
$template .= $ext;
}
return $template;
}
}
/* End of file Twig.php */
/* Location: ./application/libraries/Twig.php */
application/config/twig.php<?php $config['extension'] = '.tpl'; $config['template_dir'] = APPPATH . 'views/'; $config['cache_dir'] = APPPATH . 'cache/twig/'; $config['debug'] = (ENVIRONMENT === 'development'); $config['auto_reload'] = TRUE; /* End of file twig.php */
CI 會自動載入同名配置檔並傳入建構子。
class Blog extends CI_Controller {
public function index() {
$this->load->library('twig');
$this->twig->assign('title', '最新文章');
$this->twig->assign('posts', $this->Post_model->get_latest(10));
// 直接輸出
$this->twig->render('blog/index');
// 或取得字串用於 AJAX 回應
// $html = $this->twig->render('blog/_list', ['posts' => $posts], TRUE);
}
}
上述範例中 Twig 類別直接依賴 get_instance() 取得 CI 輔助函數,導致難以在非 CI 環境 (如 CLI 命令、單元測試、其他框架) 複用。改進方案:
application/services/ViewService.phpclass ViewService {
protected $twig;
public function __construct(Twig_Environment $twig) {
$this->twig = $twig;
// 註冊通用函數、過濾器、全域變數
$this->twig->addFunction(new Twig_SimpleFunction('asset_url', function($path) {
return base_url('assets/' . ltrim($path, '/'));
}));
}
public function render($template, array $data = []) {
return $this->twig->render($template, $data);
}
public function renderString($string, array $data = []) {
return $this->twig->render($string, $data);
}
}
// libraries/Twig.php 修改為工廠模式
class Twig {
public static function createService($config) {
// ... 初始化 Twig_Environment ...
return new ViewService($twig);
}
}
// 控制器
$this->load->library('twig');
$view = Twig::createService($config);
$view->render('blog/index', ['posts' => $posts]);
| 原則 | 說明 |
|---|---|
| 薄封裝 | Library 只負責初始化、配置合併、CI 整合,核心邏輯委託給 Service |
| 配置外部化 | 所有可調參數放在 config/twig.php,支援環境區分 (development/production) |
| 介面導向 | 控制器只依賴 ViewService 介面,不依賴具體 Twig 實作 |
| 單一職責 | 模板渲染、資料準備、HTTP 回應分離 |
cache/twig/ 可寫入 (chmod 775)Twig_Librarydebug = FALSEauto_reload,部署時手動清除快取同樣模式可應用於:
EmailService,支援多種郵件驅動 (SMTP, API, Sendmail)S3Service、SQSService 等核心精神:**將「如何使用第三方套件」的知識封裝在內部,對外提供「業務需要什麼功能」的介面。**