GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 804d88...9de2aa )
by t
02:26
created

TemplateProcessor::replaceBlock()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
c 3
b 0
f 0
nc 4
nop 3
dl 0
loc 13
rs 9.9666
1
<?php
2
/**
3
 * Class TemplateProcessor
4
 *
5
 * @link https://www.icy2003.com/
6
 * @author icy2003 <[email protected]>
7
 * @copyright Copyright (c) 2017, icy2003
8
 */
9
10
namespace icy2003\php\iexts\PhpOffice\PhpWord;
11
12
use icy2003\php\I;
13
use icy2003\php\ihelpers\Html;
14
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
15
use PhpOffice\PhpWord\IOFactory;
16
use PhpOffice\PhpWord\PhpWord;
17
use PhpOffice\PhpWord\SimpleType\Jc;
18
use PhpOffice\PhpWord\SimpleType\TblWidth;
19
use PhpOffice\PhpWord\TemplateProcessor as T;
20
use icy2003\php\ihelpers\Regular;
21
22
/**
23
 * TemplateProcessor 扩展
24
 */
25
class TemplateProcessor extends T
26
{
27
28
    /**
29
     * TemplateProcessor 构造器
30
     *
31
     * @param string $documentTemplate Word 文件路径
32
     */
33
    public function __construct($documentTemplate)
34
    {
35
        parent::__construct($documentTemplate);
36
        $this->_tempDocumentSettingPart = $this->readPartWithRels($this->_getSettingName());
37
    }
38
39
    /**
40
     * 获取 Word 的设置文件名
41
     *
42
     * @return string
43
     */
44
    protected function _getSettingName()
45
    {
46
        return 'word/settings.xml';
47
    }
48
49
    /**
50
     * Word 的设置部分
51
     *
52
     * @var string
53
     */
54
    protected $_tempDocumentSettingPart = '';
55
56
    /**
57
     * Word 的主体部分
58
     *
59
     * @return string
60
     */
61
    public function getMain()
62
    {
63
        return $this->tempDocumentMainPart;
64
    }
65
66
    /**
67
     * 设置主体代码
68
     *
69
     * @param string $main
70
     *
71
     * @return static
72
     */
73
    public function setMain($main)
74
    {
75
        $this->tempDocumentMainPart = $main;
76
        return $this;
77
    }
78
79
    /**
80
     * 获取头部
81
     *
82
     * @return string
83
     */
84
    public function getHeaders()
85
    {
86
        return $this->tempDocumentHeaders;
87
    }
88
89
    /**
90
     * 获取脚部
91
     *
92
     * @return string
93
     */
94
    public function getFooters()
95
    {
96
        return $this->tempDocumentFooters;
97
    }
98
99
    /**
100
     * 获取设置
101
     *
102
     * @return string
103
     */
104
    public function getSettings()
105
    {
106
        return $this->_tempDocumentSettingPart;
107
    }
108
109
    /**
110
     * 搜索关键字
111
     *
112
     * @param string $search 待搜索的关键字
113
     * @param string $part 搜索的字符串部分,如果是 null,则搜索整个 main 内容
114
     *
115
     * @return integer 关键字位置
116
     */
117
    public function tagPos($search, $part = null)
118
    {
119
        $search = parent::ensureMacroCompleted($search);
120
        if (null === $part) {
121
            return strpos($this->tempDocumentMainPart, $search);
122
        } else {
123
            return strpos($part, $search);
124
        }
125
    }
126
127
    /**
128
     * 覆盖父类的 save 方法
129
     *
130
     * @return string
131
     */
132
    public function save()
133
    {
134
        foreach ($this->tempDocumentHeaders as $index => $xml) {
135
            $this->savePartWithRels($this->getHeaderName($index), $xml);
136
        }
137
138
        $this->savePartWithRels($this->getMainPartName(), $this->tempDocumentMainPart);
139
        $this->savePartWithRels($this->_getSettingName(), $this->_tempDocumentSettingPart);
140
141
        foreach ($this->tempDocumentFooters as $index => $xml) {
142
            $this->savePartWithRels($this->getFooterName($index), $xml);
143
        }
144
145
        $this->zipClass->addFromString($this->getDocumentContentTypesName(), $this->tempDocumentContentTypes);
146
147
        // Close zip file
148
        if (false === $this->zipClass->close()) {
149
            throw new Exception('Could not close zip file.'); // @codeCoverageIgnore
150
        }
151
152
        return $this->tempDocumentFilename;
153
    }
154
155
    /**
156
     * 获取块
157
     *
158
     * @param string $string
159
     *
160
     * @return string
161
     */
162
    private function __getBodyBlock($string)
163
    {
164
        if (preg_match('%(?i)(?<=<w:body>)[\s|\S]*?(?=</w:body>)%', $string, $matches)) {
165
            return $matches[0];
166
        } else {
167
            return '';
168
        }
169
    }
170
171
    /**
172
     * 在某部分 XML 串中,将一个 Word 模板变量替换成表格,并合并到主文档上
173
     *
174
     * @param string $documentPartXML 某段 XML 字串
175
     * @param string $var 变量名
176
     * @param array $array 只支持行列号的二维数组
177
     * @param array $mergeArray 合并单元格的数组,例如:['A1:B1', 'C1:C2']
178
     * @param array $styleArray 对应所有单元格的样式二维数组
179
     *
180
     * @return void
181
     */
182
    public function setTableFromPart($documentPartXML, $var, $array, $mergeArray = [], $styleArray = [])
183
    {
184
        if ($this->tagPos($var, $documentPartXML)) {
185
            $phpWord = new PhpWord();
186
            $section = $phpWord->addSection();
187
            /**
188
             * addTableStyle 有 bug……
189
             * @see https://github.com/PHPOffice/PHPWord/issues/629
190
             */
191
            $table = $section->addTable([
192
                'unit' => TblWidth::PERCENT,
193
                'width' => 100 * 50,
194
                'borderColor' => '000000',
195
                'borderSize' => 9,
196
                'cellMarginLeft' => 150,
197
                'cellMarginRight' => 150,
198
            ]);
199
            $cindexs = [];
200
            foreach ($array as $rowIndex => $row) {
201
                // echo $rowIndex; 1
202
                $table->addRow();
203
                $i = 0;
204
                if (empty($cindexs)) {
205
                    $cindexs = array_map(function ($cs) {
206
                        return Coordinate::columnIndexFromString($cs);
207
                    }, array_keys($row));
208
                }
209
                foreach ($row as $c => $value) {
210
                    if ($i > 0) {
211
                        $i--;
212
                        continue;
213
                    }
214
                    // echo $c; A
215
                    $colIndex = Coordinate::columnIndexFromString($c);
216
                    $array = [];
217
                    $isContinue = false;
218
                    foreach ($mergeArray as $range) {
219
                        // 1,1 : 2,2
220
                        list($rangeStart, $rangeEnd) = Coordinate::rangeBoundaries($range); // A1:B2
221
                        if ($rangeEnd[0] > $rangeStart[0]) {
222
                            if ($colIndex >= $rangeStart[0] && $colIndex <= $rangeEnd[0] && $rowIndex >= $rangeStart[1] && $rowIndex <= $rangeEnd[1]) {
223
                                $i = count(array_intersect(range($rangeStart[0], $rangeEnd[0]), $cindexs)) - 1;
224
                                $array = array_merge($array, ['gridSpan' => $i + 1]);
225
                            }
226
                        }
227
                        if ($rangeEnd[1] > $rangeStart[1]) {
228
                            if ($colIndex >= $rangeStart[0] && $colIndex <= $rangeEnd[0]) {
229
                                if ($rowIndex == $rangeStart[1]) {
230
                                    $array = array_merge($array, ['vMerge' => 'restart']);
231
                                } elseif ($rowIndex > $rangeStart[1] && $rowIndex <= $rangeEnd[1]) {
232
                                    $isContinue = true;
233
                                    $array = array_merge($array, ['vMerge' => 'continue']);
234
                                }
235
                            }
236
                        }
237
                        if (!empty($array)) {
238
                            break;
239
                        }
240
                    }
241
                    $array = array_merge($array, [
242
                        'valign' => I::get($styleArray, $rowIndex . '.' . $c . '.valign', Jc::CENTER),
243
                        'bgColor' => I::get($styleArray, $rowIndex . '.' . $c . '.bgColor'),
244
                    ]);
245
                    $alignment = I::get($styleArray, $rowIndex . '.' . $c . '.alignment', Jc::CENTER);
246
                    // 在高版本的 Word 里,不支持 JC::JUSTIFY 呢
247
                    if (JC::JUSTIFY == $alignment) {
248
                        $alignment = JC::BOTH;
249
                    }
250
                    $cellStyle = I::get($styleArray, $rowIndex . '.' . $c);
251
                    if (true === $isContinue) {
252
                        $isContinue = false;
253
                        $table->addCell(null, $array);
254
                    } else {
255
                        $table->addCell(null, $array)->addText(Html::encode($value), $cellStyle, [
256
                            'alignment' => $alignment,
257
                        ]);
258
                    }
259
                }
260
            }
261
            $objWriter = IOFactory::createWriter($phpWord);
262
            $xml = $objWriter->getWriterPart('Document')->write();
263
            $this->replaceBlock($var, $this->__getBodyBlock($xml), $documentPartXML);
264
        }
265
    }
266
267
    /**
268
     * 在整个文档里,将一个 Word 模板变量替换成表格
269
     *
270
     * @see https://github.com/PHPOffice/PHPWord/issues/1198 感谢提供思路
271
     *
272
     * @param string $var 变量名
273
     * @param array $array 只支持行列号的二维数组
274
     * @param array $mergeArray 合并单元格的数组,例如:['A1:B1', 'C1:C2']
275
     * @param array $styleArray 对应所有单元格的样式二维数组
276
     *
277
     * @return void
278
     */
279
    public function setTable($var, $array, $mergeArray = [], $styleArray = [])
280
    {
281
        $this->setTableFromPart($this->tempDocumentMainPart, $var, $array, $mergeArray, $styleArray);
282
    }
283
284
    /**
285
     * 在某部分 XML 串中,将一个 Word 模板变量替换成列表,并合并到主文档上
286
     *
287
     * @param string $documentPartXML 某段 XML 字串
288
     * @param string $var 变量名,如 `list`,在 word 里应该写:${list}${/list}
289
     * @param array $array 一维数组
290
     * @param int $depth 列表层级,从 0 开始。默认 0
291
     *
292
     * @return void
293
     */
294
    public function setListFromPart($documentPartXML, $var, $array, $depth = 0)
295
    {
296
        if ($this->tagPos($var, $documentPartXML)) {
297
            $phpWord = new PhpWord();
298
            $section = $phpWord->addSection();
299
            foreach ($array as $item) {
300
                $section->addListItem($item, $depth);
301
            }
302
            $objWriter = IOFactory::createWriter($phpWord);
303
            $xml = $objWriter->getWriterPart('Document')->write();
304
            $this->replaceBlock($var, $this->__getBodyBlock($xml), $documentPartXML);
305
        }
306
    }
307
308
    /**
309
     * 在整个文档里,将一个 Word 模板变量替换成列表
310
     *
311
     * @param string $var 变量名,如 `list`,在 word 里应该写:${list}${/list}
312
     * @param array $array 一维数组
313
     * @param int $depth 列表层级,从 0 开始。默认 0
314
     *
315
     * @todo 给列表加样式
316
     *
317
     * @return void
318
     */
319
    public function setList($var, $array, $depth = 0)
320
    {
321
        $this->setListFromPart($this->tempDocumentMainPart, $var, $array, $depth);
322
    }
323
324
    /**
325
     * 是否强制提示更新字段(用于更新目录)
326
     *
327
     * @param boolean $isUpdate
328
     *
329
     * @return void
330
     */
331
    public function setIsUpdateFields($isUpdate = true)
332
    {
333
        if (preg_match('/w:val=\"TOC\"/', $this->tempDocumentMainPart)) {
334
            $string = $isUpdate ? 'true' : 'false';
335
            $matches = null;
336
            if (preg_match('/<w:updateFields w:val=\"(true|false)\"\/>/', $this->_tempDocumentSettingPart, $matches)) {
337
                $this->_tempDocumentSettingPart = str_replace($matches[0], '<w:updateFields w:val="' . $string . '"/>', $this->_tempDocumentSettingPart);
338
            } else {
339
                $this->_tempDocumentSettingPart = str_replace('</w:settings>', '<w:updateFields w:val="' . $string . '"/></w:settings>', $this->_tempDocumentSettingPart);
340
            }
341
        }
342
    }
343
344
    /**
345
     * 替换块标签
346
     * 注:原方法因为贪婪模式可能无法正确匹配对应的块
347
     *
348
     * @param string $blockname 变量名
349
     * @param string $replacement 替换字符串
350
     * @param string $documentPartXML xml 字符串,默认整个文档的
351
     *
352
     * @return void
353
     */
354
    public function replaceBlock($blockname, $replacement, $documentPartXML = null)
355
    {
356
        null === $documentPartXML && $documentPartXML = $this->tempDocumentMainPart;
357
        // PHP7.0~7.2 会有 bug 导致匹配不到结果,例子参见 samples/php7preg_bug.php
358
        Regular::jitOff();
359
        preg_match(
360
            '/(<w:p ((?!<w:p ).)*?\${' . $blockname . '}.*?<\/w:p>)(.*?)(<w:p ((?!<w:p ).)*\${\/' . $blockname . '}.*?<\/w:p>)/is',
361
            $documentPartXML,
362
            $matches
363
        );
364
        if (isset($matches[1])) {
365
            $part = $this->setValueForPart($matches[1] . $matches[3] . $matches[4], $replacement, $documentPartXML, parent::MAXIMUM_REPLACEMENTS_DEFAULT);
366
            $this->tempDocumentMainPart = $this->setValueForPart($documentPartXML, $part, $this->tempDocumentMainPart, parent::MAXIMUM_REPLACEMENTS_DEFAULT);
367
        }
368
    }
369
370
    /**
371
     * 在某段 XML 中替换变量,并合并到主文档上
372
     *
373
     * @param string $documentPartXML 某段 XML 字串
374
     * @param string $search 待搜索的变量名
375
     * @param string $replace 替换的值
376
     * @param int $limit 替换次数,默认-1,表示替换全部
377
     *
378
     * @return void
379
     */
380
    public function setValueFromPart($documentPartXML, $search, $replace, $limit = parent::MAXIMUM_REPLACEMENTS_DEFAULT)
381
    {
382
        if ($this->tagPos($search, $documentPartXML)) {
383
            $part = $this->setValueForPart(static::ensureMacroCompleted($search), static::ensureUtf8Encoded($replace), $documentPartXML, $limit);
384
            $this->tempDocumentMainPart = $this->setValueForPart($documentPartXML, $part, $this->tempDocumentMainPart, $limit);
385
            $this->setValue($search, $replace, $limit);
386
        }
387
    }
388
389
}
390