В данной статье хочу показать небольшой пример работы с библиотекой PHPWord. Логика работы примерно следующая: есть элемент инфоблока с некоторыми полями, в том числе, привязка к пользователю, где хранятся все его данные. При создании или обновлении элемента на лету создается word документ, в котором указывается фио сотрудника или возможно какие-то другие данные. Этот документ сохраняется в базу и присваивается этому же элементу как значение свойства типа «файл». После чего этот файл можно скачать.
Для подключения данной библиотеки, нам понадобиться composer. Для Bitrix composer подключается довольно просто. В папке /bitrix, есть файл «composer-bx.json». Переименовываем его в composer.json и в консоле,перейдя в эту папку, запускаем установщик composer`а
composer install
После чего в данном разделе появляется папка «vendor», куда и будут установлены необходимые библиотеки. Добавим зависимость «"phpoffice/phpword": "v0.16."» и установим все пакеты. Актуальную версию смотрите на официальном сайте или github разработчиков. Для подключения автозагрузки классов* в init.php пропишем следующую конструкцию:
if($_SERVER["DOCUMENT_ROOT"].'/bitrix/vendor/autoload.php') {
include_once ($_SERVER["DOCUMENT_ROOT"].'/bitrix/vendor/autoload.php');
}
Также, если нужно, можем использовать автозагрузчик от Bitrix, в котором можем использовать свои собственные классы.
CModule::AddAutoloadClasses(
«», // не указываем имя модуля
array(
// ключ - имя класса, значение - путь относительно корня сайта к файлу с классом
'MyHelper' => '/local/php_interface/MyHelper/helpers.php',
)
);
AddEventHandler("iblock", "OnAfterIBlockElementAdd", 'MyHelper::DocCreator');
В данном примере мы подключили класс MyHelper и повешали событие, которое после добавления элемента инфоблока вызовит статический метод DocCreator этого класса.
В файле /local/php_interface/MyHelper/helpers.php
class DocChange{
public static function DocCreator(&$arFields) {
if($arFields["PROPERTY_VALUES"]) { // условие на инфоблок или что угодно
self::CreateAfterDoc($arFields);
}
}
}
В публичном статическом методе проверяем, что обновляется элемент из нужного инфоблока и данные не пустые. Далее вызываем обычный метод для формирования самого .doc документа.
public function CreateAfterDoc($incomArray) {
$phpWord = new \PhpOffice\PhpWord\PhpWord(); // создаем экземпляр класса
// параметры которые будут указаны при создании документа
$phpWord->setDefaultFontName("Times New Roman"); // устанавливаем шрифт
$phpWord->setDefaultFontSize(12); // устанавливаем шрифт
$phpWord->getSettings()->setThemeFontLang(new \PhpOffice\PhpWord\Style\Language(\PhpOffice\PhpWord\Style\Language::EN_GB)); // устанавливаем язык документа
$phpWord->getSettings()->setHideSpellingErrors(true);
// параметры документа
$properties = $phpWord->getDocInfo();
$properties->setCreator('');
$properties->setCompany('MyCompany ');
$properties->setTitle('My title');
$properties->setDescription('My description');
$properties->setCategory('My category');
$properties->setLastModifiedBy('My name');
$properties->setCreated(mktime(0, 0, 0, 3, 12, 2020));
$properties->setModified(mktime(0, 0, 0, 3, 14, 2020));
$properties->setSubject('My subject');
$properties->setKeywords('my, key, word');
$sectionStyle = []; // стили для раздела
$section = $phpWord->addSection($sectionStyle); // добавление раздела с добавлением стилей
}
Мы создали документ и задали какие-то начальные параметры. Но для того, что бы начать что-то рисовать, нужно создать отдельную секцию и передать массив со стилями (цвета, шрифт, отступы, и.т.д.).
Добавление текста происходит примерно так:
$text = 'Отчет о выполнении плана работ';
$section->addText(htmlspecialchars($text), ['bold'=>true, 'size' => 14], ["alignment" => 'center']);
Добавление таблиц, так:
// стили для таблиц
$table_styles_1 = [
'borderColor' => '000000',
'borderSize' => 1,
'cellMargin' => 0,
'cellSpacing ' => 0
];
$table_styles_2 = [
'borderColor' => '000000',
'borderTopSize' => 1,
'cellMargin' => 0,
'cellSpacing ' => 0
];
$cellStyleTopBorder = [
'borderTopColor' => "000000",
'borderBottomColor' => "ffffff",
'borderRightColor' => "ffffff",
'borderLeftColor' => "ffffff",
'borderSize' => 1,
'cellMargin' => 0,
'cellSpacing ' => 0
];
// добавляем стили для таблицы. Задаем идентификатор настроек со стилями, сами стили и стили для первой строки. Параметры идут по порядку.
$firstRowStyle = array('bgColor' => '66BBFF'); // стиль для первой строки
$phpWord->addTableStyle('first_table', $table_styles_1, []);
$phpWord->addTableStyle('second_table', $table_styles_2, []);
Тут мы создали два стиля с идентификаторами ‘first_table’ и ‘'second_table’
Далее задаем параметры для строк и ячеек:
$cellColSpan = array('gridSpan' => 2, 'valign' => 'center');
$cellHCentered = array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER);
$cellVCentered = array('valign' => 'center');
И наконец, в нашей секции обрисовываем таблицу и передаем в конструктор идентификатор стилей.
$table = $section->addTable("first_table");
Переменная $table имеет доступ к методам для создания строк и ячеек таблиц. Укажем все стили и параметры которые мы задали для них.
$table->addRow([\PhpOffice\PhpWord\Shared\Converter::pixelToTwip(1200)]);
$cell2 = $table->addCell(\PhpOffice\PhpWord\Shared\Converter::pixelToTwip(600), $cellColSpan);
$textrun2 = $cell2->addTextRun($cellHCentered);
$textrun2->addText('ФИО работника');
$cell2 = $table->addCell(\PhpOffice\PhpWord\Shared\Converter::pixelToTwip(600), $cellColSpan);
$textrun2 = $cell2->addTextRun($cellHCentered);
$textrun2->addText($fullName);
$table->addRow([\PhpOffice\PhpWord\Shared\Converter::pixelToTwip(1200)]);
$cell2 = $table->addCell(\PhpOffice\PhpWord\Shared\Converter::pixelToTwip(600), $cellColSpan);
$textrun2 = $cell2->addTextRun($cellHCentered);
$textrun2->addText('Должность');
$cell2 = $table->addCell(\PhpOffice\PhpWord\Shared\Converter::pixelToTwip(600), $cellColSpan);
$textrun2 = $cell2->addTextRun($cellHCentered);
$textrun2->addText($userFields["FIELDS"]["UF_POSITION"]);
Методы вполне говорящие.
$table->addRow() - создает строку таблицы
$table->addCell() - создает ячейку в таблице. Причем сколько раз указан этот метод, столько ячеек и будет создано.
В документации указаны полные списки параметров для создании той или иной сущности. Я лишь хочу привести пример использования. В результате, должно получиться нечто подобное

С помощью метода $section->addTextBreak(1); можно выставлять разрывы между секциями.
Все данные берутся из свойств элемента . По-этому событие нужно вешать после того, как элемент сохраниться в базе данных, что бы были доступны все значения свойств.
Если нужен эффект ознакомления, то можно использовать такую же таблицу, только цвет для соответствующих границ указать белым как фон самого документа.
$cellStyleTopBorder = [
'borderTopColor' => "000000",
'borderBottomColor' => "ffffff",
'borderRightColor' => "ffffff",
'borderLeftColor' => "ffffff",
'borderSize' => 1,
'cellMargin' => 0,
'cellSpacing ' => 0
];

Так же можно добавить несколько строк для свободного текста с помощью $section->addLine(['weight' => 1, 'width' => 450, 'height' => 0]);
Что бы сохранить получившийся документ и привязать его к соответствующему свойству вашего элемента, сделаем следующие шаги:
$pathToDOC = $_SERVER["DOCUMENT_ROOT"]."/upload/hr_docs/".$incomArray["CODE"];
\Bitrix\Main\IO\Directory::createDirectory($pathToDOC);
// сохраняем документ, с помощью библиотеки. Доступен
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
$objWriter->save($pathToDOC.'/'.$incomArray["CODE"].'.docx');
// затем сохраняем его методами Bitrix, что бы файл попал в базу и проиндексировался, присваиваем значение свойству и удаляем тот что был создан с помощью библиотеки.
$arFile = CFile::MakeFileArray($pathToDOC.'/'.$incomArray["CODE"].'.docx');
$arFile["MODULE_ID"] = "iblock";
$arFile["del"] = "Y";
CIBlockElement::SetPropertyValueCode($incomArray["ID"], "DOCUMENT_SECOND", ["86" => ["VALUE" => $arFile]]);
\Bitrix\Main\IO\Directory::deleteDirectory($pathToDOC); // удаление директории куда записали исходный файл
Немного странный подход, но по-другому сделать не получилось. В моем случае, все документы делались по ajax без перезагрузки страниц.
В результате кодовая база получается довольно большая, в основном из-за больших массивов со стилями и громоздких параметров при создании элементов таблиц. Библиотека PHPWord содержит возможность создавать все элементы word, и я показал наиболее сложные, на мой взгляд, элементы - таблицы.