3v4l.org

run code in 200+ php & hhvm versions
Bugs & Features
<?php /** * This file is part of PHPWord - A pure PHP library for reading and writing * word processing documents. * * PHPWord is free software distributed under the terms of the GNU Lesser * General Public License version 3 as published by the Free Software Foundation. * * For the full copyright and license information, please read the LICENSE * file that was distributed with this source code. For the full list of * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. * * @link https://github.com/PHPOffice/PHPWord * @copyright 2010-2014 PHPWord contributors * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ namespace helpers; use PhpOffice\PhpWord\Exception\CopyFileException; use PhpOffice\PhpWord\Exception\CreateTemporaryFileException; use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Shared\String; use PhpOffice\PhpWord\Shared\ZipArchive; use PhpOffice\PhpWord\TemplateProcessor; use PhpOffice\PhpWord\Settings; class CustomTemplateProcessor { /** * ZipArchive object. * * @var mixed */ private $zipClass; /** * @var string Temporary document filename (with path). */ private $temporaryDocumentFilename; /** * Content of main document part (in XML format) of the temporary document. * * @var string */ private $temporaryDocumentMainPart; /** * Content of headers (in XML format) of the temporary document. * * @var string[] */ private $temporaryDocumentHeaders = array(); /** * Content of footers (in XML format) of the temporary document. * * @var string[] */ private $temporaryDocumentFooters = array(); /** * @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception. * * @param string $documentTemplate The fully qualified template filename. * @throws \PhpOffice\PhpWord\Exception\CreateTemporaryFileException * @throws \PhpOffice\PhpWord\Exception\CopyFileException */ public function __construct($documentTemplate) { // Temporary document filename initialization $this->temporaryDocumentFilename = tempnam(Settings::getTempDir(), 'PhpWord'); if (false === $this->temporaryDocumentFilename) { throw new CreateTemporaryFileException(); } // Template file cloning if (false === copy($documentTemplate, $this->temporaryDocumentFilename)) { throw new CopyFileException($documentTemplate, $this->temporaryDocumentFilename); } // Temporary document content extraction $this->zipClass = new ZipArchive(); $this->zipClass->open($this->temporaryDocumentFilename); $index = 1; while ($this->zipClass->locateName($this->getHeaderName($index)) !== false) { $content = $this->zipClass->getFromName($this->getHeaderName($index)); $this->temporaryDocumentHeaders[$index] = self::cleanParameters($content); $index++; } $index = 1; while ($this->zipClass->locateName($this->getFooterName($index)) !== false) { $content = $this->zipClass->getFromName($this->getFooterName($index)); $this->temporaryDocumentFooters[$index] = self::cleanParameters($content); $index++; } $content = $this->zipClass->getFromName('word/document.xml'); $this->temporaryDocumentMainPart = self::cleanParameters($content); } public static function cleanParameters($content) { $pattern = '|\$\{([^\}]+)\}|U'; $values = []; $replacements = []; preg_match_all($pattern, $content, $matches); foreach ($matches[0] as $value) { $valueCleaned = strip_tags($value); $valueCleaned = preg_replace('/\s+/U', '', $valueCleaned); $values[] = $value; $replacements[] = $valueCleaned; } return str_replace($values, $replacements, $content); } /** * Applies XSL style sheet to template's parts. * * @param \DOMDocument $xslDOMDocument * @param array $xslOptions * @param string $xslOptionsURI * @return void * @throws \PhpOffice\PhpWord\Exception\Exception */ public function applyXslStyleSheet($xslDOMDocument, $xslOptions = array(), $xslOptionsURI = '') { $xsltProcessor = new \XSLTProcessor(); $xsltProcessor->importStylesheet($xslDOMDocument); if (false === $xsltProcessor->setParameter($xslOptionsURI, $xslOptions)) { throw new Exception('Could not set values for the given XSL style sheet parameters.'); } $xmlDOMDocument = new \DOMDocument(); if (false === $xmlDOMDocument->loadXML($this->temporaryDocumentMainPart)) { throw new Exception('Could not load XML from the given template.'); } $xmlTransformed = $xsltProcessor->transformToXml($xmlDOMDocument); if (false === $xmlTransformed) { throw new Exception('Could not transform the given XML document.'); } $this->temporaryDocumentMainPart = $xmlTransformed; } /** * @param mixed $search * @param mixed $replace * @param integer $limit * @return void */ public function setValue($search, $replace, $limit = -1) { if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') { $search = '${' . $search . '}'; } if (!String::isUTF8($replace)) { $replace = utf8_encode($replace); } foreach ($this->temporaryDocumentHeaders as $index => $headerXML) { $this->temporaryDocumentHeaders[$index] = $this->setValueForPart($this->temporaryDocumentHeaders[$index], $search, $replace, $limit); } $this->temporaryDocumentMainPart = $this->setValueForPart($this->temporaryDocumentMainPart, $search, $replace, $limit); foreach ($this->temporaryDocumentFooters as $index => $headerXML) { $this->temporaryDocumentFooters[$index] = $this->setValueForPart($this->temporaryDocumentFooters[$index], $search, $replace, $limit); } } /** * Returns array of all variables in template. * * @return string[] */ public function getVariables() { $variables = $this->getVariablesForPart($this->temporaryDocumentMainPart); foreach ($this->temporaryDocumentHeaders as $headerXML) { $variables = array_merge($variables, $this->getVariablesForPart($headerXML)); } foreach ($this->temporaryDocumentFooters as $footerXML) { $variables = array_merge($variables, $this->getVariablesForPart($footerXML)); } return array_unique($variables); } /** * Clone a table row in a template document. * * @param string $search * @param integer $numberOfClones * @return void * @throws \PhpOffice\PhpWord\Exception\Exception */ public function cloneRow($search, $numberOfClones) { if (substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') { $search = '${' . $search . '}'; } $tagPos = strpos($this->temporaryDocumentMainPart, $search); if (!$tagPos) { throw new Exception("Can not clone row, template variable not found or variable contains markup."); } $rowStart = $this->findRowStart($tagPos); $rowEnd = $this->findRowEnd($tagPos); $xmlRow = $this->getSlice($rowStart, $rowEnd); // $cellStart = $this->findCellStart($tagPos); // $cellEnd = $this->findCellEnd($tagPos); // $xmlCell = $this->getSlice($cellStart, $cellEnd); // Check if there's a cell spanning multiple rows. if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) { // $extraRowStart = $rowEnd; $extraRowEnd = $rowEnd; while (true) { $extraRowStart = $this->findRowStart($extraRowEnd + 1); $extraRowEnd = $this->findRowEnd($extraRowEnd + 1); // If extraRowEnd is lower then 7, there was no next row found. if ($extraRowEnd < 7) { break; } // If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row. $tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd); if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) && !preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) { break; } // This row was a spanned row, update $rowEnd and search for the next row. $rowEnd = $extraRowEnd; } $xmlRow = $this->getSlice($rowStart, $rowEnd); } $result = $this->getSlice(0, $rowStart); for ($i = 1; $i <= $numberOfClones; $i++) { $result .= preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlRow); } $result .= $this->getSlice($rowEnd); $this->temporaryDocumentMainPart = $result; } /** * Clone a block. * * @param string $blockname * @param integer $clones * @param boolean $replace * @return string|null */ public function cloneBlock($blockname, $clones = 1, $replace = true) { $xmlBlock = null; preg_match( '/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is', $this->temporaryDocumentMainPart, $matches ); if (isset($matches[3])) { $xmlBlock = $matches[3]; $cloned = array(); for ($i = 1; $i <= $clones; $i++) { $cloned[] = $xmlBlock; } if ($replace) { $this->temporaryDocumentMainPart = str_replace( $matches[2] . $matches[3] . $matches[4], implode('', $cloned), $this->temporaryDocumentMainPart ); } } return $xmlBlock; } /** * Replace a block. * * @param string $blockname * @param string $replacement * @return void */ public function replaceBlock($blockname, $replacement) { preg_match( '/(<\?xml.*)(<w:p.*>\${' . $blockname . '}<\/w:.*?p>)(.*)(<w:p.*\${\/' . $blockname . '}<\/w:.*?p>)/is', $this->temporaryDocumentMainPart, $matches ); if (isset($matches[3])) { $this->temporaryDocumentMainPart = str_replace( $matches[2] . $matches[3] . $matches[4], $replacement, $this->temporaryDocumentMainPart ); } } /** * Delete a block of text. * * @param string $blockname * @return void */ public function deleteBlock($blockname) { $this->replaceBlock($blockname, ''); } /** * Saves the result document. * * @return string * @throws \PhpOffice\PhpWord\Exception\Exception */ public function save() { foreach ($this->temporaryDocumentHeaders as $index => $headerXML) { $this->zipClass->addFromString($this->getHeaderName($index), $this->temporaryDocumentHeaders[$index]); } $this->zipClass->addFromString('word/document.xml', $this->temporaryDocumentMainPart); foreach ($this->temporaryDocumentFooters as $index => $headerXML) { $this->zipClass->addFromString($this->getFooterName($index), $this->temporaryDocumentFooters[$index]); } // Close zip file if (false === $this->zipClass->close()) { throw new Exception('Could not close zip file.'); } return $this->temporaryDocumentFilename; } /** * Saves the result document to the user defined file. * * @since 0.8.0 * * @param string $fileName * @return void */ public function saveAs($fileName) { $tempFileName = $this->save(); if (file_exists($fileName)) { unlink($fileName); } rename($tempFileName, $fileName); } /** * Find and replace placeholders in the given XML section. * * @param string $documentPartXML * @param string $search * @param string $replace * @param integer $limit * @return string */ protected function setValueForPart($documentPartXML, $search, $replace, $limit) { return str_replace($search, $replace, $documentPartXML, $limit); } /** * Find all variables in $documentPartXML. * * @param string $documentPartXML * @return string[] */ protected function getVariablesForPart($documentPartXML) { preg_match_all('/\$\{(.*?)}/i', $documentPartXML, $matches); return $matches[1]; } /** * 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); } /** * Find the start position of the nearest table row before $offset. * * @param integer $offset * @return integer * @throws \PhpOffice\PhpWord\Exception\Exception */ private function findRowStart($offset) { $rowStart = strrpos($this->temporaryDocumentMainPart, '<w:tr ', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1)); if (!$rowStart) { $rowStart = strrpos($this->temporaryDocumentMainPart, '<w:tr>', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1)); } if (!$rowStart) { throw new Exception('Can not find the start position of the row to clone.'); } return $rowStart; } /** * Find the end position of the nearest table row after $offset. * * @param integer $offset * @return integer */ private function findRowEnd($offset) { return strpos($this->temporaryDocumentMainPart, '</w:tr>', $offset) + 7; } private function findCellStart($offset) { $cellStart = strrpos($this->temporaryDocumentMainPart, '<w:tc ', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1)); if (!$cellStart) { $cellStart = strrpos($this->temporaryDocumentMainPart, '<w:tc>', ((strlen($this->temporaryDocumentMainPart) - $offset) * -1)); } if (!$cellStart) { throw new Exception('Can not find the start position of the cell.'); } return $cellStart; } private function findCellEnd($offset) { return strpos($this->temporaryDocumentMainPart, '</w:tc>', $offset) + 7; } /** * Get a slice of a string. * * @param integer $startPosition * @param integer $endPosition * @return string */ private function getSlice($startPosition, $endPosition = 0) { if (!$endPosition) { $endPosition = strlen($this->temporaryDocumentMainPart); } return substr($this->temporaryDocumentMainPart, $startPosition, ($endPosition - $startPosition)); } }
Output for 7.0.0 - 7.1.0
Fatal error: Cannot use PhpOffice\PhpWord\Shared\String as String because 'String' is a special class name in /in/Lq5KQ on line 23
Process exited with code 255.
Output for 5.4.0 - 5.6.29