Skip to content

Commit aca145c

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

File tree

2 files changed

+66
-60
lines changed

2 files changed

+66
-60
lines changed

src/PhpWord/TemplateProcessor.php

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -321,29 +321,31 @@ 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]))
324+
325+
$matches = $this->findBlocks($blockname);
326+
327+
foreach ($matches as $match)
328328
{
329-
$xmlBlock = $matches[1];
329+
if (isset($match[1]))
330+
{
331+
$xmlBlock = $match[1];
330332

331-
$cloned = array();
333+
$cloned = array();
332334

333-
for ($i = 1; $i <= $clones; $i++)
334-
{
335-
$cloned[] = preg_replace('/\${(.*?)}/','${$1_'.$i.'}', $xmlBlock);
336-
}
335+
for ($i = 1; $i <= $clones; $i++)
336+
{
337+
$cloned[] = preg_replace('/\${(.*?)}/','${$1_'.$i.'}', $xmlBlock);
338+
}
337339

338-
if ($replace)
339-
{
340-
$this->tempDocumentMainPart = str_replace
341-
(
342-
$matches[0],
343-
implode('', $cloned),
344-
$this->tempDocumentMainPart
345-
);
346-
}
340+
if ($replace)
341+
{
342+
$this->tempDocumentMainPart = str_replace(
343+
$match[0],
344+
implode('', $cloned),
345+
$this->tempDocumentMainPart
346+
);
347+
}
348+
}
347349
}
348350

349351
return $xmlBlock;
@@ -357,16 +359,18 @@ public function cloneBlock($blockname, $clones = 1, $replace = true)
357359
*/
358360
public function replaceBlock($blockname, $replacement)
359361
{
360-
$matches = $this->findBlock($blockname);
361-
362-
if (isset($matches[1]))
362+
$matches = $this->findBlocks($blockname);
363+
364+
foreach ($matches as $match)
363365
{
364-
$this->tempDocumentMainPart = str_replace
365-
(
366-
$matches[0],
366+
if (isset($match[1]))
367+
{
368+
$this->tempDocumentMainPart = str_replace(
369+
$match[0],
367370
$replacement,
368371
$this->tempDocumentMainPart
369372
);
373+
}
370374
}
371375
}
372376

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

574578
return substr($this->tempDocumentMainPart, $startPosition, ($endPosition - $startPosition));
575579
}
576-
577-
private function findBlock($blockname)
580+
581+
private function findBlocks($blockname)
578582
{
579583
// Parse the XML
580584
$xml = new \SimpleXMLElement($this->tempDocumentMainPart);
581-
585+
582586
// Find the starting and ending tags
583587
$startNode = false; $endNode = false;
588+
$state = "outside";
589+
$pairs = array();
584590
foreach ($xml->xpath('//w:t') as $node)
585591
{
586592
if (strpos($node, '${'.$blockname.'}') !== false)
587593
{
588594
$startNode = $node;
595+
$state = "inside";
589596
continue;
590597
}
591-
592-
if (strpos($node, '${/'.$blockname.'}') !== false)
598+
599+
if ($state === 'inside' && strpos($node, '${/'.$blockname.'}') !== false)
593600
{
594601
$endNode = $node;
595-
break;
602+
$pairs[] = array($startNode, $endNode);
603+
$startNode = false; $endNode = false;
604+
$state = 'outside';
596605
}
597606
}
598-
607+
599608
// Make sure we found the tags
600-
if ($startNode === false || $endNode === false)
609+
if (count($pairs) === 0)
601610
{
602611
return null;
603612
}
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-
}
613+
614+
$result = array();
615+
foreach ($pairs as $pair) {
616+
$result[] = $this->findEnclosing($pair[0], $pair[1], $xml);
615617
}
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-
}
618+
return $result;
619+
}
620+
621+
622+
private static function getParentByName($node, $name) {
623+
while ($node->getName() !== $name) {
624+
// $node = $node->parent();
625+
$node = $node->xpath('..')[0];
627626
}
628-
627+
return $node;
628+
}
629+
630+
private function findEnclosing($startNode, $endNode, $xml) {
631+
632+
// Find the parent <w:p> nodes for startNode & endNode
633+
$startNode = self::getParentByName($startNode, 'p');
634+
$endNode = self::getParentByName($endNode, 'p');
635+
629636
/*
630637
* NOTE: Because SimpleXML reduces empty tags to "self-closing" tags.
631638
* We need to replace the original XML with the version of XML as
@@ -662,18 +669,17 @@ private function findBlock($blockname)
662669
* </w:p>
663670
* ```
664671
*/
665-
672+
666673
$this->tempDocumentMainPart = $xml->asXml();
667-
674+
668675
// Find the xml in between the tags
669-
$xmlBlock = null;
670676
preg_match
671677
(
672678
'/'.preg_quote($startNode->asXml(), '/').'(.*?)'.preg_quote($endNode->asXml(), '/').'/is',
673679
$this->tempDocumentMainPart,
674680
$matches
675681
);
676-
682+
677683
return $matches;
678684
}
679685
}
Binary file not shown.

0 commit comments

Comments
 (0)