SWF (Small Web Format) 以 Header 開頭:
FWS (未壓縮) 或 CWS (Zlib 壓縮)RECT 結構:Nbits (5 bits) + Xmin, Xmax, Ymin, Ymax (各 Nbits) → 畫面尺寸(單位:twips, 1/20 像素)FrameRate (Fixed8, 2 字節) 與 FrameCount (UI16, 2 字節)<?php
class SwfInfoParser
{
public function parse(string $filePath): ?array
{
if (!file_exists($filePath)) return null;
$data = file_get_contents($filePath);
if (strlen($data) < 8) return null;
$signature = substr($data, 0, 3);
$compressed = ($signature === 'CWS');
$version = ord($data[3]);
$fileLength = unpack('V', substr($data, 4, 4))[1];
// 解壓縮(若為 CWS)
if ($compressed) {
$data = substr($data, 0, 8) . gzuncompress(substr($data, 8));
}
// 解析 RECT (畫面尺寸)
$pos = 8;
$bits = $this->readBits($data, $pos, 5); // Nbits
$pos += 5;
$xmin = $this->readSBits($data, $pos, $bits); $pos += $bits;
$xmax = $this->readSBits($data, $pos, $bits); $pos += $bits;
$ymin = $this->readSBits($data, $pos, $bits); $pos += $bits;
$ymax = $this->readSBits($data, $pos, $bits); $pos += $bits;
$widthTwips = $xmax - $xmin;
$heightTwips = $ymax - $ymin;
$width = $widthTwips / 20;
$height = $heightTwips / 20;
// FrameRate (Fixed8: 8.8 定點數)
$frameRateRaw = unpack('n', substr($data, $pos, 2))[1];
$frameRate = $frameRateRaw / 256;
$pos += 2;
// FrameCount (UI16)
$frameCount = unpack('v', substr($data, $pos, 2))[1];
// 背景色:SWF 標準 Header 無背景色,需解析第一個 Tag (SetBackgroundColor, tag code 9)
$bgColor = $this->parseBackgroundColor($data, $pos);
return [
'version' => $version,
'compressed' => $compressed,
'file_length' => $fileLength,
'width' => $width,
'height' => $height,
'frame_rate' => $frameRate,
'frame_count' => $frameCount,
'background_color' => $bgColor, // 格式 #RRGGBB 或 null
];
}
private function readBits(string &$data, int &$pos, int $count): int
{
$val = 0;
for ($i = 0; $i < $count; $i++) {
$bytePos = $pos >> 3;
$bitPos = 7 - ($pos & 7);
$val = ($val << 1) | (($data[$bytePos] >> $bitPos) & 1);
$pos++;
}
return $val;
}
private function readSBits(string &$data, int &$pos, int $count): int
{
$val = $this->readBits($data, $pos, $count);
// 有號數擴展
if ($val & (1 << ($count - 1))) {
$val -= (1 << $count);
}
return $val;
}
private function parseBackgroundColor(string $data, int $startPos): ?string
{
// 簡易掃描 Tag 9 (SetBackgroundColor)
$pos = $startPos;
$len = strlen($data);
while ($pos + 2 < $len) {
$tagHeader = unpack('n', substr($data, $pos, 2))[1];
$tagCode = $tagHeader >> 6;
$tagLength = $tagHeader & 0x3F;
$pos += 2;
if ($tagLength === 0x3F) { // 長格式
if ($pos + 4 > $len) break;
$tagLength = unpack('V', substr($data, $pos, 4))[1];
$pos += 4;
}
if ($tagCode === 9 && $tagLength >= 3) { // SetBackgroundColor
$rgb = unpack('C3', substr($data, $pos, 3));
return sprintf('#%02X%02X%02X', $rgb[1], $rgb[2], $rgb[3]);
}
$pos += $tagLength;
}
return null;
}
}
<?php
$parser = new SwfInfoParser();
$info = $parser->parse('demo.swf');
if ($info) {
echo "版本:{$info['version']}\n";
echo "尺寸:{$info['width']} x {$info['height']} px\n";
echo "幀率:{$info['frame_rate']} fps\n";
echo "總幀數:{$info['frame_count']}\n";
echo "背景色:{$info['background_color'] ?? '無'}\n";
} else {
echo "解析失敗或檔案不存在";
}
gzuncompress() 解壓尾部資料,再解析結構。unpack('v'/'V')。readBits 實現)。composer require james-heinrich/getid3exec('swfdump -d file.swf') 取得結構化輸出。SWF 格式規格公開且結構固定,PHP 純代碼即可完整解析 Header 與關鍵 Tag。上方 SwfInfoParser 類提供輕量、無依賴的解決方案,適合嵌入上傳驗證、預覽生成等場景。