Skip to content

Commit 3e8ea9a

Browse files
committed
Support multiple occurrences of the same placeholder
1 parent 8fe9941 commit 3e8ea9a

File tree

2 files changed

+72
-73
lines changed

2 files changed

+72
-73
lines changed

src/PhpWord/TemplateProcessor.php

Lines changed: 72 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -321,28 +321,26 @@ public function cloneRow($search, $numberOfClones)
321321
public function cloneBlock($blockname, $clones = 1, $replace = true)
322322
{
323323
$xmlBlock = null;
324-
325-
$matches = $this->findBlock($blockname);
326-
327-
if (isset($matches[1]))
328-
{
329-
$xmlBlock = $matches[1];
330-
331-
$cloned = array();
332-
333-
for ($i = 1; $i <= $clones; $i++)
334-
{
335-
$cloned[] = preg_replace('/\${(.*?)}/','${$1_'.$i.'}', $xmlBlock);
336-
}
337324

338-
if ($replace)
339-
{
340-
$this->tempDocumentMainPart = str_replace
341-
(
342-
$matches[0],
343-
implode('', $cloned),
344-
$this->tempDocumentMainPart
345-
);
325+
$matches = $this->findBlocks($blockname);
326+
327+
foreach ($matches as $match) {
328+
if (isset($match[1])) {
329+
$xmlBlock = $match[1];
330+
331+
$cloned = array();
332+
333+
for ($i = 1; $i <= $clones; $i++) {
334+
$cloned[] = preg_replace('/\${(.*?)}/', '${$1_' . $i . '}', $xmlBlock);
335+
}
336+
337+
if ($replace) {
338+
$this->tempDocumentMainPart = str_replace(
339+
$match[0],
340+
implode('', $cloned),
341+
$this->tempDocumentMainPart
342+
);
343+
}
346344
}
347345
}
348346

@@ -357,16 +355,16 @@ public function cloneBlock($blockname, $clones = 1, $replace = true)
357355
*/
358356
public function replaceBlock($blockname, $replacement)
359357
{
360-
$matches = $this->findBlock($blockname);
361-
362-
if (isset($matches[1]))
363-
{
364-
$this->tempDocumentMainPart = str_replace
365-
(
366-
$matches[0],
358+
$matches = $this->findBlocks($blockname);
359+
360+
foreach ($matches as $match) {
361+
if (isset($match[1])) {
362+
$this->tempDocumentMainPart = str_replace(
363+
$match[0],
367364
$replacement,
368365
$this->tempDocumentMainPart
369366
);
367+
}
370368
}
371369
}
372370

@@ -573,59 +571,62 @@ protected function getSlice($startPosition, $endPosition = 0)
573571

574572
return substr($this->tempDocumentMainPart, $startPosition, ($endPosition - $startPosition));
575573
}
576-
577-
private function findBlock($blockname)
574+
575+
private function findBlocks($blockname)
578576
{
579577
// Parse the XML
580578
$xml = new \SimpleXMLElement($this->tempDocumentMainPart);
581-
579+
582580
// Find the starting and ending tags
583-
$startNode = false; $endNode = false;
584-
foreach ($xml->xpath('//w:t') as $node)
585-
{
586-
if (strpos($node, '${'.$blockname.'}') !== false)
587-
{
581+
$startNode = false;
582+
$endNode = false;
583+
$state = 'outside';
584+
$pairs = array();
585+
foreach ($xml->xpath('//w:t') as $node) {
586+
if (strpos($node, '${' . $blockname . '}') !== false) {
588587
$startNode = $node;
588+
$state = 'inside';
589589
continue;
590590
}
591-
592-
if (strpos($node, '${/'.$blockname.'}') !== false)
593-
{
591+
592+
if ($state === 'inside' && strpos($node, '${/' . $blockname . '}') !== false) {
594593
$endNode = $node;
595-
break;
594+
$pairs[] = array($startNode, $endNode);
595+
$startNode = false;
596+
$endNode = false;
597+
$state = 'outside';
596598
}
597599
}
598-
600+
599601
// Make sure we found the tags
600-
if ($startNode === false || $endNode === false)
601-
{
602+
if (count($pairs) === 0) {
602603
return null;
603604
}
604-
605-
// Find the parent <w:p> node for the start tag
606-
$node = $startNode; $startNode = null;
607-
while (is_null($startNode))
608-
{
609-
$node = $node->xpath('..')[0];
610-
611-
if ($node->getName() == 'p')
612-
{
613-
$startNode = $node;
614-
}
605+
606+
$result = array();
607+
foreach ($pairs as $pair) {
608+
$result[] = $this->findEnclosing($pair[0], $pair[1], $xml);
615609
}
616-
617-
// Find the parent <w:p> node for the end tag
618-
$node = $endNode; $endNode = null;
619-
while (is_null($endNode))
620-
{
621-
$node = $node->xpath('..')[0];
622-
623-
if ($node->getName() == 'p')
624-
{
625-
$endNode = $node;
626-
}
610+
611+
return $result;
612+
}
613+
614+
private static function getParentByName($node, $name)
615+
{
616+
while ($node->getName() !== $name) {
617+
$parents = $node->xpath('..');
618+
$node = $parents[0];
627619
}
628-
620+
621+
return $node;
622+
}
623+
624+
private function findEnclosing($startNode, $endNode, $xml)
625+
{
626+
// Find the parent <w:p> nodes for startNode & endNode
627+
$startNode = self::getParentByName($startNode, 'p');
628+
$endNode = self::getParentByName($endNode, 'p');
629+
629630
/*
630631
* NOTE: Because SimpleXML reduces empty tags to "self-closing" tags.
631632
* We need to replace the original XML with the version of XML as
@@ -662,18 +663,16 @@ private function findBlock($blockname)
662663
* </w:p>
663664
* ```
664665
*/
665-
666+
666667
$this->tempDocumentMainPart = $xml->asXml();
667-
668+
668669
// Find the xml in between the tags
669-
$xmlBlock = null;
670-
preg_match
671-
(
672-
'/'.preg_quote($startNode->asXml(), '/').'(.*?)'.preg_quote($endNode->asXml(), '/').'/is',
670+
preg_match(
671+
'/' . preg_quote($startNode->asXml(), '/') . '(.*?)' . preg_quote($endNode->asXml(), '/') . '/is',
673672
$this->tempDocumentMainPart,
674673
$matches
675674
);
676-
675+
677676
return $matches;
678677
}
679678
}
Binary file not shown.

0 commit comments

Comments
 (0)