Skip to content

Commit

Permalink
Fix Clone/delete
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnymallie committed Mar 14, 2017
1 parent 03cdd38 commit d825ff7
Showing 1 changed file with 113 additions and 12 deletions.
125 changes: 113 additions & 12 deletions src/PhpWord/TemplateProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -362,18 +362,16 @@ public function cloneBlock($blockname, $clones = 1, $replace = true)
*/
public function replaceBlock($blockname, $replacement)
{
preg_match(
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
$this->tempDocumentMainPart,
$matches
);

if (isset($matches[3])) {
$this->tempDocumentMainPart = str_replace(
$matches[2] . $matches[3] . $matches[4],
$replacement,
$this->tempDocumentMainPart
);
$matches = $this->findBlock($blockname);

if (isset($matches[1]))
{
$this->temporaryDocumentMainPart = str_replace
(
$matches[0],
$replacement,
$this->temporaryDocumentMainPart
);
}
}

Expand Down Expand Up @@ -584,4 +582,107 @@ protected function getSlice($startPosition, $endPosition = 0)

return substr($this->tempDocumentMainPart, $startPosition, ($endPosition - $startPosition));
}

private function findBlock($blockname)
{
// Parse the XML
$xml = new \SimpleXMLElement($this->temporaryDocumentMainPart);

// Find the starting and ending tags
$startNode = false; $endNode = false;
foreach ($xml->xpath('//w:t') as $node)
{
if (strpos($node, '${'.$blockname.'}') !== false)
{
$startNode = $node;
continue;
}

if (strpos($node, '${/'.$blockname.'}') !== false)
{
$endNode = $node;
break;
}
}

// Make sure we found the tags
if ($startNode === false || $endNode === false)
{
return null;
}

// Find the parent <w:p> node for the start tag
$node = $startNode; $startNode = null;
while (is_null($startNode))
{
$node = $node->xpath('..')[0];

if ($node->getName() == 'p')
{
$startNode = $node;
}
}

// Find the parent <w:p> node for the end tag
$node = $endNode; $endNode = null;
while (is_null($endNode))
{
$node = $node->xpath('..')[0];

if ($node->getName() == 'p')
{
$endNode = $node;
}
}

/*
* NOTE: Because SimpleXML reduces empty tags to "self-closing" tags.
* We need to replace the original XML with the version of XML as
* SimpleXML sees it. The following example should show the issue
* we are facing.
*
* This is the XML that my document contained orginally.
*
* ```xml
* <w:p>
* <w:pPr>
* <w:pStyle w:val="TextBody"/>
* <w:rPr></w:rPr>
* </w:pPr>
* <w:r>
* <w:rPr></w:rPr>
* <w:t>${CLONEME}</w:t>
* </w:r>
* </w:p>
* ```
*
* This is the XML that SimpleXML returns from asXml().
*
* ```xml
* <w:p>
* <w:pPr>
* <w:pStyle w:val="TextBody"/>
* <w:rPr/>
* </w:pPr>
* <w:r>
* <w:rPr/>
* <w:t>${CLONEME}</w:t>
* </w:r>
* </w:p>
* ```
*/

$this->temporaryDocumentMainPart = $xml->asXml();

// Find the xml in between the tags
$xmlBlock = null;
preg_match
(
'/'.preg_quote($startNode->asXml(), '/').'(.*?)'.preg_quote($endNode->asXml(), '/').'/is',
$this->temporaryDocumentMainPart,
$matches
);

return $matches;
}
}

1 comment on commit d825ff7

@FBnil
Copy link

@FBnil FBnil commented on d825ff7 Oct 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very elegant. The result of findblock can find more than one block, and thus cloneBlock() could have made use of it. Would love to profile this and see if it is faster than our current string based matches.

Please sign in to comment.