PHPWord复制块cloneBlock及删除块deleteBlock无效的解决办法

蛰伏已久 蛰伏已久 2019-08-16

最近在使用PHPWord进行动态文档的生成,遇到一个非常奇怪的问题,在phpword功能测试的时候,cloneBlock和deleteBlock都好用,可是在使用正式模板的时候(模板比较大),总是不好用,无法复制块,花了一下午的时间检查这个问题,终于解决,如果对你也有帮助,请打赏我喝杯咖啡压压惊哈哈~~

先上结论

通过查看源码发现,在进行cloneBlock的时候,需要通过preg_match进行正则匹配,找到要复制的块内容。但是由于文档较大,导致preg_match无法匹配出要复制的块。

原来PHP有个递归次数限制pcre.recursion_limit,无限次的递归容易消耗所有进程的可用堆栈,最后导致PHP崩溃。所以默认“pcre.backtrack_limit ”的值默认只设了100000。当我们word中内容过大时候,就导致超出这个递归次数限制,导致匹配出无结果,也就没有办法实现复制块的操作。

解决办法:修改“pcre.backtrack_limit ”的值默认,在代码前面加一句,搞定! 

ini_set('pcre.backtrack_limit', 999999999);


问题定位过程

先看下cloneBlock的源码,实际比较简单,就是通过正则找出要匹配的块,然后复制几次。

public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVariables = false, $variableReplacements = null)
    {

        $xmlBlock = null;
        //匹配要复制的块
        preg_match(
            '/(<\?xml.*)(<w:p\b.*>\$\{' . $blockname . '\}<\/w:.*?p>)(.*)(<w:p\b.*\$\{\/' . $blockname . '\}<\/w:.*?p>)/is',
            $this->tempDocumentMainPart,
            $matches
        );

        if (isset($matches[3])) {
            $xmlBlock = $matches[3];
            if ($indexVariables) {
                $cloned = $this->indexClonedVariables($clones, $xmlBlock);
            } elseif ($variableReplacements !== null && is_array($variableReplacements)) {
                $cloned = $this->replaceClonedVariables($variableReplacements, $xmlBlock);
            } else {
                //复制几块就循环几次
                $cloned = array();
                for ($i = 1; $i <= $clones; $i++) {
                    $cloned[] = $xmlBlock;
                }
            }

            if ($replace) {
                //默认会把之前设置的块删除,只保留复制后的内容
                $this->tempDocumentMainPart = str_replace(
                    $matches[2] . $matches[3] . $matches[4],
                    implode('', $cloned),
                    $this->tempDocumentMainPart
                );
            }
        }


        return $xmlBlock;
    }

首先我是将匹配结果  $matches打印出来,发现匹配结果为空,当时是怀疑,是不是作者正则表达式写的有问题,于是将$this->tempDocumentMainPart打印出来,和表达式进行肉眼对比,发现并没有错。

public function cloneBlock($blockname, $clones = 1, $replace = true, $indexVariables = false, $variableReplacements = null)
    {

        $xmlBlock = null;
        preg_match(
            '/(<\?xml.*)(<w:p\b.*>\$\{' . $blockname . '\}<\/w:.*?p>)(.*)(<w:p\b.*\$\{\/' . $blockname . '\}<\/w:.*?p>)/is',
            $this->tempDocumentMainPart,
            $matches
        );
        //把匹配结果打印出来
        var_dump($matches)

将$this->tempDocumentMainPart打印出来的时候,记得要在浏览器调试器network中查看真是response,而不是浏览器屏幕显示的字符串,因为默认浏览器把response编码为html显示在浏览器了,看到的并不是真实内容。


既然正则表达式没有错,而匹配结果不正确,那就应该是php出了问题,考虑$this->tempDocumentMainPart内容较大,于是百度搜索 “preg_match字符串过大”,果然发现有人遇到这样的问题,于是也就定位到了问题,解决就很简单了,一行代码。

整个调试过程花了一下午,真是快崩溃了,如果解决了你的问题,不给我打赏喝杯咖啡,你好意思吗~哈哈哈哈哈

PHPOffice使用文档整理

PHPWord使用方法整理https://www.shanhuxueyuan.com/news/detail/124.html

PHPExcel使用方法整理https://www.shanhuxueyuan.com/news/detail/119.html

PHPPresentation(PPT)使用方法整理https://www.shanhuxueyuan.com/news/detail/123.html

分享到

点赞(2)