Skip to content

Commit 763de34

Browse files
author
Dave Gudgeon
committed
Add support for document headers and footers to Template class.
1 parent e405bf2 commit 763de34

File tree

3 files changed

+125
-8
lines changed

3 files changed

+125
-8
lines changed

src/PhpWord/Template.php

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,26 @@ class Template
3232
*/
3333
private $_tempFileName;
3434

35+
/**
36+
* Document header XML
37+
*
38+
* @var string[]
39+
*/
40+
private $_headerXMLs = array();
41+
3542
/**
3643
* Document XML
3744
*
3845
* @var string
3946
*/
4047
private $_documentXML;
4148

49+
/**
50+
* Document footer XML
51+
*
52+
* @var string[]
53+
*/
54+
private $_footerXMLs = array();
4255

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

78+
// Find and load up to three headers and footers
79+
for ($i = 1; $i <= 3; $i++) {
80+
$headerName = $this->getHeaderName($i);
81+
$footerName = $this->getFooterName($i);
82+
if ($this->_objZip->locateName($headerName) !== false) {
83+
$this->_headerXMLs[$i] = $this->_objZip->getFromName($headerName);
84+
}
85+
if ($this->_objZip->locateName($footerName) !== false) {
86+
$this->_footerXMLs[$i] = $this->_objZip->getFromName($footerName);
87+
}
88+
}
89+
6590
$this->_documentXML = $this->_objZip->getFromName('word/document.xml');
6691
}
6792

93+
/**
94+
* Get the name of the footer file for $index
95+
* @param integer $index
96+
* @return string
97+
*/
98+
private function getFooterName($index)
99+
{
100+
return sprintf('word/footer%d.xml', $index);
101+
}
102+
103+
/**
104+
* Get the name of the header file for $index
105+
* @param integer $index
106+
* @return string
107+
*/
108+
private function getHeaderName($index)
109+
{
110+
return sprintf('word/header%d.xml', $index);
111+
}
112+
68113
/**
69114
* Applies XSL style sheet to template's parts
70115
*
@@ -97,20 +142,22 @@ public function applyXslStyleSheet(&$xslDOMDocument, $xslOptions = array(), $xsl
97142
}
98143

99144
/**
100-
* Set a Template value
145+
* Find and replace placeholders in the given XML section.
101146
*
102-
* @param mixed $search
147+
* @param string $documentPartXML
148+
* @param string $search
103149
* @param mixed $replace
104150
* @param integer $limit
151+
* @return string
105152
*/
106-
public function setValue($search, $replace, $limit = -1)
153+
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
107154
{
108155
$pattern = '|\$\{([^\}]+)\}|U';
109-
preg_match_all($pattern, $this->_documentXML, $matches);
156+
preg_match_all($pattern, $documentPartXML, $matches);
110157
foreach ($matches[0] as $value) {
111158
$valueCleaned = preg_replace('/<[^>]+>/', '', $value);
112159
$valueCleaned = preg_replace('/<\/[^>]+>/', '', $valueCleaned);
113-
$this->_documentXML = str_replace($value, $valueCleaned, $this->_documentXML);
160+
$documentPartXML = str_replace($value, $valueCleaned, $documentPartXML);
114161
}
115162

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

131178
$regExpDelim = '/';
132179
$escapedSearch = preg_quote($search, $regExpDelim);
133-
$this->_documentXML = preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $this->_documentXML, $limit);
180+
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
181+
}
182+
183+
/**
184+
* Set a Template value
185+
*
186+
* @param mixed $search
187+
* @param mixed $replace
188+
* @param integer $limit
189+
*/
190+
public function setValue($search, $replace, $limit = -1)
191+
{
192+
foreach ($this->_headerXMLs as $index => $headerXML) {
193+
$this->_headerXMLs[$index] = $this->setValueForPart($this->_headerXMLs[$index], $search, $replace, $limit);
194+
}
195+
196+
$this->_documentXML = $this->setValueForPart($this->_documentXML, $search, $replace, $limit);
197+
198+
foreach ($this->_footerXMLs as $index => $headerXML) {
199+
$this->_footerXMLs[$index] = $this->setValueForPart($this->_footerXMLs[$index], $search, $replace, $limit);
200+
}
201+
}
202+
203+
/**
204+
* Find all variables in $documentPartXML
205+
* @param string $documentPartXML
206+
* @return string[]
207+
*/
208+
protected function getVariablesForPart($documentPartXML)
209+
{
210+
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
211+
212+
return $matches[1];
134213
}
135214

136215
/**
137216
* Returns array of all variables in template
217+
* @return string[]
138218
*/
139219
public function getVariables()
140220
{
141-
preg_match_all('/\$\{(.*?)}/i', $this->_documentXML, $matches);
142-
return $matches[1];
221+
$variables = $this->getVariablesForPart($this->_documentXML);
222+
223+
foreach ($this->_headerXMLs as $headerXML) {
224+
$variables = array_merge($variables, $this->getVariablesForPart($headerXML));
225+
}
226+
227+
foreach ($this->_footerXMLs as $footerXML) {
228+
$variables = array_merge($variables, $this->getVariablesForPart($footerXML));
229+
}
230+
231+
return array_unique($variables);
143232
}
144233

145234
/**
@@ -251,8 +340,16 @@ public function cloneRow($search, $numberOfClones)
251340
*/
252341
public function save()
253342
{
343+
foreach ($this->_headerXMLs as $index => $headerXML) {
344+
$this->_objZip->addFromString($this->getHeaderName($index), $this->_headerXMLs[$index]);
345+
}
346+
254347
$this->_objZip->addFromString('word/document.xml', $this->_documentXML);
255348

349+
foreach ($this->_footerXMLs as $index => $headerXML) {
350+
$this->_objZip->addFromString($this->getFooterName($index), $this->_footerXMLs[$index]);
351+
}
352+
256353
// Close zip file
257354
if ($this->_objZip->close() === false) {
258355
throw new Exception('Could not close zip file.');

tests/PhpWord/Tests/TemplateTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,24 @@ public function testCloneRow()
156156
$this->assertEquals($expectedVar, $actualVar);
157157
$this->assertTrue($docFound);
158158
}
159+
160+
public function testVariablesCanBeReplacedInHeaderAndFooter()
161+
{
162+
$template = __DIR__ . "/_files/templates/header-footer.docx";
163+
$expectedVar = array('documentContent', 'headerValue', 'footerValue');
164+
$docName = 'header-footer-test-result.docx';
165+
166+
$document = new Template($template);
167+
$actualVar = $document->getVariables();
168+
$document->setValue('headerValue', 'Header Value');
169+
$document->setValue('documentContent', 'Document text.');
170+
$document->setValue('footerValue', 'Footer Value');
171+
$document->saveAs($docName);
172+
$docFound = file_exists($docName);
173+
unlink($docName);
174+
175+
$this->assertEquals($expectedVar, $actualVar);
176+
$this->assertTrue($docFound);
177+
178+
}
159179
}
4.39 KB
Binary file not shown.

0 commit comments

Comments
 (0)