在Windows系统下使用PHP生成Word文档的教程


Posted in PHP onJuly 03, 2015

准备工作

首先,请确保在你的Windows系统中已经安装并配置好了一个典型的WAMP环境。由于Interop纯粹是一个Windows的特性,我们将在Windows平台下搭建Apache和PHP。在这个实例中,我使用了EasyPHP 14.1,这款软件安装和配置都十分容易。

接下来,我们要安装Microsoft Office。版本不是严格要求的。我正在使用的是Office2013专业版,但是任何2007之后的Office版本都应该可以使用。

我们然后需要去确保开发Interop应用(又被称作PIA,优先交互组件)的库是安装好的。为了确保这个,我们可以打开资源管理器,然后找到<Windows目录>\assembly,我们将会看到下面安装好的PIAs分支:

在Windows系统下使用PHP生成Word文档的教程

我们可以看到一个 Microsoft.Office.Interop.Word 条目(在这个截图中有下划线)。 这就是我们在这个示例中将要使用的 PIA。请特别注意它的“名称”,“版本”和“公钥标记”。我们将要在PHP脚本中用到它们。

在这个目录中,我们还可以看到其它用于编程(不仅是PHP,还有VB.net, C#等)的PIAs(包括整个Office家族)。

如果这个列表没有包含 Microsoft.Office.Interop 的整个包,我们可以重新安装Office并且在安装中包含PIA;我们也可以手动下载安装这个包。安装的详细步骤可以查阅这个MSDN页面。

注意:只有Microsoft Office 2010 PIA Redistributable 可以被单独下载安装。这个包中的 PIA 版本是14.0.0。版本15只能通过安装Office获得。

最后,我们需要在文件 php.ini 中启用 PHP 扩展 php_com_dotnet.dll,并且重启服务器。

现在我们可以开始编程了。

HTML表单

由于该demo主要关注与后台的处理,所以我们这里就用一个简单的HTML表单做前台的展示,看起来应该是这样的:

在Windows系统下使用PHP生成Word文档的教程

我们有一个文本框用于输入“Name”,一个“Gender”的单选按钮组,一个“Age”的域值控制还有一个文本域来写“Message”,最后,还需要一个“Submit”按钮。

将该文件命名为“index.html”,保存在虚拟主机的根目录下,这样我们可以直接通过URL访问该文件,例如:http://test/test/interop

后台

后台的PHP文件是我们所要讨论的核心部分。我先将代码贴到下面,接下来在一步一步的进行解释
 

<?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;
}

在设置完用来获取表单中传过来的值的变量$inputs之后,我们要创建一个虚拟值用来存放printdate——我们稍后会讨论为何需要这个变量——现在,我们看到这4行比较关键的代码:
 

$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操纵需要在一个assembly里请求一个class的实例。在我们的案例中,我见将要操作Word。如果考虑到我们上一个截图中展示的代码,我们将能够构造出一个完整签名的Word PIA:

  •     “Name”,“Version”,“Public Key Token”是在当我们浏览“c:\Windows\assembly”时所展示的信息
  •     “Cultrue”总是neutrual的。

调用类编译后的文件后缀名为通常为ApplicationClass.

通过设置下面两个步骤,我们可以初始化一个word对象:

首先,word对象可以保存在后台或者通过将visible属性设置为true,使它在前台展示出来。

然后,我们打开将要处理的文档,把它实例化为一个$d变量。

在文档对象中,基于html表单的文本来添加文档的内容,这里可以设置一些参数。
最不好的方式是对php页面上所有内容进行硬编码,然后将它们添加到word对象中。我强烈建议不采用此种方式,原因有:

1 代码没有灵活性,php内容的任何变化都需要重新修改脚本;
2 违反了控制层、展示层的分离;
3  如果需要设置word内容的格式(对齐,字体,样式,等),这种方式大大增加了代码行数,并且以编程的方式来修改样式是非常麻烦的。

另一种方式是使用“搜索-替换”。PHP内置的这种功能非常强大。我们可以创建一个word文档,在那些需要被替换的占位内容周围放置一些分隔符。比如,我们创建一个文档包含如下内容:

{{name}}

在PHP中,我们只需使用从表单提交中获取的“Name”值来替换。这种方式避免了第一选项中的那些缺点。我们只需要找到正确的分隔符,在这个例子中,除了使用的模板是word文档,我们更像是做一个模板渲染。

第三个选项是我的建议,并是Word中的高级主题。我们将用域来表示占位符,在我们的PHP代码中,我们会直接更新了相应的表单值的字段。 

这种方法灵活,快速,符合Word的最佳实践。这也避免了文件的全文搜索,这有助于提高性能。请注意,此选项有它的缺点了。 

总之,自从首次亮相,Word从来没有支持命名索引的字段。尽管我们对于我们在Word文档中创建的字段提供了一个名字,我们还是要用数字下标来访问每个字段。这也解释了为什么我们要使用专用的功能(setupfields)做表单字段的字段索引和名之间的映射手册

学习如何在word文档中插入字段(点击这里查看一个定制好的版本),请参阅相关 Word 帮助主题和手册。对于这个demo,我们有一个具备5个MERGEFIELD字段的文档。此外,我们将文档和PHP脚本放在一个目录下,以方便获取。

请注意,printdate字段并没有一个相应的窗体字段。这就是为什么我们要在$inputs数组中添加一个假的printdate作为key。没有这个key,脚本依然可以执行,但是会有提示说明$inputs数组中不存在索引printdate。

在使用表单数据更新完字段的值之后,我们将会使用下面的命令打印文档:
 

$d->PrintOut();

PrintOut方法有几个可选参数,这里,我们使用最简单的格式。这将会给链接到我们Windows机器的默认打印机打印一份副本。

我们可以通过使用PrintPreview进行打印预览。在纯自动化的情景下,当然,我们直接使用PrintOut进行打印。

在退出word应用程序之前,我们还需要稍作等待,因为打印工作需要时间来完全退出后台。如果没有delay(3),$w->Quit将会立刻得到执行,并且打印工作立刻被终止。

最终,我们调用 $w->Quit(false) 来选择通过我们的PHP脚本调用关闭word应用程序。这里提供的唯一参数是用来指明我们是否希望在退出前保存更改。我们确实对文档进行了更改,但是我们不希望保存它们,因为我们希望能为其他用户的输入提供一份干净的模板。
 

当我们完成编码之后,我们可以加载表单页面,输入一些内容并提交表单。下面的截图展示了PHP脚本的输出,同时更新了Word文档:

在Windows系统下使用PHP生成Word文档的教程

在Windows系统下使用PHP生成Word文档的教程

提高编码速度并更好的理解PIA

PHP是一种弱类型的语言。一个COM对象是一种Object类型。在我们的PHP编码过程中,在一个对象中我们无法使用代码自动提示和完成功能,在一个Word应用,一个文档甚至一个字段中同样如此。我们不知道它有哪些特性,或者它支持哪些方法。

这将大幅度的降低我们开发的速度。为了使开发更快,首先,我建议我们在c#中开发功能应当迁移至我们的PHP编码。我推荐一款免费的C# IDE 叫做"#develop",你可以在这里下载。相比VS,我更喜欢这一款软件,因为#develop体积更小,更简洁,响应更快。

C#代码迁移至PHP一点也不吓人。先让我展示一些C#的代码:
 

Word.Application w=new Word.Application();

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的代码基础一模一样。由于C#是一种强类型语言,所以我们可以看到有些类型转换的语句,我们不得不显性的给我们的变量赋一种类型。

有了代码的类型,我们可以尽情的享受代码的自动提示和代码自动完成功能,这样我们开发的速度将有大幅度提高。

另一种可以给予我们更快速度进行PHP开发的方式是使用Word的宏命令。我们先操作一遍我们需要重复的动作,然后用一个宏将其录制下来。一个宏其实是Visual Basic,同样也可以非常容易的翻译成PHP。

最重要的是,Office PIA微软官方文档,特别是文档中对于每个Office应用的命名空间,总会是我们所需要的最想进的参考。比较常用的3个应用如下:

  •     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

结语

在这篇文章中,我们演示了如何使用PHP COM库和Microsoft Office Interop功能来倩影一个Word文档。

Windows和Office在我们的日常生活中可以说是被广泛的使用。能够知道和了解Office或者Windows的强大之处还有PHP,对于任何一个在Windows平台上进行PHP开发的程序员都是十分必要的。

使用PHP的COM扩展,掌握这一组合的大门就被打开了。

如果你对于这部分的编程比较感兴趣,请留下你的评论,我们将会考虑在这个话题上写更多的文章。我十分期待更多现实生活的应用开发能使用这种方式。

PHP 相关文章推荐
使用PHP遍历文件夹与子目录的函数代码
Sep 26 PHP
php在window iis的莫名问题的测试方法
May 14 PHP
关于查看MSSQL 数据库 用户每个表 占用的空间大小
Jun 21 PHP
ThinkPHP之foreach标签使用概述
Jun 30 PHP
phpmyadmin中禁止外网使用的方法
Nov 04 PHP
PHP实现将科学计数法转换为原始数字字符串的方法
Dec 16 PHP
php读取der格式证书乱码解决方法
Jun 22 PHP
thinkPHP中create方法与令牌验证实例浅析
Dec 08 PHP
Thinkphp和Bootstrap结合打造个性的分页样式(推荐)
Aug 01 PHP
Laravel 中使用简单的方法跟踪用户是否在线(推荐)
Oct 30 PHP
Laravel5.1 框架数据库操作DB运行原生SQL的方法分析
Jan 07 PHP
Laravel框架源码解析之反射的使用详解
May 14 PHP
深入理解PHP中的Streams工具
Jul 03 #PHP
列举PHP的Yii 2框架的开发优势
Jul 03 #PHP
PHP中异常处理的一些方法整理
Jul 03 #PHP
在PHP程序中使用Rust扩展的方法
Jul 03 #PHP
PHP整合七牛实现上传文件
Jul 03 #PHP
ThinkPHP自定义函数解决模板标签加减运算的方法
Jul 03 #PHP
5款适合PHP使用的HTML编辑器推荐
Jul 03 #PHP
You might like
用PHP将数据导入到Foxmail
2006/10/09 PHP
解析如何在PHP下载文件名中解决乱码的问题
2013/06/20 PHP
解析php根据ip查询所在地区(非常有用,赶集网就用到)
2013/07/01 PHP
php 无限级分类,超级简单的无限级分类,支持输出树状图
2014/06/29 PHP
PHP的压缩函数实现:gzencode、gzdeflate和gzcompress的区别
2016/01/27 PHP
PHP针对中英文混合字符串长度判断及截取方法示例
2017/03/31 PHP
PHP实现的堆排序算法详解
2017/08/17 PHP
JavaScript 通过模式匹配实现重载
2010/08/12 Javascript
javascript getElementsByTagName
2011/01/31 Javascript
js实现可兼容IE、FF、Chrome、Opera及Safari的音乐播放器
2015/02/11 Javascript
JavaScript创建闭包的两种方式的优劣与区别分析
2015/06/22 Javascript
JS实现带关闭功能的阿里妈妈网站顶部滑出banner工具条代码
2015/09/17 Javascript
浅谈javascript中onbeforeunload与onunload事件
2015/12/10 Javascript
Bootstrap Navbar Component实现响应式导航
2016/10/08 Javascript
深入理解Javascript中的观察者模式
2017/02/20 Javascript
深入理解es6块级作用域的使用
2019/03/28 Javascript
Django+Vue实现WebSocket连接的示例代码
2019/05/28 Javascript
[03:14]辉夜杯主赛事 12月25日每日之星
2015/12/26 DOTA
Python3.6正式版新特性预览
2016/12/15 Python
利用Python实现Windows定时关机功能
2017/03/21 Python
Python 实现使用dict 创建二维数据、DataFrame
2018/04/13 Python
Python 类的魔法属性用法实例分析
2019/11/21 Python
Python实现Canny及Hough算法代码实例解析
2020/08/06 Python
python实现自动清理重复文件
2020/08/24 Python
Django中使用Celery的方法步骤
2020/12/07 Python
python中numpy.empty()函数实例讲解
2021/02/05 Python
HTML5的表单(绝对特别强大的功能)使用示例
2013/06/20 HTML / CSS
纽约香氛品牌:NEST Fragrance
2018/10/15 全球购物
P D PAOLA法国官网:西班牙著名的珠宝首饰品牌
2020/02/15 全球购物
监察建议书范文
2014/03/12 职场文书
法人委托书范本
2014/04/04 职场文书
还款承诺书范文
2014/05/20 职场文书
关于读书的活动方案
2014/08/14 职场文书
风雨哈佛路观后感
2015/06/03 职场文书
详解Flask开发技巧之异常处理
2021/06/15 Python
Nginx报404错误的详细解决方法
2022/07/23 Servers