Skip to content

Commit b453cf0

Browse files
authored
Merge pull request #2261 from ismail1432/customize-macro
Allow to customize macro delimiters in TemplateProcessor
2 parents f195d28 + c4bcba9 commit b453cf0

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;
@@ -856,8 +860,12 @@ public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVaria
856860
{
857861
$xmlBlock = null;
858862
$matches = [];
863+
$escapedMacroOpeningChars = self::$macroOpeningChars;
864+
$escapedMacroClosingChars = self::$macroClosingChars;
859865
preg_match(
860-
'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\${' . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\${\/' . $blockname . '}<\/w:.*?p>)/is',
866+
//'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\{{' . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\{{\/' . $blockname . '}<\/w:.*?p>)/is',
867+
'/(.*((?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',
868+
//'/(.*((?s)<w:p\b(?:(?!<w:p\b).)*?\\'. $escapedMacroOpeningChars . $blockname . '}<\/w:.*?p>))(.*)((?s)<w:p\b(?:(?!<w:p\b).)[^$]*?\\'.$escapedMacroOpeningChars.'\/' . $blockname . '}<\/w:.*?p>)/is',
861869
$this->tempDocumentMainPart,
862870
$matches
863871
);
@@ -896,8 +904,10 @@ public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVaria
896904
public function replaceBlock($blockname, $replacement): void
897905
{
898906
$matches = [];
907+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
908+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
899909
preg_match(
900-
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
910+
'/(<\?xml.*)(<w:p.*>' . $escapedMacroOpeningChars . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)(.*)(<w:p.*' . $escapedMacroOpeningChars . '\/' . $blockname . $escapedMacroClosingChars . '<\/w:.*?p>)/is',
901911
$this->tempDocumentMainPart,
902912
$matches
903913
);
@@ -1013,8 +1023,12 @@ public function saveAs($fileName): void
10131023
*/
10141024
protected function fixBrokenMacros($documentPart)
10151025
{
1026+
$brokenMacroOpeningChars = substr(self::$macroOpeningChars, 0, 1);
1027+
$endMacroOpeningChars = substr(self::$macroOpeningChars, 1);
1028+
$macroClosingChars = self::$macroClosingChars;
1029+
10161030
return preg_replace_callback(
1017-
'/\$(?:\{|[^{$]*\>\{)[^}$]*\}/U',
1031+
'/\\' . $brokenMacroOpeningChars . '(?:\\' . $endMacroOpeningChars . '|[^{$]*\>\{)[^' . $macroClosingChars . '$]*\}/U',
10181032
function ($match) {
10191033
return strip_tags($match[0]);
10201034
},
@@ -1053,7 +1067,10 @@ protected function setValueForPart($search, $replace, $documentPartXML, $limit)
10531067
protected function getVariablesForPart($documentPartXML)
10541068
{
10551069
$matches = [];
1056-
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
1070+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
1071+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
1072+
1073+
preg_match_all("/$escapedMacroOpeningChars(.*?)$escapedMacroClosingChars/i", $documentPartXML, $matches);
10571074

10581075
return $matches[1];
10591076
}
@@ -1238,8 +1255,11 @@ protected function getSlice($startPosition, $endPosition = 0)
12381255
protected function indexClonedVariables($count, $xmlBlock)
12391256
{
12401257
$results = [];
1258+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
1259+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
1260+
12411261
for ($i = 1; $i <= $count; ++$i) {
1242-
$results[] = preg_replace('/\$\{([^:]*?)(:.*?)?\}/', '\${\1#' . $i . '\2}', $xmlBlock);
1262+
$results[] = preg_replace("/$escapedMacroOpeningChars([^:]*?)(:.*?)?$escapedMacroClosingChars/", self::$macroOpeningChars . '\1#' . $i . '\2' . self::$macroClosingChars, $xmlBlock);
12431263
}
12441264

12451265
return $results;
@@ -1394,7 +1414,7 @@ protected function splitTextIntoTexts($text)
13941414
}
13951415

13961416
$unformattedText = preg_replace('/>\s+</', '><', $text);
1397-
$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);
1417+
$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);
13981418

13991419
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);
14001420
}
@@ -1408,6 +1428,25 @@ protected function splitTextIntoTexts($text)
14081428
*/
14091429
protected function textNeedsSplitting($text)
14101430
{
1411-
return preg_match('/[^>]\${|}[^<]/i', $text) == 1;
1431+
$escapedMacroOpeningChars = preg_quote(self::$macroOpeningChars);
1432+
$escapedMacroClosingChars = preg_quote(self::$macroClosingChars);
1433+
1434+
return 1 === preg_match('/[^>]' . $escapedMacroOpeningChars . '|' . $escapedMacroClosingChars . '[^<]/i', $text);
1435+
}
1436+
1437+
public function setMacroOpeningChars(string $macroOpeningChars): void
1438+
{
1439+
self::$macroOpeningChars = $macroOpeningChars;
1440+
}
1441+
1442+
public function setMacroClosingChars(string $macroClosingChars): void
1443+
{
1444+
self::$macroClosingChars = $macroClosingChars;
1445+
}
1446+
1447+
public function setMacroChars(string $macroOpeningChars, string $macroClosingChars): void
1448+
{
1449+
self::$macroOpeningChars = $macroOpeningChars;
1450+
self::$macroClosingChars = $macroClosingChars;
14121451
}
14131452
}

0 commit comments

Comments
 (0)