首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

ThinkPHP 模板變量運算解決方案:自定義函數實現加減乘除與進階邏輯

     編輯:PHP 相關知識

ThinkPHP 模板變量運算解決方案:自定義函數實現加減乘除與進階邏輯

ThinkPHP 模板引擎預設不支持變量直接運算(如 {$a+$b}),官方推薦通過自定義函數實現。本文提供完整封裝方案,支援四則運算、比較邏輯、格式化輸出等常見場景。

核心原理:模板標簽調用 PHP 函數

ThinkPHP 模板語法 {:functionName($arg1, $arg2)} 可調用任意 PHP 可調用單元(函數、類靜態方法、匿名函數)。利用此機制封裝運算邏輯。

基礎運算函數庫實現

// application/common.php (ThinkPHP 5/6 自動載入)
/**
 * 模板運算函數庫
 * 用法:{:math_add($a, $b)} {:math_sub($price, $discount)} {:math_mul($qty, $unit_price)}
 */

/** 加法 */
function math_add($a, $b): float|int {
    return (float)$a + (float)$b;
}

/** 減法 */
function math_sub($a, $b): float|int {
    return (float)$a - (float)$b;
}

/** 乘法 */
function math_mul($a, $b): float|int {
    return (float)$a * (float)$b;
}

/** 除法(含除零保護) */
function math_div($a, $b, int $precision = 2): float|string {
    $b = (float)$b;
    if ($b == 0) return 'N/A'; // 或拋異常、返回 0、返回 null
    return round((float)$a / $b, $precision);
}

/** 取模 */
function math_mod($a, $b): int {
    return (int)$a % (int)$b;
}

/** 百分比計算 */
function math_pct($part, $total, int $precision = 2): string {
    $total = (float)$total;
    if ($total == 0) return '0%';
    return round((float)$part / $total * 100, $precision) . '%';
}

進階邏輯函數:比較、條件、格式化

/*** 比較運算(返回布爾,配合模板 if 標簽使用) **/
function math_gt($a, $b): bool { return (float)$a > (float)$b; }
function math_lt($a, $b): bool { return (float)$a < (float)$b; }
function math_gte($a, $b): bool { return (float)$a >= (float)$b; }
function math_lte($a, $b): bool { return (float)$a <= (float)$b; }
function math_eq($a, $b): bool { return (float)$a == (float)$b; }
function math_neq($a, $b): bool { return (float)$a != (float)$b; }

/** 條件選擇(三元運算符替代) */
function math_if($condition, $trueVal, $falseVal) {
    return $condition ? $trueVal : $falseVal;
}

/** 數字格式化 */
function math_format($number, int $decimals = 2, string $decPoint = '.', string $thousandsSep = ','): string {
    return number_format((float)$number, $decimals, $decPoint, $thousandsSep);
}

/** 貨幣格式化 */
function math_currency($amount, string $symbol = '¥', int $decimals = 2): string {
    return $symbol . math_format($amount, $decimals);
}

/** 範圍限制 */
function math_clamp($value, $min, $max): float {
    $value = (float)$value;
    return max($min, min($max, $value));
}

模板中使用示例

{* 基礎四則運算 *}

總價:{:math_mul($goods.price, $goods.num)}

折扣價:{:math_sub($goods.price, $goods.discount)}

單價:{:math_div($order.total, $order.qty, 2)}

{* 比較邏輯配合 if *} {if condition="math_gt($user.score, 1000)"} VIP 用戶 {else} 普通用戶 {/if} {* 條件顯示 *}

庫存:{:math_if(math_gt($goods.stock, 0), '有貨', '缺貨')}

{* 格式化輸出 *}

金額:{:math_currency($order.amount)}

佔比:{:math_pct($cate.sales, $total_sales)}

{* 陣列元素運算(注意語法) *} {* 錯誤:{:math_add($data['a'], $data['b'])} *} {* 正確:{:math_add($data.a, $data.b)} 或 {:math_add($data.a, $data.b)} *}

總計:{:math_add($order.subtotal, $order.shipping)}

類封裝方案(更推薦,支援鏈式調用、依賴注入)

// app/common/TemplateMath.php
namespace app\common;

class TemplateMath {
    /** 加法 */
    public static function add($a, $b): float { return (float)$a + (float)$b; }
    /** 減法 */
    public static function sub($a, $b): float { return (float)$a - (float)$b; }
    /** 乘法 */
    public static function mul($a, $b): float { return (float)$a * (float)$b; }
    /** 除法 */
    public static function div($a, $b, int $precision = 2): float|string {
        $b = (float)$b;
        return $b == 0 ? 'N/A' : round((float)$a / $b, $precision);
    }
    /** 格式化 */
    public static function format($num, int $d = 2): string { return number_format((float)$num, $d); }
    /** 貨幣 */
    public static function currency($amt, string $sym = '¥'): string { return $sym . self::format($amt); }
}

// 模板調用:{:app\common\TemplateMath::add($a, $b)}
// 或註冊別名(config/template.php 'function_alias' => ['tmath' => 'app\common\TemplateMath'])
// 然後:{:tmath::add($a, $b)}

ThinkPHP 6/8 現代化最佳實踐

1. 服務容器註冊(支援依賴注入、單例)

// config/provider.php
return [
    app\common\TemplateMath::class => [],
];

// 控制器/模板中自動可用:{:app('tmath')->add($a, $b)}

2. 模板引擎擴展(原生語法支援)

// 註冊模板標簽(ThinkPHP 6+)
\think\facade\View::addFunction('add', 'app\common\TemplateMath::add');
\think\facade\View::addFunction('sub', 'app\common\TemplateMath::sub');
// 然後模板可直接:{$a|add=$b} 或 {:add($a, $b)}

3. 視圖模型/DTO 模式(推薦:邏輯前置,模板純展示)

// app/viewmodel/OrderViewModel.php
class OrderViewModel {
    protected Order $order;
    
    public function __construct(Order $order) { $this->order = $order; }
    
    public function getTotalPrice(): float {
        return $this->order->subtotal + $this->order->shipping - $this->order->discount;
    }
    
    public function getFormattedTotal(): string {
        return '¥' . number_format($this->getTotalPrice(), 2);
    }
    
    public function isFreeShipping(): bool {
        return $this->order->subtotal >= 99;
    }
}

// 控制器
public function detail($id) {
    $order = Order::find($id);
    $viewModel = new OrderViewModel($order);
    return view('order/detail', ['vm' => $viewModel]);
}

// 模板:純屬性訪問,零邏輯

總價:{$vm.formattedTotal}

{$vm.isFreeShipping ? '免運費' : '需付運費'}

方案對比與選型建議

方案優點缺點適用場景
全局函數簡單直接、模板調用短污染全局命名空間、難測試、無依賴注入極簡項目、快速原型
靜態類方法命名空間隔離、可測試、IDE 提示友好靜態調用難 Mock、模板語法稍長中小型項目、團隊協作
服務容器/視圖模型依賴注入、可測試、關注點分離、複雜邏輯封裝初始代碼量大、學習曲線陡中大型項目、長期維護、團隊規範
模板原生擴展語法最優雅(修飾符管道)、性能最好框架版本綁定、調試相對困難高性能要求、框架深度用戶

避坑指南

  1. 模板變量輸出必須轉義:運算結果若含用戶輸入,務必 {:htmlspecialchars(math_add($a, $b))} 或使用框架內置輸出轉義
  2. 浮點數精度陷阱:金額計算用 bcmath 擴展或整數分單位,避免 0.1 + 0.2 = 0.30000000000000004
  3. 除零異常處理:統一返回 'N/A'/'--'/0,避免模板拋出 PHP Warning 破壞頁面
  4. 數組鍵名語法:模板中 $data.key 而非 $data['key'],嵌套用 $data.key.sub
  5. 性能考量:模板中避免重複調用昂貴運算,控制器預計算好傳入視圖模型

ThinkPHP 模板不支持直接運算是為了強制「邏輯與視圖分離」。正確路徑:簡單運算用靜態類/全局函數,複雜邏輯用視圖模型/服務類,極致性能用模板原生擴展。選型原則:能在控制器/視圖模型預計算的,絕不放到模板運算。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved