Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix block handling in template processor #2448

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

glaszig
Copy link

@glaszig glaszig commented Aug 29, 2023

  • more relaxed but reliable regexp to detect blocks
  • support multiple blocks with same name

maybe fixes #2444, #2410
related: #1836, #1354

Checklist:

  • I have run composer run-script check --timeout=0 and no errors were reported
  • The new code is covered by unit tests (check build/coverage for coverage report)
  • I have updated the documentation to describe the changes

@glaszig glaszig force-pushed the template-processor-blocks branch 2 times, most recently from 86bf46e to 19ca448 Compare August 29, 2023 03:07
@Progi1984 Progi1984 force-pushed the master branch 3 times, most recently from 2d9f999 to e458249 Compare August 30, 2023 11:56
- more relaxed but reliable regexp to detect blocks
- support multiple blocks with same name
@coveralls
Copy link

Coverage Status

coverage: 81.467% (-0.005%) from 81.472% when pulling 50e23b2 on glaszig:template-processor-blocks into c17c4c7 on PHPOffice:master.

@glaszig
Copy link
Author

glaszig commented Sep 3, 2023

@coveralls don't be that pedantic.

@thomasb88
Copy link

Why don't you use the
findContainingXmlBlockForMacro function ?

In some way it has the same logic as your regexp, but your regexp seems complex for big files.

Whereas findContainingXmlBlockForMacro use first findMacro to find it straight, then from the position found, search for the first opening w:p.

I had reflexion about this problem, and my conclusion is not everything should be done by regexp to manage well complexity (else the regexp would use Lookahead or Lookbehind feature that are having complexity far more than O(n)).

I also customized a little bit this function, like

/**
* Clone a block.
*
* @param string $blockname
* @param int $clones How many time the block should be cloned
* @param bool $replace
* @return string|null
*/
public function cloneBlock($blockname, $clones = 1, $replace = true, $separator = 'linebreak')
{
// Separator
switch($separator){
case "pagebreak":
$objectClass = 'PhpOffice\PhpWord\Element\' . 'PageBreak';
$xml_obj = new $objectClass();
$xml_separator = $this->getXmlObject($xml_obj);
break;
case "linebreak":
$objectClass = 'PhpOffice\PhpWord\Element\' . 'TextBreak';
$xml_obj = new $objectClass();
$xml_separator = $this->getXmlObject($xml_obj);
break;
default:
$xml_separator = '';
break;
}
// Get Block to be Cloned And Generate Cloned Data
$rawClonedData = '';
$xmlBlock = null;
$where = $this->findContainingXmlBlockForMacro($blockname, 'w:p', 'Block', 1, 'regexp');
if(false !== $where){
$xmlBlock = $this->getSlice($where['start'], $where['end']);
$cloned = array_fill(0, $clones, $xmlBlock);
$rawClonedData = implode($xml_separator, $cloned);
$rawClonedData = str_replace(self::$macroBlockOpeningChars.$blockname.self::$macroBlockClosingChars, '', $rawClonedData);
$rawClonedData = str_replace(self::$macroBlockOpeningChars.self::$close_block_char.$blockname.self::$macroBlockClosingChars, '', $rawClonedData);
}
// Insert Cloned Data
if ($replace and (strlen($rawClonedData) > 0)){
$this->replaceXmlBlock($blockname, $rawClonedData, 'w:p', 'Block', 1);
}
return $where;
}

And then customized findMacro to be able to compare different algorithm;
protected function findMacro($search, $offset = 0, $macro_type = 'Block', $macro_index = 1, $search_algorithm = 'regexp')
{
$search = static::ensureMacroCompleted($search, $macro_type);
if('regexp' == $search_algorithm){
$pattern_macro = $this->regexp_delimiter.preg_quote($search, $this->regexp_delimiter).$this->regexp_delimiter.'i';
preg_match_all($pattern_macro, substr($this->tempDocumentMainPart, $offset), $matches, PREG_OFFSET_CAPTURE);
$pos = (($macro_index >= 1) and (count($matches[0]) >= $macro_index))?($offset + $matches[0][$macro_index-1][1]):-1;
} elseif('recursive' == $search_algorithm) {
$pos = ($macro_index >= 1)?($offset-1*strlen($search)):false;
for ($i=1 ; $i <= $macro_index ; $i++){
$pos = strpos($this->tempDocumentMainPart, $search, $pos+strlen($search));
if($pos === false){
break;
}
}
} else {
$pos = strpos($this->tempDocumentMainPart, $search, $offset);
}
return ($pos === false) ? -1 : $pos;
}

Off course, all of this is dependant of the fixBrokenMacros that is also modified to be able to fix merge all the w:t including tha macro without breaking style and other data.

So, sorry for my long post, but the summary is that breaking the problem in function seems to be a good way to organise the TemplateProcessor processing. What do you think ?

@fotrino
Copy link

fotrino commented Oct 10, 2023

Any update on this PR?

@kovalovme
Copy link

BUMP

@Progi1984 Progi1984 added this to the 2.0.0 milestone Nov 30, 2023
@chinmayshah24
Copy link

This doesn't always work. Didn't work for me when used wrapping over a table.

@MrLexisDev
Copy link

Any update of this fix please?

@Progi1984 Progi1984 modified the milestones: 1.3.0, 2.0.0 Aug 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Allow to Separate Variables and Blocks
8 participants