COM (Component Object Model) 是 Windows 平台的二進制組件標準。PHP COM 擴展允許 PHP 腳本實例化、調用、操作 COM 物件,實現與 Office、Windows 系統組件、第三方 ActiveX/COM 組件的互操作。
適用場景:
限制:僅限 Windows 平台(Linux 需 Wine/CrossOver,不推薦生產)。PHP 8.x 仍支援,但微軟推薦遷移至 .NET Core / REST API 架構。
; php.ini extension=com_dotnet ; PHP 7.4+ 內建,無需額外 DLL ; 可選:允許 DCOM 遠程調用 [com] com.allow_dcom = true com.autoregister_typelib = true com.autoregister_casesensitive = false com.autoregister_verbose = true
| 類/函數 | 用途 |
|---|---|
new COM($progId, $server = null, $codepage = CP_ACP) | 創建 COM 物件實例(本地或遠程) |
new DOTNET($assembly, $classname) | 實例化 .NET 組件類(需 .NET Runtime) |
variant_*() 系列 | VARIANT 類型轉換、運算、比較 |
com_event_sink($obj, $sink, $interface) | 連接事件接收器 |
com_load_typelib($typelib) | 載入類型庫常數(如 Word 常數 wdAlignParagraphCenter) |
com_print_typeinfo($obj, $dispInterface = null, $sink = false) | 輸出 COM 物件方法/屬性定義(生成 PHP 存根類) |
<?php
try {
// 1. 實例化 Word Application
$word = new COM('Word.Application') or die('無法啟動 Word');
$word->Visible = false; // 後台運行
$word->DisplayAlerts = false; // 禁止彈窗
// 2. 創建新文檔
$doc = $word->Documents->Add();
// 3. 寫入內容
$selection = $word->Selection;
$selection->TypeText('PHP COM 自動生成報告');
$selection->TypeParagraph();
$selection->TypeText('生成時間: ' . date('Y-m-d H:i:s'));
$selection->TypeParagraph();
$selection->TypeText('此文檔由 PHP COM 擴展自動創建。');
// 4. 格式化
$range = $doc->Range(0, 0);
$range->InsertAfter("\n\n表格示例:");
$table = $doc->Tables->Add($range, 3, 3);
$table->Borders->Enable = true;
$headers = ['欄位', '類型', '說明'];
foreach ($headers as $i => $h) {
$table->Cell(1, $i+1)->Range->Text = $h;
}
$data = [
['id', 'int', '主鍵'],
['name', 'varchar(50)', '用戶名'],
['email', 'varchar(100)', '郵箱'],
];
foreach ($data as $rowIdx => $row) {
foreach ($row as $colIdx => $cell) {
$table->Cell($rowIdx+2, $colIdx+1)->Range->Text = $cell;
}
}
// 5. 保存並關閉
$savePath = 'C:/temp/php_com_report.docx';
$doc->SaveAs($savePath);
$doc->Close();
$word->Quit();
// 6. 釋放資源(關鍵!)
$table = $range = $selection = $doc = $word = null;
gc_collect_cycles();
echo "文檔生成成功: $savePath";
} catch (com_exception $e) {
echo "COM 錯誤: " . $e->getMessage() . " (Code: {$e->getCode()})";
// 確保進程退出
if (isset($word)) { $word->Quit(); $word = null; }
}
?>
<?php
$excel = new COM('Excel.Application') or die('無法啟動 Excel');
$excel->Visible = false;
$excel->DisplayAlerts = false;
$wb = $excel->Workbooks->Add();
$ws = $wb->ActiveSheet;
$ws->Name = '銷售數據';
// 寫入標題
$headers = ['日期', '產品', '數量', '單價', '金額'];
foreach ($headers as $i => $h) {
$ws->Cells(1, $i+1)->Value = $h;
$ws->Cells(1, $i+1)->Font->Bold = true;
}
// 寫入數據
$data = [
['2024-01-15', '筆記本', 10, 5999, 59990],
['2024-01-16', '滑鼠', 50, 199, 9950],
['2024-01-17', '鍵盤', 30, 499, 14970],
];
foreach ($data as $rowIdx => $row) {
foreach ($row as $colIdx => $val) {
$ws->Cells($rowIdx+2, $colIdx+1)->Value = $val;
}
}
// 公式
$ws->Cells(5, 5)->Formula = '=SUM(E2:E4)';
$ws->Cells(5, 5)->Font->Bold = true;
// 自動調整列寬
$ws->Columns->AutoFit();
$savePath = 'C:/temp/sales_report.xlsx';
$wb->SaveAs($savePath);
$wb->Close(false);
$excel->Quit();
$ws = $wb = $excel = null;
gc_collect_cycles();
echo "Excel 生成: $savePath";
?>
gc_collect_cycles(),否則 Word/Excel 進程殘留com_exception,finally 中確保 Quit()Visible = false、DisplayAlerts = false 避免桌面互動阻塞$_SESSION['word'] = $word 會失敗(資源不可序列化)$range->Value = $dataArray,比逐 Cell 快 100 倍<?php
// 遠程服務器需配置 DCOM 權限:dcomcnfg -> 元件服務 -> 電腦 -> 我的電腦 -> 屬性 -> 預設屬性 -> 啟用分散式 COM
// 目標應用:dcomcnfg -> 元件服務 -> 電腦 -> 我的電腦 -> DCOM 配置 -> Microsoft Word -> 屬性 -> 身分識別/安全性
$remoteServer = '192.168.1.100';
$word = new COM('Word.Application', $remoteServer) or die("無法連接遠程 Word: $remoteServer");
// 後續操作同本地
?>
<?php
class IEEventSink {
public function DocumentComplete($pDisp, &$url) {
echo "頁面加載完成: $url\n";
}
public function OnQuit() {
echo "IE 關閉\n";
}
}
$ie = new COM('InternetExplorer.Application');
$ie->Visible = true;
$ie->Navigate('https://example.com');
$sink = new IEEventSink();
com_event_sink($ie, $sink, 'DWebBrowserEvents2');
// 等待事件(簡單輪詢,生產需更完善機制)
for ($i = 0; $i < 30; $i++) {
com_message_pump(1000); // 處理 COM 消息泵 1 秒
if (!$ie->Busy) break;
}
$ie->Quit();
?>
| 錯誤 | 原因 | 解決 |
|---|---|---|
| Class not registered / 無法創建 COM 物件 | ProgID 錯誤、組件未註冊、32/64 位不匹配 | 確認 ProgID (Word.Application, Excel.Application),註冊組件 regsvr32,PHP/Apache 與 Office 位數一致 |
| DCOM 拒絕存取 / 80070005 | DCOM 權限未配置 | dcomcnfg 配置啟動/存取權限,添加 IIS_IUSRS/網絡服務帳戶 |
| Variant type not supported | PHP 不支援該 VARIANT 類型(如多維陣列、某些自定義結構) | 轉換為支援類型(一維陣列、標量),或在 COM 端封裝轉換方法 |
| PHP 進程崩潰 / 500 錯誤無日誌 | COM 物件未正確釋放導致記憶體洩漏/死鎖 | 嚴格 null 釋放、gc_collect_cycles、考慮獨立 Worker 進程隔離 |
| 中文亂碼 | 代碼頁不匹配 | 實例化時指定代碼頁:new COM('Word.Application', null, CP_UTF8) |
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| PHP COM | 直接調用、功能完整、無額外服務 | 僅 Windows、資源重、進程殘留風險、難容器化 | 遷移舊系統、內網 Windows 服務器、低併發 |
| LibreOffice Headless + unoconv | 跨平台、開源、容器友好 | 格式兼容性不如 Office、轉換較慢 | Linux 服務器、文檔轉換、批量處理 |
| PhpSpreadsheet / PhpWord | 純 PHP、無 Office 依賴、跨平台 | 功能子集、大檔案記憶體壓力、複雜排版受限 | 報表導出、模板填充、中小檔案 |
| Microsoft Graph API / Office 365 API | 雲原生、官方支援、高併發、跨平台 | 需訂閱、網路延遲、學習曲線 | 企業級、雲部署、協作場景 |
| Node.js + officegen / python-docx 微服務 | 技術棧靈活、易容器化、語言生態豐富 | 額外服務維護、序列化開銷 | 微服務架構、多語言團隊 |
建議:新專案優先選擇純 PHP 庫或雲 API。僅在 Windows 內網、低併發、需完整 Office 功能(如郵件合併、複雜圖表、VBA 宏)時使用 COM。生產環境務必:獨立 Worker 池處理 COM 任務、嚴格超時/重試/熔斷、監控殘留進程、定期回收 Worker。