文章撰寫於 2000 年代前期,展示使用 **PHP `libswf` 擴充模組 (Ming)** 動態產生 **SWF (Shockwave Flash)** 檔案,繪製餅圖視覺化資料。當時 Flash Player 滲透率 > 90%,向量繪圖、動畫、互動性遠超 HTML/JavaScript (IE 6 時代)。
| 元件 | 說明 | 現狀 |
|---|---|---|
libswf / Ming | C 函式庫 + PHP 擴充,提供 `swf_openfile`, `swf_ortho2`, `swf_shapearc`, `swf_definetext` 等底層繪圖 API | PHP 5.3 移除 (Ming 獨立專案仍存但無人維護) |
| SWF 格式 | 二進位向量圖形/動畫格式,需 Flash Player 播放 | Adobe 2020/12/31 終止支援,瀏覽器全面封鎖 |
| 字體處理 | Type 1 字體轉換工具、/usr/local/swf/fonts/ 目錄需 Web Server 讀取權限 | 現代用 Web Fonts (WOFF2) + CSS `@font-face` |
| 資料來源 | MySQL `city` 表,`city_id`, `city_name`, `city_timestamp` | 標準關聯式資料庫查詢 |
| 輸出方式 | swf_openfile("city.swf", ...) 寫檔 → HTML ` | 現代:API 回傳 JSON → 前端 Chart.js/D3/ECharts 渲染 |
class flash_pie {
var $r_arr = [0.1, 1, 0, 1, 0, 1, 0.388, 0.4, 0.388, 0.929]; // 顏色 R
var $g_arr = [1, 0, 0, 1, 1, 0, 0.8, 0.4, 0.8, 0.439]; // 顏色 G
var $b_arr = [0.25, 0, 1, 0, 1, 1, 1, 0.4, 1, 0.043]; // 顏色 B
function flash_pie($values, $this_file) {
swf_openfile($this_file, 1000, 450, 30, 1, 1, 1); // 寫檔
swf_ortho2(-400, 300, -90, 250); // 座標系
swf_definefont(10, "Mod"); // 字體
// 計算總和、各扇區角度 (度數)
// 迴圈呼叫 show_slice($i, $objID, $begin, $end)
}
function show_slice($i, $objID, $begin, $end) {
swf_addcolor($this->r_arr[$i], $this->g_arr[$i], $this->b_arr[$i], 1);
swf_startshape($objID);
swf_shapefillsolid(0,0,0,1);
swf_shapearc(0, 0, 100, $begin, $end); // 畫圓弧
swf_shapecurveto(0, 0, 0, 0); // 閉合到中心
swf_endshape($objID);
swf_pushmatrix(); swf_placeobject($objID, 1); swf_popmatrix();
swf_showframe(); // 每扇區一幀
}
// pie_legend, pie_text, pie_title, show, close 同理...
}
**PHP 只負責輸出 JSON 資料**,前端使用成熟圖表庫:
| 圖表庫 | 特色 | 授權 | 適合場景 |
|---|---|---|---|
| Chart.js | Canvas、簡單易用、響應式、動畫流暢、Tree-shakable | MIT | 儀表板、報表、簡單圖表 |
| ECharts | Canvas/SVG、功能極豐富 (地圖、3D、流向、關係圖)、主題系統 | Apache 2.0 | 複雜大屏、地理視覺化、企業級 BI |
| D3.js | SVG、資料驅動文檔、底層控制力最強、學習曲線陡峭 | BSD-3 | 高度客製化、學術視覺化、特殊圖表 |
| Apache ECharts / AntV G2 | Grammar of Graphics、TypeScript 優先、React/Vue 整合佳 | MIT/Apache | 現代前端專案、型別安全 |
<!-- PHP API: /api/chart-data.php -->
<?php
header('Content-Type: application/json');
$pdo = new PDO(...);
$stmt = $pdo->query("SELECT city_name, COUNT(*) as cnt FROM city GROUP BY city_name");
$data = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); // [city => count]
echo json_encode(['labels' => array_keys($data), 'values' => array_values($data)]);
<!-- 前端 Vue + Chart.js -->
<script setup>
import { ref, onMounted } from 'vue';
import { Chart, ArcElement, Tooltip, Legend } from 'chart.js';
Chart.register(ArcElement, Tooltip, Legend);
const chart = ref(null);
onMounted(async () => {
const { labels, values } = await fetch('/api/chart-data.php').then(r => r.json());
new Chart(chart.value, {
type: 'pie',
data: { labels, datasets: [{ data: values, backgroundColor: ['#FF6384','#36A2EB','#FFCE56','#4BC0C0','#9966FF','#FF9F40'] }] },
options: { responsive: true, plugins: { legend: { position: 'right' } } }
});
});
</script>
<template><canvas ref="chart"></canvas></template>
需求:無 JavaScript 環境 (郵件、PDF、Open Graph 社群分享圖、爬蟲預覽)。
| 工具 | 語言/引擎 | 輸出格式 | 特色 |
|---|---|---|---|
| Node.js + Puppeteer/Playwright | Headless Chrome | PNG, PDF, JPEG | 完整支援 CSS/Canvas/SVG/動畫、最佳相容性 |
| Python + Matplotlib/Plotly/Kaleido | Agg/ORCA | PNG, SVG, PDF | 科學繪圖標準、向量輸出完美 |
| PHP + GD / Imagick | C Library | PNG, JPEG, GIF, WebP | PHP 原生、無外部依賴、效能佳、功能較簡單 (無動畫、3D) |
| Go + go-echarts / uPlot | Go | PNG, SVG | 高併發、單一二進位部署 |
| QuickChart.io (API) | SaaS | PNG, SVG, PDF | 免維運、URL 參數即時生成、免費額度大 |
<?php
// pie.php?data=10,20,30&labels=A,B,C
$values = array_map('intval', explode(',', $_GET['data'] ?? '1,1,1'));
$labels = explode(',', $_GET['labels'] ?? 'A,B,C');
$colors = ['#FF6384','#36A2EB','#FFCE56','#4BC0C0','#9966FF','#FF9F40','#C9CBCF'];
$sum = array_sum($values);
$w = $h = 400; $r = 150; $cx = $cy = 200;
$img = imagecreatetruecolor($w, $h);
$white = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $white);
imageantialias($img, true);
$start = -90; // 從頂部開始
foreach ($values as $i => $val) {
$angle = 360 * $val / $sum;
$color = imagecolorallocate($img, ...sscanf($colors[$i % count($colors)], "#%2x%2x%2x"));
imagefilledarc($img, $cx, $cy, $r*2, $r*2, $start, $start + $angle, $color, IMG_ARC_PIE);
$start += $angle;
}
// 圖例 (簡易繪製在右側)
$legendX = 320; $legendY = 50;
foreach ($values as $i => $val) {
$color = imagecolorallocate($img, ...sscanf($colors[$i % count($colors)], "#%2x%2x%2x"));
$textColor = imagecolorallocate($img, 0, 0, 0);
imagefilledrectangle($img, $legendX, $legendY, $legendX+15, $legendY+15, $color);
imagestring($img, 3, $legendX+20, $legendY, "{$labels[$i]}: " . round(100*$val/$sum) . "%", $textColor);
$legendY += 25;
}
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
需要互動/動畫/即時更新? → 是 → 前端渲染 (Chart.js/ECharts/D3)
↓ 否
需求環境支援 JS? → 否 → SSR 靜態圖片 (Puppeteer/GD/QuickChart)
↓ 是
SEO/社群分享需靜態圖? → 是 → 雙軌:API + SSR 預渲染 (Next.js/Edge Functions)
↓ 否
純前端渲染即可
PHP `libswf`/`Ming` 產生 SWF 是技術演進中的一個分支,隨著 Flash 終結、HTML5 Canvas/SVG/WebGL 成熟、**「資料與視覺分離」**已成共識。現代架構採用 **PHP 輸出 JSON → 前端聲明式圖表庫渲染**,兼顧效能、可維護性、無障礙、SEO、響應式與離線支援。若需靜態圖片,請使用 **Headless Chrome (Puppeteer)** 或 **QuickChart.io API**,避免在 PHP 中手繪 GD 圖形 (除非極簡場景)。