Skip to content

Commit d579736

Browse files
author
Roman Syroeshko
committed
1 parent 1c5bb3a commit d579736

File tree

7 files changed

+143
-31
lines changed

7 files changed

+143
-31
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Place announcement text here.
1212
- Introduced the `\PhpOffice\PhpWord\SimpleType\JcTable` simple type. - @RomanSyroeshko
1313
- Introduced writer for the "Paragraph Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\ParagraphAlignment`). - @RomanSyroeshko
1414
- Introduced writer for the "Table Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\TableAlignment`). - @RomanSyroeshko
15+
- Supported indexed arrays in arguments of `TemplateProcessor::setValue()`. - @RomanSyroeshko #618
1516

1617
### Changed
1718
- Improved error message for the case when `autoload.php` is not found. - @RomanSyroeshko #371
@@ -39,7 +40,7 @@ so installation via ZIP-archive download is not an option anymore. To install PH
3940

4041
### Fixed
4142
- `Undefined property` error while reading MS-DOC documents. - @jaberu #610
42-
- Corrupted OOXML template issue in case when its macro is broken immediately after `$` sign.
43+
- Corrupted OOXML template issue in case when its names is broken immediately after `$` sign.
4344
That case wasn't taken into account in implementation of `TemplateProcessor::fixBrokenMacros()`. - @RomanSyroeshko @d-damien #548
4445

4546

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
"require": {
3535
"php": ">=5.3.3",
3636
"ext-xml": "*",
37-
"zendframework/zend-validator": "2.5.*",
3837
"zendframework/zend-stdlib": "~2.5",
38+
"zendframework/zend-validator": "2.5.*",
3939
"phpoffice/common": "0.2.*"
4040
},
4141
"require-dev": {
@@ -47,8 +47,8 @@
4747
"dompdf/dompdf":"0.6.*",
4848
"tecnickcom/tcpdf": "6.*",
4949
"mpdf/mpdf": "5.*",
50-
"zendframework/zend-validator": "2.5.*",
5150
"zendframework/zend-stdlib": "~2.5",
51+
"zendframework/zend-validator": "2.5.*",
5252
"phpoffice/common": "0.2.*"
5353
},
5454
"suggest": {

docs/templates-processing.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Templates processing
44
====================
55

6-
You can create a .docx document template with included search-patterns which can be replaced by any value you wish. Only single-line values can be replaced.
6+
You can create an OOXML document template with included search-patterns (macros) which can be replaced by any value you wish. Only single-line values can be replaced.
77

88
To deal with a template file, use ``new TemplateProcessor`` statement. After TemplateProcessor instance creation the document template is copied into the temporary directory. Then you can use ``TemplateProcessor::setValue`` method to change the value of a search pattern. The search-pattern model is: ``${search-pattern}``.
99

@@ -12,8 +12,8 @@ Example:
1212
.. code-block:: php
1313
1414
$templateProcessor = new TemplateProcessor('Template.docx');
15-
$templateProcessor->setValue('Name', 'Somebody someone');
16-
$templateProcessor->setValue('Street', 'Coming-Undone-Street 32');
15+
$templateProcessor->setValue('Name', 'John Doe');
16+
$templateProcessor->setValue(array('City', 'Street'), array('Detroit', '12th Street'));
1717
1818
It is not possible to directly add new OOXML elements to the template file being processed, but it is possible to transform main document part of the template using XSLT (see ``TemplateProcessor::applyXslStyleSheet``).
1919

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
/**
3+
* This file is part of PHPWord - A pure PHP library for reading and writing
4+
* word processing documents.
5+
*
6+
* PHPWord is free software distributed under the terms of the GNU Lesser
7+
* General Public License version 3 as published by the Free Software Foundation.
8+
*
9+
* For the full copyright and license information, please read the LICENSE
10+
* file that was distributed with this source code. For the full list of
11+
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
12+
*
13+
* @link https://github.com/PHPOffice/PHPWord
14+
* @copyright 2010-2016 PHPWord contributors
15+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16+
*/
17+
18+
namespace PhpOffice\PhpWord\Escaper;
19+
20+
/**
21+
* @since 0.13.0
22+
*/
23+
interface EscaperInterface
24+
{
25+
/**
26+
* @param mixed $subject
27+
*
28+
* @return mixed
29+
*/
30+
public static function escape($subject);
31+
}

src/PhpWord/Escaper/RegExp.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
/**
3+
* This file is part of PHPWord - A pure PHP library for reading and writing
4+
* word processing documents.
5+
*
6+
* PHPWord is free software distributed under the terms of the GNU Lesser
7+
* General Public License version 3 as published by the Free Software Foundation.
8+
*
9+
* For the full copyright and license information, please read the LICENSE
10+
* file that was distributed with this source code. For the full list of
11+
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
12+
*
13+
* @link https://github.com/PHPOffice/PHPWord
14+
* @copyright 2010-2016 PHPWord contributors
15+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16+
*/
17+
18+
namespace PhpOffice\PhpWord\Escaper;
19+
20+
/**
21+
* @since 0.13.0
22+
*/
23+
class RegExp implements EscaperInterface
24+
{
25+
const REG_EXP_DELIMITER = '/';
26+
27+
/**
28+
* @param string $subject
29+
*
30+
* @return string
31+
*/
32+
protected static function escapeSingleItem($subject)
33+
{
34+
return self::REG_EXP_DELIMITER . preg_quote($subject, self::REG_EXP_DELIMITER) . self::REG_EXP_DELIMITER . 'u';
35+
}
36+
37+
public static function escape($subject)
38+
{
39+
if (is_array($subject)) {
40+
foreach ($subject as &$item) {
41+
$item = self::escapeSingleItem($item);
42+
}
43+
} else {
44+
$subject = self::escapeSingleItem($subject);
45+
}
46+
47+
return $subject;
48+
}
49+
}

src/PhpWord/TemplateProcessor.php

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace PhpOffice\PhpWord;
1919

20+
use PhpOffice\PhpWord\Escaper\RegExp;
2021
use PhpOffice\PhpWord\Exception\CopyFileException;
2122
use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
2223
use PhpOffice\PhpWord\Exception\Exception;
@@ -136,31 +137,61 @@ public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslO
136137
}
137138

138139
/**
139-
* @param mixed $macro
140-
* @param mixed $replace
141-
* @param integer $limit
140+
* @param string $macro
142141
*
143-
* @return void
142+
* @return string
144143
*/
145-
public function setValue($macro, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
144+
protected static function ensureMacroCompleted($macro)
146145
{
147146
if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
148147
$macro = '${' . $macro . '}';
149148
}
150149

151-
if (!StringUtils::isValidUtf8($replace)) {
152-
$replace = utf8_encode($replace);
153-
}
150+
return $macro;
151+
}
154152

155-
foreach ($this->tempDocumentHeaders as $index => $headerXML) {
156-
$this->tempDocumentHeaders[$index] = $this->setValueForPart($this->tempDocumentHeaders[$index], $macro, $replace, $limit);
153+
/**
154+
* @param string $subject
155+
*
156+
* @return string
157+
*/
158+
protected static function ensureUtf8Encoded($subject)
159+
{
160+
if (!StringUtils::isValidUtf8($subject)) {
161+
$subject = utf8_encode($subject);
157162
}
158163

159-
$this->tempDocumentMainPart = $this->setValueForPart($this->tempDocumentMainPart, $macro, $replace, $limit);
164+
return $subject;
165+
}
160166

161-
foreach ($this->tempDocumentFooters as $index => $headerXML) {
162-
$this->tempDocumentFooters[$index] = $this->setValueForPart($this->tempDocumentFooters[$index], $macro, $replace, $limit);
167+
/**
168+
* @param mixed $search
169+
* @param mixed $replace
170+
* @param integer $limit
171+
*
172+
* @return void
173+
*/
174+
public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
175+
{
176+
if (is_array($search)) {
177+
foreach ($search as &$item) {
178+
$item = self::ensureMacroCompleted($item);
179+
}
180+
} else {
181+
$search = self::ensureMacroCompleted($search);
163182
}
183+
184+
if (is_array($replace)) {
185+
foreach ($replace as &$item) {
186+
$item = self::ensureUtf8Encoded($item);
187+
}
188+
} else {
189+
$replace = self::ensureUtf8Encoded($replace);
190+
}
191+
192+
$this->tempDocumentHeaders = $this->setValueForPart($search, $replace, $this->tempDocumentHeaders, $limit);
193+
$this->tempDocumentMainPart = $this->setValueForPart($search, $replace, $this->tempDocumentMainPart, $limit);
194+
$this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit);
164195
}
165196

166197
/**
@@ -398,22 +429,20 @@ function ($match) {
398429
/**
399430
* Find and replace macros in the given XML section.
400431
*
432+
* @param mixed $search
433+
* @param mixed $replace
401434
* @param string $documentPartXML
402-
* @param string $search
403-
* @param string $replace
404435
* @param integer $limit
405436
*
406437
* @return string
407438
*/
408-
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
439+
protected function setValueForPart($search, $replace, $documentPartXML, $limit)
409440
{
410441
// Note: we can't use the same function for both cases here, because of performance considerations.
411442
if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) {
412443
return str_replace($search, $replace, $documentPartXML);
413444
} else {
414-
$regExpDelim = '/';
415-
$escapedSearch = preg_quote($search, $regExpDelim);
416-
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
445+
return preg_replace(RegExp::escape($search), $replace, $documentPartXML, $limit);
417446
}
418447
}
419448

tests/PhpWord/TemplateProcessorTest.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,17 @@ public function testMacrosCanBeReplacedInHeaderAndFooter()
175175
{
176176
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx');
177177

178-
$this->assertEquals(
179-
array('documentContent', 'headerValue', 'footerValue'),
180-
$templateProcessor->getVariables()
178+
$this->assertEquals(array('documentContent', 'headerValue', 'footerValue'), $templateProcessor->getVariables());
179+
180+
$macroNames = array('headerValue', 'documentContent', 'footerValue');
181+
$macroValues = array(
182+
htmlspecialchars('Header Value', ENT_COMPAT, 'UTF-8'),
183+
htmlspecialchars('Document text.', ENT_COMPAT, 'UTF-8'),
184+
htmlspecialchars('Footer Value', ENT_COMPAT, 'UTF-8')
181185
);
186+
$templateProcessor->setValue($macroNames, $macroValues);
182187

183188
$docName = 'header-footer-test-result.docx';
184-
$templateProcessor->setValue('headerValue', htmlspecialchars('Header Value', ENT_COMPAT, 'UTF-8'));
185-
$templateProcessor->setValue('documentContent', htmlspecialchars('Document text.', ENT_COMPAT, 'UTF-8'));
186-
$templateProcessor->setValue('footerValue', htmlspecialchars('Footer Value', ENT_COMPAT, 'UTF-8'));
187189
$templateProcessor->saveAs($docName);
188190
$docFound = file_exists($docName);
189191
unlink($docName);

0 commit comments

Comments
 (0)