在Windows系統(tǒng)下使用PHP生成Word文檔的教程
準(zhǔn)備工作
首先,請(qǐng)確保在你的Windows系統(tǒng)中已經(jīng)安裝并配置好了一個(gè)典型的WAMP環(huán)境。由于Interop純粹是一個(gè)Windows的特性,我們將在Windows平臺(tái)下搭建Apache和PHP。在這個(gè)實(shí)例中,我使用了EasyPHP 14.1,這款軟件安裝和配置都十分容易。
接下來,我們要安裝Microsoft Office。版本不是嚴(yán)格要求的。我正在使用的是Office2013專業(yè)版,但是任何2007之后的Office版本都應(yīng)該可以使用。
我們?nèi)缓笮枰ゴ_保開發(fā)Interop應(yīng)用(又被稱作PIA,優(yōu)先交互組件)的庫是安裝好的。為了確保這個(gè),我們可以打開資源管理器,然后找到<Windows目錄>\assembly,我們將會(huì)看到下面安裝好的PIAs分支:
我們可以看到一個(gè) Microsoft.Office.Interop.Word 條目(在這個(gè)截圖中有下劃線)。 這就是我們?cè)谶@個(gè)示例中將要使用的 PIA。請(qǐng)?zhí)貏e注意它的“名稱”,“版本”和“公鑰標(biāo)記”。我們將要在PHP腳本中用到它們。
在這個(gè)目錄中,我們還可以看到其它用于編程(不僅是PHP,還有VB.net, C#等)的PIAs(包括整個(gè)Office家族)。
如果這個(gè)列表沒有包含 Microsoft.Office.Interop 的整個(gè)包,我們可以重新安裝Office并且在安裝中包含PIA;我們也可以手動(dòng)下載安裝這個(gè)包。安裝的詳細(xì)步驟可以查閱這個(gè)MSDN頁面。
注意:只有Microsoft Office 2010 PIA Redistributable 可以被單獨(dú)下載安裝。這個(gè)包中的 PIA 版本是14.0.0。版本15只能通過安裝Office獲得。
最后,我們需要在文件 php.ini 中啟用 PHP 擴(kuò)展 php_com_dotnet.dll,并且重啟服務(wù)器。
現(xiàn)在我們可以開始編程了。
HTML表單
由于該demo主要關(guān)注與后臺(tái)的處理,所以我們這里就用一個(gè)簡單的HTML表單做前臺(tái)的展示,看起來應(yīng)該是這樣的:
我們有一個(gè)文本框用于輸入“Name”,一個(gè)“Gender”的單選按鈕組,一個(gè)“Age”的域值控制還有一個(gè)文本域來寫“Message”,最后,還需要一個(gè)“Submit”按鈕。
將該文件命名為“index.html”,保存在虛擬主機(jī)的根目錄下,這樣我們可以直接通過URL訪問該文件,例如:http://test/test/interop
后臺(tái)
后臺(tái)的PHP文件是我們所要討論的核心部分。我先將代碼貼到下面,接下來在一步一步的進(jìn)行解釋
<?php $inputs = $_POST; $inputs['printdate']=''; // A dummy value to avoid a PHP notice as we don't have "printdate" in the POST variables. $assembly = 'Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'; $class = 'Microsoft.Office.Interop.Word.ApplicationClass'; $w = new DOTNET($assembly, $class); $w->visible = true; $fn = __DIR__ . '\\template.docx'; $d = $w->Documents->Open($fn); echo "Document opened.<br><hr>"; $flds = $d->Fields; $count = $flds->Count; echo "There are $count fields in this document.<br>"; echo "<ul>"; $mapping = setupfields(); foreach ($flds as $index => $f) { $f->Select(); $key = $mapping[$index]; $value = $inputs[$key]; if ($key == 'gender') { if ($value == 'm') $value = 'Mr.'; else $value = 'Ms.'; } if($key=='printdate') $value= date ('Y-m-d H:i:s'); $w->Selection->TypeText($value); echo "<li>Mappig field $index: $key with value $value</li>"; } echo "</ul>"; echo "Mapping done!<br><hr>"; echo "Printing. Please wait...<br>"; $d->PrintOut(); sleep(3); echo "Done!"; $w->Quit(false); $w=null; function setupfields() { $mapping = array(); $mapping[0] = 'gender'; $mapping[1] = 'name'; $mapping[2] = 'age'; $mapping[3] = 'msg'; $mapping[4] = 'printdate'; return $mapping; }
在設(shè)置完用來獲取表單中傳過來的值的變量$inputs之后,我們要?jiǎng)?chuàng)建一個(gè)虛擬值用來存放printdate——我們稍后會(huì)討論為何需要這個(gè)變量——現(xiàn)在,我們看到這4行比較關(guān)鍵的代碼:
$assembly = 'Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'; $class = 'Microsoft.Office.Interop.Word.ApplicationClass'; $w = new DOTNET($assembly, $class); $w->visible = true;
在PHP中的COM操縱需要在一個(gè)assembly里請(qǐng)求一個(gè)class的實(shí)例。在我們的案例中,我見將要操作Word。如果考慮到我們上一個(gè)截圖中展示的代碼,我們將能夠構(gòu)造出一個(gè)完整簽名的Word PIA:
- “Name”,“Version”,“Public Key Token”是在當(dāng)我們?yōu)g覽“c:\Windows\assembly”時(shí)所展示的信息
- “Cultrue”總是neutrual的。
調(diào)用類編譯后的文件后綴名為通常為ApplicationClass.
通過設(shè)置下面兩個(gè)步驟,我們可以初始化一個(gè)word對(duì)象:
首先,word對(duì)象可以保存在后臺(tái)或者通過將visible屬性設(shè)置為true,使它在前臺(tái)展示出來。
然后,我們打開將要處理的文檔,把它實(shí)例化為一個(gè)$d變量。
在文檔對(duì)象中,基于html表單的文本來添加文檔的內(nèi)容,這里可以設(shè)置一些參數(shù)。
最不好的方式是對(duì)php頁面上所有內(nèi)容進(jìn)行硬編碼,然后將它們添加到word對(duì)象中。我強(qiáng)烈建議不采用此種方式,原因有:
1 代碼沒有靈活性,php內(nèi)容的任何變化都需要重新修改腳本;
2 違反了控制層、展示層的分離;
3 如果需要設(shè)置word內(nèi)容的格式(對(duì)齊,字體,樣式,等),這種方式大大增加了代碼行數(shù),并且以編程的方式來修改樣式是非常麻煩的。
另一種方式是使用“搜索-替換”。PHP內(nèi)置的這種功能非常強(qiáng)大。我們可以創(chuàng)建一個(gè)word文檔,在那些需要被替換的占位內(nèi)容周圍放置一些分隔符。比如,我們創(chuàng)建一個(gè)文檔包含如下內(nèi)容:
{{name}}
在PHP中,我們只需使用從表單提交中獲取的“Name”值來替換。這種方式避免了第一選項(xiàng)中的那些缺點(diǎn)。我們只需要找到正確的分隔符,在這個(gè)例子中,除了使用的模板是word文檔,我們更像是做一個(gè)模板渲染。
第三個(gè)選項(xiàng)是我的建議,并是Word中的高級(jí)主題。我們將用域來表示占位符,在我們的PHP代碼中,我們會(huì)直接更新了相應(yīng)的表單值的字段。
這種方法靈活,快速,符合Word的最佳實(shí)踐。這也避免了文件的全文搜索,這有助于提高性能。請(qǐng)注意,此選項(xiàng)有它的缺點(diǎn)了。
總之,自從首次亮相,Word從來沒有支持命名索引的字段。盡管我們對(duì)于我們?cè)赪ord文檔中創(chuàng)建的字段提供了一個(gè)名字,我們還是要用數(shù)字下標(biāo)來訪問每個(gè)字段。這也解釋了為什么我們要使用專用的功能(setupfields)做表單字段的字段索引和名之間的映射手冊(cè)
學(xué)習(xí)如何在word文檔中插入字段(點(diǎn)擊這里查看一個(gè)定制好的版本),請(qǐng)參閱相關(guān) Word 幫助主題和手冊(cè)。對(duì)于這個(gè)demo,我們有一個(gè)具備5個(gè)MERGEFIELD字段的文檔。此外,我們將文檔和PHP腳本放在一個(gè)目錄下,以方便獲取。
請(qǐng)注意,printdate字段并沒有一個(gè)相應(yīng)的窗體字段。這就是為什么我們要在$inputs數(shù)組中添加一個(gè)假的printdate作為key。沒有這個(gè)key,腳本依然可以執(zhí)行,但是會(huì)有提示說明$inputs數(shù)組中不存在索引printdate。
在使用表單數(shù)據(jù)更新完字段的值之后,我們將會(huì)使用下面的命令打印文檔:
$d->PrintOut();
PrintOut方法有幾個(gè)可選參數(shù),這里,我們使用最簡單的格式。這將會(huì)給鏈接到我們Windows機(jī)器的默認(rèn)打印機(jī)打印一份副本。
我們可以通過使用PrintPreview進(jìn)行打印預(yù)覽。在純自動(dòng)化的情景下,當(dāng)然,我們直接使用PrintOut進(jìn)行打印。
在退出word應(yīng)用程序之前,我們還需要稍作等待,因?yàn)榇蛴」ぷ餍枰獣r(shí)間來完全退出后臺(tái)。如果沒有delay(3),$w->Quit將會(huì)立刻得到執(zhí)行,并且打印工作立刻被終止。
最終,我們調(diào)用 $w->Quit(false) 來選擇通過我們的PHP腳本調(diào)用關(guān)閉word應(yīng)用程序。這里提供的唯一參數(shù)是用來指明我們是否希望在退出前保存更改。我們確實(shí)對(duì)文檔進(jìn)行了更改,但是我們不希望保存它們,因?yàn)槲覀兿M転槠渌脩舻妮斎胩峁┮环莞蓛舻哪0濉?br />
當(dāng)我們完成編碼之后,我們可以加載表單頁面,輸入一些內(nèi)容并提交表單。下面的截圖展示了PHP腳本的輸出,同時(shí)更新了Word文檔:
提高編碼速度并更好的理解PIA
PHP是一種弱類型的語言。一個(gè)COM對(duì)象是一種Object類型。在我們的PHP編碼過程中,在一個(gè)對(duì)象中我們無法使用代碼自動(dòng)提示和完成功能,在一個(gè)Word應(yīng)用,一個(gè)文檔甚至一個(gè)字段中同樣如此。我們不知道它有哪些特性,或者它支持哪些方法。
這將大幅度的降低我們開發(fā)的速度。為了使開發(fā)更快,首先,我建議我們?cè)赾#中開發(fā)功能應(yīng)當(dāng)遷移至我們的PHP編碼。我推薦一款免費(fèi)的C# IDE 叫做"#develop",你可以在這里下載。相比VS,我更喜歡這一款軟件,因?yàn)?develop體積更小,更簡潔,響應(yīng)更快。
C#代碼遷移至PHP一點(diǎn)也不嚇人。先讓我展示一些C#的代碼:
w.Visible=true;
String path=Application.StartupPath+"\\template.docx";
Word.Document d=w.Documents.Open(path) as Word.Document;
Word.Fields flds=d.Fields;
int len=flds.Count;
foreach (Word.Field f in flds)
{
f.Select();
int i=f.Index;
w.Selection.TypeText("...");
}
我們可以看到,C#的代碼和我們之前展示的PHP的代碼基礎(chǔ)一模一樣。由于C#是一種強(qiáng)類型語言,所以我們可以看到有些類型轉(zhuǎn)換的語句,我們不得不顯性的給我們的變量賦一種類型。
有了代碼的類型,我們可以盡情的享受代碼的自動(dòng)提示和代碼自動(dòng)完成功能,這樣我們開發(fā)的速度將有大幅度提高。
另一種可以給予我們更快速度進(jìn)行PHP開發(fā)的方式是使用Word的宏命令。我們先操作一遍我們需要重復(fù)的動(dòng)作,然后用一個(gè)宏將其錄制下來。一個(gè)宏其實(shí)是Visual Basic,同樣也可以非常容易的翻譯成PHP。
最重要的是,Office PIA微軟官方文檔,特別是文檔中對(duì)于每個(gè)Office應(yīng)用的命名空間,總會(huì)是我們所需要的最想進(jìn)的參考。比較常用的3個(gè)應(yīng)用如下:
- Excel 2013:http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel(v=office.15).aspx
- Word 2013:http://msdn.microsoft.com/en-us/library/microsoft.office.interop.word(v=office.15).aspx
- PowerPoint2013:http://msdn.microsoft.com/en-us/library/microsoft.office.interop.powerpoint(v=office.15).aspx
結(jié)語
在這篇文章中,我們演示了如何使用PHP COM庫和Microsoft Office Interop功能來倩影一個(gè)Word文檔。
Windows和Office在我們的日常生活中可以說是被廣泛的使用。能夠知道和了解Office或者Windows的強(qiáng)大之處還有PHP,對(duì)于任何一個(gè)在Windows平臺(tái)上進(jìn)行PHP開發(fā)的程序員都是十分必要的。
使用PHP的COM擴(kuò)展,掌握這一組合的大門就被打開了。
如果你對(duì)于這部分的編程比較感興趣,請(qǐng)留下你的評(píng)論,我們將會(huì)考慮在這個(gè)話題上寫更多的文章。我十分期待更多現(xiàn)實(shí)生活的應(yīng)用開發(fā)能使用這種方式。
- phpword插件導(dǎo)出word文件時(shí)中文亂碼問題處理方案
- PHP生成word文檔的三種實(shí)現(xiàn)方式
- php在程序中將網(wǎng)頁生成word文檔并提供下載的代碼
- 在PHP中讀取和寫入WORD文檔的代碼
- 使用PHP導(dǎo)出Word文檔的原理和實(shí)例
- PHP中將網(wǎng)頁導(dǎo)出為Word文檔的代碼
- php導(dǎo)出word文檔與excel電子表格的簡單示例代碼
- PHP讀取word文檔的方法分析【基于COM組件】
- php通過baihui網(wǎng)API實(shí)現(xiàn)讀取word文檔并展示
- PHP創(chuàng)建word文檔的方法(平臺(tái)無關(guān))
- 使用PHPWord生成word文檔的方法詳解
相關(guān)文章
記錄PHP錯(cuò)誤日志 display_errors與log_errors的區(qū)別
錯(cuò)誤回顯,一般常用語開發(fā)模式,但是很多應(yīng)用在正式環(huán)境中也忘記了關(guān)閉此選項(xiàng)。錯(cuò)誤回顯可以暴露出非常多的敏感信息,為攻擊者下一步攻擊提供便利。推薦關(guān)閉此選項(xiàng)2012-10-10php獲取URL中帶#號(hào)等特殊符號(hào)參數(shù)的解決方法
這篇文章主要介紹了php獲取URL中帶#號(hào)等特殊符號(hào)參數(shù)的解決方法,本文使用JS中的escape函數(shù)編碼后傳遞解決這個(gè)問題,需要的朋友可以參考下2014-09-09PHP中curl_setopt函數(shù)用法實(shí)例分析
這篇文章主要介紹了PHP中curl_setopt函數(shù)用法,以實(shí)例形式分析了curl_setopt函數(shù)的功能、定義、用途及相關(guān)的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04thinkphp項(xiàng)目部署到Linux服務(wù)器上報(bào)錯(cuò)“模板不存在”如何解決
一個(gè)項(xiàng)目部署到Linux服務(wù)器上去的時(shí)候,發(fā)現(xiàn)某些模板竟然會(huì)報(bào)錯(cuò)說“模板不存在:/Application/Admin/....”,這篇文章就是介紹了thinkphp項(xiàng)目部署到Linux服務(wù)器上報(bào)錯(cuò)“模板不存在”的解決方法,感興趣的小伙伴們可以參考一下2016-04-04