diff --git a/CHANGELOG.md b/CHANGELOG.md index 8993a818..ebf98184 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Place announcement text here. - Introduced the `\PhpOffice\PhpWord\SimpleType\JcTable` simple type. - @RomanSyroeshko - Introduced writer for the "Paragraph Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\ParagraphAlignment`). - @RomanSyroeshko - Introduced writer for the "Table Alignment" element (see `\PhpOffice\PhpWord\Writer\Word2007\Element\TableAlignment`). - @RomanSyroeshko +- Supported indexed arrays in arguments of `TemplateProcessor::setValue()`. - @RomanSyroeshko #618 ### Changed - 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 ### Fixed - `Undefined property` error while reading MS-DOC documents. - @jaberu #610 -- Corrupted OOXML template issue in case when its macro is broken immediately after `$` sign. +- Corrupted OOXML template issue in case when its names is broken immediately after `$` sign. That case wasn't taken into account in implementation of `TemplateProcessor::fixBrokenMacros()`. - @RomanSyroeshko @d-damien #548 diff --git a/composer.json b/composer.json index 83d43740..48d39ada 100644 --- a/composer.json +++ b/composer.json @@ -34,8 +34,8 @@ "require": { "php": ">=5.3.3", "ext-xml": "*", - "zendframework/zend-validator": "2.5.*", "zendframework/zend-stdlib": "~2.5", + "zendframework/zend-validator": "2.5.*", "phpoffice/common": "0.2.*" }, "require-dev": { @@ -47,8 +47,8 @@ "dompdf/dompdf":"0.6.*", "tecnickcom/tcpdf": "6.*", "mpdf/mpdf": "5.*", - "zendframework/zend-validator": "2.5.*", "zendframework/zend-stdlib": "~2.5", + "zendframework/zend-validator": "2.5.*", "phpoffice/common": "0.2.*" }, "suggest": { diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index 6a65ea0d..1c0b8b03 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -3,7 +3,7 @@ Templates processing ==================== -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. +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. 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}``. @@ -12,8 +12,8 @@ Example: .. code-block:: php $templateProcessor = new TemplateProcessor('Template.docx'); - $templateProcessor->setValue('Name', 'Somebody someone'); - $templateProcessor->setValue('Street', 'Coming-Undone-Street 32'); + $templateProcessor->setValue('Name', 'John Doe'); + $templateProcessor->setValue(array('City', 'Street'), array('Detroit', '12th Street')); 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``). diff --git a/src/PhpWord/Escaper/EscaperInterface.php b/src/PhpWord/Escaper/EscaperInterface.php new file mode 100644 index 00000000..2d860500 --- /dev/null +++ b/src/PhpWord/Escaper/EscaperInterface.php @@ -0,0 +1,31 @@ +tempDocumentHeaders as $index => $headerXML) { - $this->tempDocumentHeaders[$index] = $this->setValueForPart($this->tempDocumentHeaders[$index], $macro, $replace, $limit); + return $subject; + } + + /** + * @param mixed $search + * @param mixed $replace + * @param integer $limit + * + * @return void + */ + public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT) + { + if (is_array($search)) { + foreach ($search as &$item) { + $item = self::ensureMacroCompleted($item); + } + } else { + $search = self::ensureMacroCompleted($search); } - $this->tempDocumentMainPart = $this->setValueForPart($this->tempDocumentMainPart, $macro, $replace, $limit); - - foreach ($this->tempDocumentFooters as $index => $headerXML) { - $this->tempDocumentFooters[$index] = $this->setValueForPart($this->tempDocumentFooters[$index], $macro, $replace, $limit); + if (is_array($replace)) { + foreach ($replace as &$item) { + $item = self::ensureUtf8Encoded($item); + } + } else { + $replace = self::ensureUtf8Encoded($replace); } + + $this->tempDocumentHeaders = $this->setValueForPart($search, $replace, $this->tempDocumentHeaders, $limit); + $this->tempDocumentMainPart = $this->setValueForPart($search, $replace, $this->tempDocumentMainPart, $limit); + $this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit); } /** @@ -398,22 +429,20 @@ class TemplateProcessor /** * Find and replace macros in the given XML section. * + * @param mixed $search + * @param mixed $replace * @param string $documentPartXML - * @param string $search - * @param string $replace * @param integer $limit * * @return string */ - protected function setValueForPart($documentPartXML, $search, $replace, $limit) + protected function setValueForPart($search, $replace, $documentPartXML, $limit) { // Note: we can't use the same function for both cases here, because of performance considerations. if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) { return str_replace($search, $replace, $documentPartXML); } else { - $regExpDelim = '/'; - $escapedSearch = preg_quote($search, $regExpDelim); - return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit); + return preg_replace(RegExp::escape($search), $replace, $documentPartXML, $limit); } } diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 9a475017..82c1b754 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -175,15 +175,17 @@ final class TemplateProcessorTest extends \PHPUnit_Framework_TestCase { $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx'); - $this->assertEquals( - array('documentContent', 'headerValue', 'footerValue'), - $templateProcessor->getVariables() + $this->assertEquals(array('documentContent', 'headerValue', 'footerValue'), $templateProcessor->getVariables()); + + $macroNames = array('headerValue', 'documentContent', 'footerValue'); + $macroValues = array( + htmlspecialchars('Header Value', ENT_COMPAT, 'UTF-8'), + htmlspecialchars('Document text.', ENT_COMPAT, 'UTF-8'), + htmlspecialchars('Footer Value', ENT_COMPAT, 'UTF-8') ); + $templateProcessor->setValue($macroNames, $macroValues); $docName = 'header-footer-test-result.docx'; - $templateProcessor->setValue('headerValue', htmlspecialchars('Header Value', ENT_COMPAT, 'UTF-8')); - $templateProcessor->setValue('documentContent', htmlspecialchars('Document text.', ENT_COMPAT, 'UTF-8')); - $templateProcessor->setValue('footerValue', htmlspecialchars('Footer Value', ENT_COMPAT, 'UTF-8')); $templateProcessor->saveAs($docName); $docFound = file_exists($docName); unlink($docName);