在不安裝 ffmpeg、swftools 等外部工具的受限環境(共享主機、Docker 無權限容器)中,直接用 PHP 讀取 SWF 檔頭即可獲得尺寸、版本、幀率、背景色等元資料,零依賴、極低延遲,適合上傳驗證、資產管理系統即時預覽。
FWS(未壓縮)或 CWS(Zlib 壓縮)。Nbits(座標位元數,通常 14-31)。SetBackgroundColor 標籤(Tag ID 9)中,3 字節 RGB。<?php
class FlashParser {
// 可選屬性開關
public $needBackColor = true;
public $needVersion = true;
public $needFrameRate = true;
public $needFrameCount= true;
/**
* 解析 SWF 檔案
* @param string $filePath
* @return array ['width'=>, 'height'=>, 'version'=>, 'frameRate'=>, 'frameCount'=>, 'bgColor'=>] 或 ['error'=>檔案路徑]
*/
public function parse($filePath) {
if (!file_exists($filePath)) return ['error' => $filePath];
$fp = fopen($filePath, 'rb');
$data = fread($fp, filesize($filePath));
fclose($fp);
// === 1. 檢測壓縮並解壓 ===
if ($data[0] === 'C') { // CWS
$header = substr($data, 0, 8); // 簽名+版本+長度
$body = substr($data, 8);
$body = @gzuncompress($body);
if ($body === false) return ['error' => 'Zlib decompress failed'];
$data = $header . $body;
$data[0] = 'F'; // 標記為已解壓
} elseif ($data[0] !== 'F') {
return ['error' => 'Invalid SWF signature'];
}
// === 2. 解析 RECT (Frame Size) ===
// 位元流讀取輔助
$bits = '';
for ($i = 8; $i < strlen($data); $i++) {
$bits .= str_pad(decbin(ord($data[$i])), 8, '0', STR_PAD_LEFT);
}
$pos = 0;
$nBits = bindec(substr($bits, $pos, 5)); $pos += 5;
$readCoord = function() use (&$bits, &$pos, $nBits) {
$val = bindec(substr($bits, $pos, $nBits));
$pos += $nBits;
// 有號數轉換(最高位為符號位)
if ($val >= (1 << ($nBits-1))) $val -= (1 << $nBits);
return $val;
};
$xMin = $readCoord(); $xMax = $readCoord();
$yMin = $readCoord(); $yMax = $readCoord();
$width = ($xMax - $xMin) / 20;
$height = ($yMax - $yMin) / 20;
// 位元對齊到字節邊界
$pos = (int)(($pos + 7) / 8) * 8;
$bytePos = 8 + ($pos / 8); // 轉回 byte 索引
$result = ['width' => $width, 'height' => $height];
// === 3. 版本 ===
if ($this->needVersion) {
$result['version'] = ord($data[3]);
}
// === 4. Frame Rate & Frame Count ===
// 緊接在 RECT 之後:Frame Rate (2 bytes) + Frame Count (2 bytes) 小端序
if ($bytePos + 3 < strlen($data)) {
if ($this->needFrameRate) {
$rateInt = ord($data[$bytePos]);
$rateFrac = ord($data[$bytePos+1]);
$result['frameRate'] = $rateInt + $rateFrac / 256;
}
if ($this->needFrameCount) {
$fcLow = ord($data[$bytePos+2]);
$fcHigh = ord($data[$bytePos+3]);
$result['frameCount'] = $fcLow + ($fcHigh << 8);
}
}
// === 5. 背景色(掃描 Tag 9)===
if ($this->needBackColor) {
$tagPos = $bytePos + 4; // 跳過 Frame Rate/Count
while ($tagPos + 1 < strlen($data)) {
$tagHeader = ord($data[$tagPos]) + (ord($data[$tagPos+1]) << 8);
$tagType = $tagHeader >> 6;
$tagLen = $tagHeader & 0x3F;
$tagPos += 2;
if ($tagLen === 0x3F) { // 長格式
if ($tagPos + 3 > strlen($data)) break;
$tagLen = ord($data[$tagPos]) + (ord($data[$tagPos+1])<<8) + (ord($data[$tagPos+2])<<16) + (ord($data[$tagPos+3])<<24);
$tagPos += 4;
}
if ($tagType === 9) { // SetBackgroundColor
if ($tagPos + 2 < strlen($data)) {
$r = ord($data[$tagPos]);
$g = ord($data[$tagPos+1]);
$b = ord($data[$tagPos+2]);
$result['bgColor'] = sprintf('#%02X%02X%02X', $r, $g, $b);
}
break;
}
$tagPos += $tagLen;
if ($tagPos > strlen($data)) break;
}
}
return $result;
}
}
// === 使用範例 ===
$parser = new FlashParser();
$info = $parser->parse('/path/to/movie.swf');
if (isset($info['error'])) {
echo "解析失敗:{$info['error']}";
} else {
echo "寬度:{$info['width']}px
";
echo "高度:{$info['height']}px
";
echo "版本:{$info['version']}
";
echo "幀率:{$info['frameRate']} FPS
";
echo "幀數:{$info['frameCount']}
";
echo "背景色:{$info['bgColor']}
";
}
| 方案 | 優點 | 缺點 |
|---|---|---|
| getID3() | 支援超多格式、成熟穩定、Composer 安裝 | 體積大、類庫依賴 |
| FFmpeg/ffprobe | 最權威、支援影音流、元資料完整 | 需安裝二進制、執行開銷大 |
| swftools (swfdump) | 專精 SWF、可導出矢量/動作腳本 | 停止維護、編譯麻煩 |
| 本純 PHP 類 | 零依賴、單檔、嵌入專案極簡 | 功能僅限基本屬性、大檔記憶體壓力 |
純 PHP 解析 SWF 檔頭是零依賴環境下的最佳選擇。本 FlashParser 類完整實現簽名檢測、Zlib 解壓、RECT 位元解碼、定點數幀率、標籤掃描背景色,代碼清晰、可直接嵌入專案。若需更豐富元資料(音訊、腳本、字體),請改用 getID3 或 ffprobe。