Как я уже говорил в моей предыдущей статье, PHP и WMI — копаясь в Windows с PHP , мы живем в мире, где разработчики PHP время от времени сталкиваются с операционной системой Windows. WMI (интерфейс управления Windows) является одним из таких случаев, а Microsoft Office Interop — другим, еще более важным и более часто используемым.
В этой статье мы увидим простую интеграцию между Word и PHP: для создания документа Microsoft Word на основе входных данных в форме HTML с использованием PHP (и его расширения Interop).
Препараты
Во-первых, убедитесь, что на вашем компьютере для разработки Windows установлена типичная среда WAMP. Поскольку Interop — это просто функция Windows, нам нужно будет разместить Apache и PHP под Windows. В этом случае я использую EasyPHP 14.1 , который довольно прост в установке и настройке.
Далее нам нужно будет установить Microsoft Office. Его версия не так критична. Я использую Office 2013 Pro, но любая версия Office более поздняя, чем 2007, должна работать.
Затем мы должны убедиться, что библиотеки для разработки приложения Interop (называемые PIA, Primary Interop Assemblies) установлены. Чтобы убедиться в этом, мы можем открыть проводник Windows и перейти к: <Windows Directory>\assembly
и мы увидим несколько установленных PIA:
Мы видим запись Microsoft.Office.Interop.Word
(подчеркнута на снимке). Это будет PIA, которую мы используем в этой демонстрации. Пожалуйста, обратите особое внимание на «Имя сборки», «Версия» и «Токен открытого ключа». Они должны быть использованы в наших сценариях PHP очень скоро.
В этом каталоге мы также видим другие PIA (включая все семейство Office), доступные для программирования (не только для PHP, но и для VB.net, C # и т. Д.)
Если список PIA не включает в себя весь пакет Microsoft.Office.Interop
, мы либо переустановим наш Office, но и включим функции PIA; или мы должны вручную загрузить пакет от Microsoft и установить его. Пожалуйста, обратитесь к этой странице MSDN для получения подробных инструкций.
ПРИМЕЧАНИЕ. Для загрузки и установки доступна только распространяемая версия Microsoft Office 2010 PIA. Версия PIA в этом пакете — 14.0.0. Версия 15 поставляется только с установкой Office 2013.
Наконец, мы должны включить расширение PHP php_com_dotnet.dll
в файле php.ini
и перезапустить сервер.
Теперь мы можем перейти к программированию.
HTML-форма
Поскольку основное внимание в этой демонстрации уделяется серверной обработке, мы создадим простой интерфейс с простой формой HTML, который выглядит как на рисунке ниже:
У нас есть текстовое поле для «Имя», группа переключателей для «Пол», контроль диапазона для «Возраст» и текстовое поле для «Сообщение»; и, наконец, конечно, кнопка «Отправить».
Сохраните этот файл как «index.html» в каталоге в корневом каталоге виртуального хоста, чтобы мы могли получить к нему доступ с помощью URI, например 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 ('Ymd 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; }
-<?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 ('Ymd 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
— printdate
мы обсудим, зачем нам это нужно — мы натолкнулись на эти четыре критические строки:
$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;
Манипулирование COM в PHP требует создания экземпляра « класса » внутри « сборки ». В нашем случае мы должны работать с Word. Если мы рассмотрим первый скриншот, который мы показали, мы сможем построить полную подпись Word PIA:
- «Имя», «Версия», «Токен открытого ключа» — все они взяты из информации, отображаемой при просмотре «
c:\Windows\assembly
». - «Культура» всегда
neutrual
.
Класс, который мы должны вызвать, — это всегда имя сборки плюс « .ApplicationClass
«.
С этими двумя параметрами мы сможем создать экземпляр объекта Word.
Этот объект может оставаться на заднем плане, или мы можем вывести его на передний план, установив для его атрибута visible
значение true
.
Затем мы открываем документ для обработки и назначаем экземпляр «документа» переменной $d
.
В этом документе для создания контента на основе входных данных из формы HTML у нас есть несколько вариантов.
Самый неблагоприятный способ — это жестко закодировать все содержимое в PHP и затем вывести его в документ Word. Я настоятельно не одобряю это по следующим причинам:
- Там не будет гибкости. Любое изменение в выводе потребует модификации скрипта PHP.
- Это нарушает разделение между контролем и представлением.
- Это значительно увеличит количество строк кода, если мы будем применять стили к содержимому документа (выравнивание, шрифт, стиль и т. Д.). Программно меняющиеся стили слишком громоздки.
Другой способ — выполнить «поиск-замену». PHP имеет сильные встроенные возможности для этого. Мы можем создать документ Word, поместив некоторые специальные разделители вокруг содержимого заполнителя, которые должны быть заменены. Например, мы можем создать документ, содержащий что-то вроде этого:
{{name}}
а в PHP мы можем просто заменить это значением «Name», которое мы получили из отправки формы.
Это просто и позволяет избежать всех недостатков в первом варианте. Нам просто нужно найти правильный разделитель, и в этом случае мы больше похожи на рендеринг шаблона, за исключением того, что используемый шаблон теперь является документом Word.
Третий вариант — моя рекомендация и является продвинутой темой в Word. Мы будем использовать поля для представления заполнителей, а в нашем PHP-коде мы будем напрямую обновлять поля соответствующими значениями формы.
Этот подход является гибким, быстрым и соответствует лучшим практикам Word. Это также позволяет избежать полнотекстового поиска в документах, что способствует повышению производительности. Обратите внимание, что эта опция также имеет свои недостатки.
Word с момента своего появления никогда не поддерживал именованные индексы для полей. Несмотря на то, что мы предоставили имя для полей, которые мы создали в документе Word, мы все равно должны использовать числовые индексы для доступа к каждому полю. Это также объясняет, почему мы должны использовать выделенную функцию ( setupfields
) для ручного отображения между индексом поля и именем поля формы.
Чтобы узнать, как вставить поля в документ Word (нажмите здесь, чтобы получить готовую версию), ознакомьтесь с соответствующими разделами справки Word и руководствами. Для этой демонстрации у нас есть документ с 5 полями MERGEFIELD
. Кроме того, мы поместили документ в ту же директорию, что и скрипт PHP, для удобства доступа.
Обратите внимание, что поле printdate
не имеет соответствующего поля формы. Вот почему мы добавили фиктивный ключ printdate
в массив $inputs
. Без этого скрипт все еще может работать, но будет printdate
что индекс printdate
не представлен в массиве $inputs
.
После обновления полей со значениями формы мы распечатаем документ, используя:
$d->PrintOut();
Метод PrintOut
имеет несколько необязательных параметров, и мы используем его простейшую форму. Это напечатает одну копию на принтер по умолчанию, подключенный к нашей машине Windows.
Мы также можем использовать PrintPreview
чтобы посмотреть на вывод, прежде чем мы решим напечатать документ. В чисто автоматизированной среде мы, конечно, будем использовать PrintOut
.
Нам нужно подождать несколько секунд, прежде чем мы выйдем из приложения Word, потому что задание на печать требует некоторого времени для полной буферизации. Без delay(3)
, $w->Quit
запускается немедленно, и задание на печать также уничтожается.
Наконец, мы вызываем $w->Quit(false)
чтобы закрыть приложение Word, вызываемое нашим PHP-скриптом. Единственный предоставленный параметр — указать, хотим ли мы сохранить изменения перед выходом. Мы внесли изменения в документ, но на самом деле мы не хотим их сохранять, потому что мы хотим сохранить чистый шаблон для ввода других пользователей.
После того как мы завершим код, мы можем загрузить страницу формы, ввести некоторые значения и отправить форму. На рисунках ниже показаны выходные данные сценария 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.
Самое главное, что официальная документация Microsoft по Office PIA , особенно документация по пространству имен для каждого приложения Office, всегда является наиболее подробным справочным материалом. Чаще всего используются три приложения:
- 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
- PowerPoint 2013: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.powerpoint(v=office.15).aspx
Вывод
В этой статье мы продемонстрировали, как заполнять документ Word, используя библиотеки PHP COM и возможности взаимодействия с Microsoft Office.
Windows и Office широко используются в повседневной жизни. Знание возможностей Office / Windows и PHP будет важно для любого программиста на PHP + Windows.
С расширением COM в PHP открывается возможность освоить эту комбинацию.
Если вы заинтересованы в этой области программирования, пожалуйста, оставьте свои комментарии, и мы рассмотрим больше статей на эту тему. Я с нетерпением жду возможности увидеть больше реальных приложений, разработанных с использованием этого подхода.