Skip to content

Commit c4bcba9

Browse files
committed
allow to customize macro in TemplateProcessor
1 parent 02ccca1 commit c4bcba9

File tree

6 files changed

+645
-10
lines changed

6 files changed

+645
-10
lines changed

docs/templates-processing.rst

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Templates processing
44
====================
55

66
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.
7-
Macros are defined like this: ``${search-pattern}``.
7+
By default Macros are defined like this: ``${search-pattern}`` but you can define custom macros.
88
To load a template file, create a new instance of the TemplateProcessor.
99

1010
.. code-block:: php
@@ -35,6 +35,30 @@ You can also set multiple values by passing all of them in an array.
3535
3636
$templateProcessor->setValues(array('firstname' => 'John', 'lastname' => 'Doe'));
3737
38+
setMacroOpeningChars
39+
""""""""
40+
You can define a custom opening macro. The following will set ``{#`` as the opening search pattern.
41+
42+
.. code-block:: php
43+
44+
$templateProcessor->setMacroOpeningChars('{#');
45+
46+
setMacroClosingChars
47+
""""""""
48+
You can define a custom closing macro. The following will set ``#}`` as the closing search pattern.
49+
50+
.. code-block:: php
51+
52+
$templateProcessor->setMacroClosingChars('#}');
53+
54+
setMacroChars
55+
""""""""
56+
You can define a custom opening and closing macro at the same time . The following will set the search-pattern like this: ``{#search-pattern#}`` .
57+
58+
.. code-block:: php
59+
60+
$templateProcessor->setMacroChars('{#', '#}');
61+
3862
setImageValue
3963
"""""""""""""
4064
The search-pattern model for images can be like:

src/PhpWord/TemplateProcessor.php

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ class TemplateProcessor
9494
*/
9595
protected $tempDocumentNewImages = [];
9696

97+
protected static $macroOpeningChars = '${';
98+
99+
protected static $macroClosingChars = '}';
100+
97101
/**
98102
* @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception
99103
*
@@ -238,8 +242,8 @@ public function applyXslStyleSheet($xslDomDocument, $xslOptions = [], $xslOption
238242
*/
239243
protected static function ensureMacroCompleted($macro)
240244
{
241-
if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
242-
$macro = '${' . $macro . '}';
245+
if (substr($macro, 0, 2) !== self::$macroOpeningChars && substr($macro, -1) !== self::$macroClosingChars) {
246+
$macro = self::$macroOpeningChars . $macro . self::$macroClosingChars;
243247
}
244248

245249
return $macro;
@@ -792,8 +796,12 @@ public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVaria
792796
{
793797
$xmlBlock = null;
794798
$matches = [];
799+
$escapedMacroOpeningChars = self::$macroOpeningChars;
800+
$escapedMacroClosingChars = self::$macroClosingChars;
795801
preg_match(
796-
'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\${' . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\${\/' . $blockname . '}<\/w:.*?p>)/is',
802+
//'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\{{' . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\{{\/' . $blockname . '}<\/w:.*?p>)/is',
803+
'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\\' . $escapedMacroOpeningChars . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\\' . $escapedMacroOpeningChars . '\/' . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)/is',
804+
//'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\\'. $escapedMacroOpeningChars . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\\'.$escapedMacroOpeningChars.'\/' . $blockname . '}<\/w:.*?p>)/is',
797805
$this->tempDocumentMainPart,
798806
$matches
799807
);
@@ -832,8 +840,10 @@ public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVaria
832840
public function replaceBlock($blockname, $replacement): void
833841
{
834842
$matches = [];
843+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
844+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
835845
preg_match(
836-
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
846+
'/(<\?xml.*)(<w:p.*>' . $escapedMacroOpeningChars . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)(.*)(<w:p.*' . $escapedMacroOpeningChars . '\/' . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)/is',
837847
$this->tempDocumentMainPart,
838848
$matches
839849
);
@@ -949,8 +959,12 @@ public function saveAs($fileName): void
949959
*/
950960
protected function fixBrokenMacros($documentPart)
951961
{
962+
$brokenMacroOpeningChars = substr(self::$macroOpeningChars, 0, 1);
963+
$endMacroOpeningChars = substr(self::$macroOpeningChars, 1);
964+
$macroClosingChars = self::$macroClosingChars;
965+
952966
return preg_replace_callback(
953-
'/\$(?:\{|[^{$]*\>\{)[^}$]*\}/U',
967+
'/\\' . $brokenMacroOpeningChars . '(?:\\' . $endMacroOpeningChars . '|[^{$]*\>\{)[^' . $macroClosingChars . '$]*\}/U',
954968
function ($match) {
955969
return strip_tags($match[0]);
956970
},
@@ -989,7 +1003,10 @@ protected function setValueForPart($search, $replace, $documentPartXML, $limit)
9891003
protected function getVariablesForPart($documentPartXML)
9901004
{
9911005
$matches = [];
992-
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
1006+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
1007+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
1008+
1009+
preg_match_all("/$escapedMacroOpeningChars(.*?)$escapedMacroClosingChars/i", $documentPartXML, $matches);
9931010

9941011
return $matches[1];
9951012
}
@@ -1141,8 +1158,11 @@ protected function getSlice($startPosition, $endPosition = 0)
11411158
protected function indexClonedVariables($count, $xmlBlock)
11421159
{
11431160
$results = [];
1161+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
1162+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
1163+
11441164
for ($i = 1; $i <= $count; ++$i) {
1145-
$results[] = preg_replace('/\$\{([^:]*?)(:.*?)?\}/', '\${\1#' . $i . '\2}', $xmlBlock);
1165+
$results[] = preg_replace("/$escapedMacroOpeningChars([^:]*?)(:.*?)?$escapedMacroClosingChars/", self::$macroOpeningChars . '\1#' . $i . '\2' . self::$macroClosingChars, $xmlBlock);
11461166
}
11471167

11481168
return $results;
@@ -1297,7 +1317,7 @@ protected function splitTextIntoTexts($text)
12971317
}
12981318

12991319
$unformattedText = preg_replace('/>\s+</', '><', $text);
1300-
$result = str_replace(['${', '}'], ['</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">${', '}</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">'], $unformattedText);
1320+
$result = str_replace([self::$macroOpeningChars, self::$macroClosingChars], ['</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">' . self::$macroOpeningChars, self::$macroClosingChars . '</w:t></w:r><w:r>' . $extractedStyle . '<w:t xml:space="preserve">'], $unformattedText);
13011321

13021322
return str_replace(['<w:r>' . $extractedStyle . '<w:t xml:space="preserve"></w:t></w:r>', '<w:r><w:t xml:space="preserve"></w:t></w:r>', '<w:t>'], ['', '', '<w:t xml:space="preserve">'], $result);
13031323
}
@@ -1311,6 +1331,25 @@ protected function splitTextIntoTexts($text)
13111331
*/
13121332
protected function textNeedsSplitting($text)
13131333
{
1314-
return preg_match('/[^>]\${|}[^<]/i', $text) == 1;
1334+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
1335+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
1336+
1337+
return 1 === preg_match('/[^>]' . $escapedMacroOpeningChars . '|' . $escapedMacroClosingChars . '[^<]/i', $text);
1338+
}
1339+
1340+
public function setMacroOpeningChars(string $macroOpeningChars): void
1341+
{
1342+
self::$macroOpeningChars = $macroOpeningChars;
1343+
}
1344+
1345+
public function setMacroClosingChars(string $macroClosingChars): void
1346+
{
1347+
self::$macroClosingChars = $macroClosingChars;
1348+
}
1349+
1350+
public function setMacroChars(string $macroOpeningChars, string $macroClosingChars): void
1351+
{
1352+
self::$macroOpeningChars = $macroOpeningChars;
1353+
self::$macroClosingChars = $macroClosingChars;
13151354
}
13161355
}

0 commit comments

Comments
 (0)