Update 2 Template processor setValue() improvements #614
This commit is contained in:
parent
873d41a872
commit
b446a23b61
@ -1,5 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file is part of PHPWord - A pure PHP library for reading and writing
|
* This file is part of PHPWord - A pure PHP library for reading and writing
|
||||||
* word processing documents.
|
* word processing documents.
|
||||||
@ -26,7 +25,7 @@ use PhpOffice\PhpWord\Shared\ZipArchive;
|
|||||||
|
|
||||||
class TemplateProcessor
|
class TemplateProcessor
|
||||||
{
|
{
|
||||||
const MAXIMUM_REPLACEMENTS_DEFAULT = - 1;
|
const MAXIMUM_REPLACEMENTS_DEFAULT = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ZipArchive object.
|
* ZipArchive object.
|
||||||
@ -69,8 +68,8 @@ class TemplateProcessor
|
|||||||
* @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException
|
* @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException
|
||||||
* @throws \PhpOffice\PhpWord\Exception\CopyFileException
|
* @throws \PhpOffice\PhpWord\Exception\CopyFileException
|
||||||
*/
|
*/
|
||||||
public function __construct($documentTemplate) {
|
public function __construct($documentTemplate)
|
||||||
|
{
|
||||||
// Temporary document filename initialization
|
// Temporary document filename initialization
|
||||||
$this->tempDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
|
$this->tempDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord');
|
||||||
if (false === $this->tempDocumentFilename) {
|
if (false === $this->tempDocumentFilename) {
|
||||||
@ -87,12 +86,16 @@ class TemplateProcessor
|
|||||||
$this->zipClass->open($this->tempDocumentFilename);
|
$this->zipClass->open($this->tempDocumentFilename);
|
||||||
$index = 1;
|
$index = 1;
|
||||||
while (false !== $this->zipClass->locateName($this->getHeaderName($index))) {
|
while (false !== $this->zipClass->locateName($this->getHeaderName($index))) {
|
||||||
$this->tempDocumentHeaders[$index] = $this->fixBrokenMacros($this->zipClass->getFromName($this->getHeaderName($index)));
|
$this->tempDocumentHeaders[$index] = $this->fixBrokenMacros(
|
||||||
|
$this->zipClass->getFromName($this->getHeaderName($index))
|
||||||
|
);
|
||||||
$index++;
|
$index++;
|
||||||
}
|
}
|
||||||
$index = 1;
|
$index = 1;
|
||||||
while (false !== $this->zipClass->locateName($this->getFooterName($index))) {
|
while (false !== $this->zipClass->locateName($this->getFooterName($index))) {
|
||||||
$this->tempDocumentFooters[$index] = $this->fixBrokenMacros($this->zipClass->getFromName($this->getFooterName($index)));
|
$this->tempDocumentFooters[$index] = $this->fixBrokenMacros(
|
||||||
|
$this->zipClass->getFromName($this->getFooterName($index))
|
||||||
|
);
|
||||||
$index++;
|
$index++;
|
||||||
}
|
}
|
||||||
$this->tempDocumentMainPart = $this->fixBrokenMacros($this->zipClass->getFromName('word/document.xml'));
|
$this->tempDocumentMainPart = $this->fixBrokenMacros($this->zipClass->getFromName('word/document.xml'));
|
||||||
@ -109,7 +112,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||||
*/
|
*/
|
||||||
public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslOptionsURI = '') {
|
public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslOptionsURI = '')
|
||||||
|
{
|
||||||
$xsltProcessor = new \XSLTProcessor();
|
$xsltProcessor = new \XSLTProcessor();
|
||||||
|
|
||||||
$xsltProcessor->importStylesheet($xslDOMDocument);
|
$xsltProcessor->importStylesheet($xslDOMDocument);
|
||||||
@ -138,7 +142,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function setValue($macro, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT) {
|
public function setValue($macro, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT)
|
||||||
|
{
|
||||||
if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
|
if (substr($macro, 0, 2) !== '${' && substr($macro, -1) !== '}') {
|
||||||
$macro = '${' . $macro . '}';
|
$macro = '${' . $macro . '}';
|
||||||
}
|
}
|
||||||
@ -163,7 +168,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
public function getVariables() {
|
public function getVariables()
|
||||||
|
{
|
||||||
$variables = $this->getVariablesForPart($this->tempDocumentMainPart);
|
$variables = $this->getVariablesForPart($this->tempDocumentMainPart);
|
||||||
|
|
||||||
foreach ($this->tempDocumentHeaders as $headerXML) {
|
foreach ($this->tempDocumentHeaders as $headerXML) {
|
||||||
@ -187,7 +193,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||||
*/
|
*/
|
||||||
public function cloneRow($search, $numberOfClones) {
|
public function cloneRow($search, $numberOfClones)
|
||||||
|
{
|
||||||
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
|
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
|
||||||
$search = '${' . $search . '}';
|
$search = '${' . $search . '}';
|
||||||
}
|
}
|
||||||
@ -203,7 +210,6 @@ class TemplateProcessor
|
|||||||
|
|
||||||
// Check if there's a cell spanning multiple rows.
|
// Check if there's a cell spanning multiple rows.
|
||||||
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
|
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
|
||||||
|
|
||||||
// $extraRowStart = $rowEnd;
|
// $extraRowStart = $rowEnd;
|
||||||
$extraRowEnd = $rowEnd;
|
$extraRowEnd = $rowEnd;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -217,10 +223,10 @@ class TemplateProcessor
|
|||||||
|
|
||||||
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
|
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
|
||||||
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
|
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
|
||||||
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) && !preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
|
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
|
||||||
|
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This row was a spanned row, update $rowEnd and search for the next row.
|
// This row was a spanned row, update $rowEnd and search for the next row.
|
||||||
$rowEnd = $extraRowEnd;
|
$rowEnd = $extraRowEnd;
|
||||||
}
|
}
|
||||||
@ -229,9 +235,9 @@ class TemplateProcessor
|
|||||||
|
|
||||||
$result = $this->getSlice(0, $rowStart);
|
$result = $this->getSlice(0, $rowStart);
|
||||||
for ($i = 1; $i <= $numberOfClones; $i++) {
|
for ($i = 1; $i <= $numberOfClones; $i++) {
|
||||||
$result.= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow);
|
$result .= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow);
|
||||||
}
|
}
|
||||||
$result.= $this->getSlice($rowEnd);
|
$result .= $this->getSlice($rowEnd);
|
||||||
|
|
||||||
$this->tempDocumentMainPart = $result;
|
$this->tempDocumentMainPart = $result;
|
||||||
}
|
}
|
||||||
@ -245,9 +251,14 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function cloneBlock($blockname, $clones = 1, $replace = true) {
|
public function cloneBlock($blockname, $clones = 1, $replace = true)
|
||||||
|
{
|
||||||
$xmlBlock = null;
|
$xmlBlock = null;
|
||||||
preg_match('/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is', $this->tempDocumentMainPart, $matches);
|
preg_match(
|
||||||
|
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||||
|
$this->tempDocumentMainPart,
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
|
||||||
if (isset($matches[3])) {
|
if (isset($matches[3])) {
|
||||||
$xmlBlock = $matches[3];
|
$xmlBlock = $matches[3];
|
||||||
@ -257,7 +268,11 @@ class TemplateProcessor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($replace) {
|
if ($replace) {
|
||||||
$this->tempDocumentMainPart = str_replace($matches[2] . $matches[3] . $matches[4], implode('', $cloned), $this->tempDocumentMainPart);
|
$this->tempDocumentMainPart = str_replace(
|
||||||
|
$matches[2] . $matches[3] . $matches[4],
|
||||||
|
implode('', $cloned),
|
||||||
|
$this->tempDocumentMainPart
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,11 +287,20 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function replaceBlock($blockname, $replacement) {
|
public function replaceBlock($blockname, $replacement)
|
||||||
preg_match('/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is', $this->tempDocumentMainPart, $matches);
|
{
|
||||||
|
preg_match(
|
||||||
|
'/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is',
|
||||||
|
$this->tempDocumentMainPart,
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
|
||||||
if (isset($matches[3])) {
|
if (isset($matches[3])) {
|
||||||
$this->tempDocumentMainPart = str_replace($matches[2] . $matches[3] . $matches[4], $replacement, $this->tempDocumentMainPart);
|
$this->tempDocumentMainPart = str_replace(
|
||||||
|
$matches[2] . $matches[3] . $matches[4],
|
||||||
|
$replacement,
|
||||||
|
$this->tempDocumentMainPart
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +311,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function deleteBlock($blockname) {
|
public function deleteBlock($blockname)
|
||||||
|
{
|
||||||
$this->replaceBlock($blockname, '');
|
$this->replaceBlock($blockname, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +323,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||||
*/
|
*/
|
||||||
public function save() {
|
public function save()
|
||||||
|
{
|
||||||
foreach ($this->tempDocumentHeaders as $index => $headerXML) {
|
foreach ($this->tempDocumentHeaders as $index => $headerXML) {
|
||||||
$this->zipClass->addFromString($this->getHeaderName($index), $this->tempDocumentHeaders[$index]);
|
$this->zipClass->addFromString($this->getHeaderName($index), $this->tempDocumentHeaders[$index]);
|
||||||
}
|
}
|
||||||
@ -326,7 +352,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function saveAs($fileName) {
|
public function saveAs($fileName)
|
||||||
|
{
|
||||||
$tempFileName = $this->save();
|
$tempFileName = $this->save();
|
||||||
|
|
||||||
if (file_exists($fileName)) {
|
if (file_exists($fileName)) {
|
||||||
@ -338,7 +365,7 @@ class TemplateProcessor
|
|||||||
* As a result, user cannot open the file directly getting "Access denied" message.
|
* As a result, user cannot open the file directly getting "Access denied" message.
|
||||||
*
|
*
|
||||||
* @see https://github.com/PHPOffice/PHPWord/issues/532
|
* @see https://github.com/PHPOffice/PHPWord/issues/532
|
||||||
*/
|
*/
|
||||||
copy($tempFileName, $fileName);
|
copy($tempFileName, $fileName);
|
||||||
unlink($tempFileName);
|
unlink($tempFileName);
|
||||||
}
|
}
|
||||||
@ -353,12 +380,17 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function fixBrokenMacros($documentPart) {
|
protected function fixBrokenMacros($documentPart)
|
||||||
|
{
|
||||||
$fixedDocumentPart = $documentPart;
|
$fixedDocumentPart = $documentPart;
|
||||||
|
|
||||||
$fixedDocumentPart = preg_replace_callback('|\$\{([^\}]+)\}|U', function ($match) {
|
$fixedDocumentPart = preg_replace_callback(
|
||||||
return strip_tags($match[0]);
|
'|\$\{([^\}]+)\}|U',
|
||||||
}, $fixedDocumentPart);
|
function ($match) {
|
||||||
|
return strip_tags($match[0]);
|
||||||
|
},
|
||||||
|
$fixedDocumentPart
|
||||||
|
);
|
||||||
|
|
||||||
return $fixedDocumentPart;
|
return $fixedDocumentPart;
|
||||||
}
|
}
|
||||||
@ -367,19 +399,18 @@ class TemplateProcessor
|
|||||||
* Find and replace macros in the given XML section.
|
* Find and replace macros in the given XML section.
|
||||||
*
|
*
|
||||||
* @param string $documentPartXML
|
* @param string $documentPartXML
|
||||||
* @param string $searchP
|
* @param string $search
|
||||||
* @param string $replace
|
* @param string $replace
|
||||||
* @param integer $limit
|
* @param integer $limit
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function setValueForPart($documentPartXML, $search, $replace, $limit) {
|
protected function setValueForPart($documentPartXML, $search, $replace, $limit)
|
||||||
|
{
|
||||||
// Note: we can't use the same function for both cases here, because of performance considerations.
|
// Note: we can't use the same function for both cases here, because of performance considerations.
|
||||||
if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) {
|
if (self::MAXIMUM_REPLACEMENTS_DEFAULT === $limit) {
|
||||||
return str_replace($search, $replace, $documentPartXML);
|
return str_replace($search, $replace, $documentPartXML);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$regExpDelim = '/';
|
$regExpDelim = '/';
|
||||||
$escapedSearch = preg_quote($search, $regExpDelim);
|
$escapedSearch = preg_quote($search, $regExpDelim);
|
||||||
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
|
return preg_replace("{$regExpDelim}{$escapedSearch}{$regExpDelim}u", $replace, $documentPartXML, $limit);
|
||||||
@ -393,7 +424,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
protected function getVariablesForPart($documentPartXML) {
|
protected function getVariablesForPart($documentPartXML)
|
||||||
|
{
|
||||||
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
|
preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches);
|
||||||
|
|
||||||
return $matches[1];
|
return $matches[1];
|
||||||
@ -406,7 +438,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function getFooterName($index) {
|
protected function getFooterName($index)
|
||||||
|
{
|
||||||
return sprintf('word/footer%d.xml', $index);
|
return sprintf('word/footer%d.xml', $index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +450,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function getHeaderName($index) {
|
protected function getHeaderName($index)
|
||||||
|
{
|
||||||
return sprintf('word/header%d.xml', $index);
|
return sprintf('word/header%d.xml', $index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +464,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @throws \PhpOffice\PhpWord\Exception\Exception
|
* @throws \PhpOffice\PhpWord\Exception\Exception
|
||||||
*/
|
*/
|
||||||
protected function findRowStart($offset) {
|
protected function findRowStart($offset)
|
||||||
|
{
|
||||||
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr ', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
|
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tr ', ((strlen($this->tempDocumentMainPart) - $offset) * -1));
|
||||||
|
|
||||||
if (!$rowStart) {
|
if (!$rowStart) {
|
||||||
@ -450,7 +485,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
protected function findRowEnd($offset) {
|
protected function findRowEnd($offset)
|
||||||
|
{
|
||||||
return strpos($this->tempDocumentMainPart, '</w:tr>', $offset) + 7;
|
return strpos($this->tempDocumentMainPart, '</w:tr>', $offset) + 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,7 +498,8 @@ class TemplateProcessor
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function getSlice($startPosition, $endPosition = 0) {
|
protected function getSlice($startPosition, $endPosition = 0)
|
||||||
|
{
|
||||||
if (!$endPosition) {
|
if (!$endPosition) {
|
||||||
$endPosition = strlen($this->tempDocumentMainPart);
|
$endPosition = strlen($this->tempDocumentMainPart);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user