Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 105 additions & 8 deletions src/PhpWord/Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,26 @@ class Template
*/
private $_tempFileName;

/**
* Document header XML
*
* @var string[]
*/
private $_headerXMLs = array();

/**
* Document XML
*
* @var string
*/
private $_documentXML;

/**
* Document footer XML
*
* @var string[]
*/
private $_footerXMLs = array();

/**
* Create a new Template Object
Expand All @@ -62,9 +75,41 @@ public function __construct($strFilename)
$this->_objZip = new $zipClass();
$this->_objZip->open($this->_tempFileName);

// Find and load up to three headers and footers
for ($i = 1; $i <= 3; $i++) {
Copy link
Member

Choose a reason for hiding this comment

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

Why three ?

Copy link
Author

Choose a reason for hiding this comment

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

The only information I could find on this suggested that three was the maximum number allowed. Is that incorrect?

I could change it to loop until headerN.xml doesn't exist perhaps?

$headerName = $this->getHeaderName($i);
$footerName = $this->getFooterName($i);
if ($this->_objZip->locateName($headerName) !== false) {
$this->_headerXMLs[$i] = $this->_objZip->getFromName($headerName);
}
if ($this->_objZip->locateName($footerName) !== false) {
$this->_footerXMLs[$i] = $this->_objZip->getFromName($footerName);
}
}

$this->_documentXML = $this->_objZip->getFromName('word/document.xml');
}

/**
* Get the name of the footer file for $index
* @param integer $index
* @return string
*/
private function getFooterName($index)
{
return sprintf('word/footer%d.xml', $index);
}

/**
* Get the name of the header file for $index
* @param integer $index
* @return string
*/
private function getHeaderName($index)
{
return sprintf('word/header%d.xml', $index);
}

/**
* Applies XSL style sheet to template's parts
*
Expand Down Expand Up @@ -97,20 +142,22 @@ public function applyXslStyleSheet(&$xslDOMDocument, $xslOptions = array(), $xsl
}

/**
* Set a Template value
* Find and replace placeholders in the given XML section.
*
* @param mixed $search
* @param string $documentPartXML
* @param string $search
* @param mixed $replace
* @param integer $limit
* @return string
*/
public function setValue($search, $replace, $limit = -1)
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
{
$pattern = '|\$\{([^\}]+)\}|U';
preg_match_all($pattern, $this->_documentXML, $matches);
preg_match_all($pattern, $documentPartXML, $matches);
foreach ($matches[0] as $value) {
$valueCleaned = preg_replace('/<[^>]+>/', '', $value);
$valueCleaned = preg_replace('/<\/[^>]+>/', '', $valueCleaned);
$this->_documentXML = str_replace($value, $valueCleaned, $this->_documentXML);
$documentPartXML = str_replace($value, $valueCleaned, $documentPartXML);
}

if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
Expand All @@ -130,16 +177,58 @@ public function setValue($search, $replace, $limit = -1)

$regExpDelim = '/';
$escapedSearch = preg_quote($search, $regExpDelim);
$this->_documentXML = preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $this->_documentXML, $limit);
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
}

/**
* Set a Template value
*
* @param mixed $search
* @param mixed $replace
* @param integer $limit
*/
public function setValue($search, $replace, $limit = -1)
{
foreach ($this->_headerXMLs as $index => $headerXML) {
$this->_headerXMLs[$index] = $this->setValueForPart($this->_headerXMLs[$index], $search, $replace, $limit);
}

$this->_documentXML = $this->setValueForPart($this->_documentXML, $search, $replace, $limit);

foreach ($this->_footerXMLs as $index => $headerXML) {
$this->_footerXMLs[$index] = $this->setValueForPart($this->_footerXMLs[$index], $search, $replace, $limit);
}
}

/**
* Find all variables in $documentPartXML
* @param string $documentPartXML
* @return string[]
*/
protected function getVariablesForPart($documentPartXML)
{
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);

return $matches[1];
}

/**
* Returns array of all variables in template
* @return string[]
*/
public function getVariables()
{
preg_match_all('/\$\{(.*?)}/i', $this->_documentXML, $matches);
return $matches[1];
$variables = $this->getVariablesForPart($this->_documentXML);

foreach ($this->_headerXMLs as $headerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($headerXML));
}

foreach ($this->_footerXMLs as $footerXML) {
$variables = array_merge($variables, $this->getVariablesForPart($footerXML));
}

return array_unique($variables);
}

/**
Expand Down Expand Up @@ -251,8 +340,16 @@ public function cloneRow($search, $numberOfClones)
*/
public function save()
{
foreach ($this->_headerXMLs as $index => $headerXML) {
$this->_objZip->addFromString($this->getHeaderName($index), $this->_headerXMLs[$index]);
}

$this->_objZip->addFromString('word/document.xml', $this->_documentXML);

foreach ($this->_footerXMLs as $index => $headerXML) {
$this->_objZip->addFromString($this->getFooterName($index), $this->_footerXMLs[$index]);
}

// Close zip file
if ($this->_objZip->close() === false) {
throw new Exception('Could not close zip file.');
Expand Down
20 changes: 20 additions & 0 deletions tests/PhpWord/Tests/TemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,24 @@ public function testCloneRow()
$this->assertEquals($expectedVar, $actualVar);
$this->assertTrue($docFound);
}

public function testVariablesCanBeReplacedInHeaderAndFooter()
{
$template = __DIR__ . "/_files/templates/header-footer.docx";
$expectedVar = array('documentContent', 'headerValue', 'footerValue');
$docName = 'header-footer-test-result.docx';

$document = new Template($template);
$actualVar = $document->getVariables();
$document->setValue('headerValue', 'Header Value');
$document->setValue('documentContent', 'Document text.');
$document->setValue('footerValue', 'Footer Value');
$document->saveAs($docName);
$docFound = file_exists($docName);
unlink($docName);

$this->assertEquals($expectedVar, $actualVar);
$this->assertTrue($docFound);

}
}
Binary file not shown.