Passed
Push — develop ( 8c8a50...38b54f )
by nguereza
01:42
created

DocxTemplate::copyTemplateFileToTempDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * Platine Docx template
5
 *
6
 * Platine Docx template is the lightweight library to manipulate the content
7
 * of .docx files
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Docx template
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file DocxTemplate.php
34
 *
35
 *  The Docx Template main class.
36
 *
37
 *  @package    Platine\DocxTemplate
38
 *  @author Platine Developers Team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   http://www.iacademy.cf
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\DocxTemplate;
49
50
use FilesystemIterator;
51
use Platine\DocxTemplate\Convertor\NullConvertor;
52
use Platine\DocxTemplate\Exception\DocxTemplateException;
53
use Platine\DocxTemplate\Renderer\NullRenderer;
54
use Platine\Filesystem\Filesystem;
55
use RecursiveDirectoryIterator;
56
use RecursiveIteratorIterator;
57
use ZipArchive;
58
59
/**
60
 * @class DocxTemplate
61
 * @package Platine\DocxTemplate
62
 */
63
class DocxTemplate
64
{
65
66
    /**
67
     * The convertor instance to use
68
     * @var DocxConvertorInterface
69
     */
70
    protected DocxConvertorInterface $convertor;
71
72
    /**
73
     * The template renderer instance
74
     * @var DocxTemplateRendererInterface
75
     */
76
    protected DocxTemplateRendererInterface $renderer;
77
78
    /**
79
     * The file instance to use
80
     * @var Filesystem
81
     */
82
    protected Filesystem $filesystem;
83
84
    /**
85
     * The data to use to replace template variables
86
     * @var array<string, mixed>
87
     */
88
    protected array $data = [];
89
90
    /**
91
     * The document template file
92
     * @var string
93
     */
94
    protected string $templateFile;
95
96
    /**
97
     * The template temporary file path
98
     * @var string
99
     */
100
    protected string $templateTempFile;
101
102
    /**
103
     * Output template file after renderer
104
     * @var string
105
     */
106
    protected string $outputTemplateFile;
107
108
    /**
109
     * The file path after conversion
110
     * @var string
111
     */
112
    protected string $convertionFile = '';
113
114
    /**
115
     * The list of files inside document template
116
     * @var array<string>
117
     */
118
    protected array $docxFileList = [];
119
120
    /**
121
     * The list of files pattern to use
122
     * @var array<string>
123
     */
124
    protected array $fileListToProcess = [
125
        'word/document.xml',
126
        'word/endnotes.xml',
127
        'word/footer*.xml',
128
        'word/footnotes.xml',
129
        'word/header*.xml',
130
    ];
131
132
    /**
133
     * Temporary directory to use to extract template
134
     * file into
135
     * @var string
136
     */
137
    protected string $tempDir;
138
139
    /**
140
     * The directory to use to extract template into
141
     * @var string
142
     */
143
    protected string $templateExtractDir;
144
145
    /**
146
     * Create new instance
147
     * @param Filesystem $filesystem
148
     * @param DocxTemplateRendererInterface|null $renderer
149
     * @param DocxConvertorInterface|null $convertor
150
     */
151
    public function __construct(
152
        Filesystem $filesystem,
153
        ?DocxTemplateRendererInterface $renderer = null,
154
        ?DocxConvertorInterface $convertor = null
155
    ) {
156
        $this->filesystem = $filesystem;
157
        $this->convertor = $convertor ?? new NullConvertor();
158
        $this->renderer = $renderer ?? new NullRenderer();
159
        $this->tempDir = sys_get_temp_dir();
160
    }
161
162
    /**
163
     * Return the convertor
164
     * @return DocxConvertorInterface
165
     */
166
    public function getConvertor(): DocxConvertorInterface
167
    {
168
        return $this->convertor;
169
    }
170
171
    /**
172
     * Set the convertor
173
     * @param DocxConvertorInterface $convertor
174
     * @return $this
175
     */
176
    public function setConvertor(DocxConvertorInterface $convertor): self
177
    {
178
        $this->convertor = $convertor;
179
        return $this;
180
    }
181
182
    /**
183
     * Return the renderer
184
     * @return DocxTemplateRendererInterface
185
     */
186
    public function getRenderer(): DocxTemplateRendererInterface
187
    {
188
        return $this->renderer;
189
    }
190
191
    /**
192
     * Set the renderer
193
     * @param DocxTemplateRendererInterface $renderer
194
     * @return $this
195
     */
196
    public function setRenderer(DocxTemplateRendererInterface $renderer): self
197
    {
198
        $this->renderer = $renderer;
199
        return $this;
200
    }
201
202
    /**
203
     * Return the output template file after processed
204
     * @return string
205
     */
206
    public function getOutputTemplateFile(): string
207
    {
208
        return $this->outputTemplateFile;
209
    }
210
211
    /**
212
     * Return the conversion file path or content
213
     * @return string
214
     */
215
    public function getConvertionFile(): string
216
    {
217
        return $this->convertionFile;
218
    }
219
220
    /**
221
     * Return the data
222
     * @return array<string, mixed>
223
     */
224
    public function getData(): array
225
    {
226
        return $this->data;
227
    }
228
229
    /**
230
     * Set the data
231
     * @param array<string, mixed> $data
232
     * @return $this
233
     */
234
    public function setData(array $data): self
235
    {
236
        $this->data = $data;
237
        return $this;
238
    }
239
240
    /**
241
     * Set template file
242
     * @param string $templateFile
243
     * @return $this
244
     */
245
    public function setTemplateFile(string $templateFile): self
246
    {
247
        $this->templateFile = $templateFile;
248
        return $this;
249
    }
250
251
    /**
252
     * Set the template directory
253
     * @param string $tempDir
254
     * @return $this
255
     */
256
    public function setTempDir(string $tempDir): self
257
    {
258
        $this->tempDir = $tempDir;
259
        return $this;
260
    }
261
262
263
    /**
264
     * Process the document template
265
     * @return void
266
     */
267
    public function process(): void
268
    {
269
        $this->copyTemplateFileToTempDir();
270
        $this->extractDocumentFiles();
271
        foreach ($this->docxFileList as $file) {
272
            $this->renderTemplate($file);
273
        }
274
275
        if (empty($this->outputTemplateFile)) {
276
            $this->outputTemplateFile = $this->tempDir . '/output_' . basename($this->templateFile);
277
        }
278
279
        $zip = new ZipArchive();
280
        $create = $zip->open(
281
            $this->outputTemplateFile,
282
            ZipArchive::CREATE | ZipArchive::OVERWRITE
283
        );
284
        if ($create === true) {
285
             // Create recursive directory iterator
286
            $files = new RecursiveIteratorIterator(
287
                new RecursiveDirectoryIterator($this->templateExtractDir, FilesystemIterator::SKIP_DOTS),
288
                RecursiveIteratorIterator::LEAVES_ONLY
289
            );
290
291
            foreach ($files as $name => $file) {
292
                $name = substr($name, strlen($this->templateExtractDir . '/'));
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type null and true; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

292
                $name = substr(/** @scrutinizer ignore-type */ $name, strlen($this->templateExtractDir . '/'));
Loading history...
293
                $zip->addFile($file->getRealPath(), $name);
294
            }
295
            $zip->close();
296
        } else {
297
            throw new DocxTemplateException(sprintf(
298
                'Can not open file [%s]',
299
                $this->outputTemplateFile
300
            ));
301
        }
302
    }
303
304
    /**
305
     * Convert the output template to another format like PDF, HTML
306
     * @return string
307
     */
308
    public function convert(): string
309
    {
310
        return $this->convertor->convert($this->outputTemplateFile);
311
    }
312
313
    /**
314
     * Copy the template file to temporary directory
315
     * @return void
316
     */
317
    protected function copyTemplateFileToTempDir(): void
318
    {
319
        $templateFile = $this->filesystem->file($this->templateFile);
320
        $this->templateTempFile = $templateFile->copyTo($this->tempDir)->getPath();
321
322
        $tempDir = $this->filesystem->directory($this->tempDir);
323
        $extractDir = $tempDir->create('tmp_extract_' . uniqid());
324
325
        $this->templateExtractDir = $extractDir->getPath();
326
    }
327
328
    /**
329
     * Extract template document file
330
     * @return void
331
     */
332
    protected function extractDocumentFiles(): void
333
    {
334
        $fileList = [];
335
        $zip = new ZipArchive();
336
        $open = $zip->open($this->templateTempFile);
337
        if ($open === true) {
338
            if (!$zip->extractTo($this->templateExtractDir)) {
339
                throw new DocxTemplateException(sprintf(
340
                    'Can not extract file [%s]',
341
                    $this->templateTempFile
342
                ));
343
            }
344
            $total = $zip->numFiles;
345
            for ($i = 0; $i < $total; $i++) {
346
                $stat = $zip->statIndex($i);
347
                if($stat !== false ){
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
Coding Style introduced by
Expected 0 spaces before closing bracket; 1 found
Loading history...
348
                    foreach ($this->fileListToProcess as $fileToProcess) {
349
                        if (fnmatch($fileToProcess, $stat['name'])) {
350
                            $fileList[] = $stat['name'];
351
                        }
352
                    }
353
                }
354
            }
355
            $zip->close();
356
            $this->docxFileList = $fileList;
357
        } else {
358
            throw new DocxTemplateException(sprintf(
359
                'Can not open file [%s]',
360
                $this->templateTempFile
361
            ));
362
        }
363
    }
364
365
    /**
366
     * Render the template (replace variables)
367
     * @param string $file
368
     * @return void
369
     */
370
    protected function renderTemplate(string $file): void
371
    {
372
        $templateFile = $this->filesystem->file($this->templateExtractDir . '/' . $file);
373
        if ($templateFile->exists()) {
374
            $content = $templateFile->read();
375
            $renderContent = $this->renderer->render($content, $this->data);
376
            $templateFile->write($renderContent);
377
        }
378
    }
379
}
380