Passed
Push — develop ( 38b54f...493714 )
by nguereza
01:37
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], error code [%s]',
299
                $this->outputTemplateFile,
300
                $create
301
            ));
302
        }
303
304
        $this->cleanTempData();
305
    }
306
307
    /**
308
     * Convert the output template to another format like PDF, HTML
309
     * @return $this
310
     */
311
    public function convert(): self
312
    {
313
        $this->convertionFile = $this->convertor
314
                               ->convert($this->outputTemplateFile);
315
        return $this;
316
    }
317
318
    /**
319
     * Copy the template file to temporary directory
320
     * @return void
321
     */
322
    protected function copyTemplateFileToTempDir(): void
323
    {
324
        $templateFile = $this->filesystem->file($this->templateFile);
325
        $this->templateTempFile = $templateFile->copyTo($this->tempDir)->getPath();
326
327
        $tempDir = $this->filesystem->directory($this->tempDir);
328
        $extractDir = $tempDir->create('tmp_extract_' . uniqid());
329
330
        $this->templateExtractDir = $extractDir->getPath();
331
    }
332
333
    /**
334
     * Extract template document file
335
     * @return void
336
     */
337
    protected function extractDocumentFiles(): void
338
    {
339
        $fileList = [];
340
        $zip = new ZipArchive();
341
        $open = $zip->open($this->templateTempFile);
342
        if ($open === true) {
343
            if (!$zip->extractTo($this->templateExtractDir)) {
344
                throw new DocxTemplateException(sprintf(
345
                    'Can not extract file [%s]',
346
                    $this->templateTempFile
347
                ));
348
            }
349
            $total = $zip->numFiles;
350
            for ($i = 0; $i < $total; $i++) {
351
                $stat = $zip->statIndex($i);
352
                if ($stat !== false) {
353
                    foreach ($this->fileListToProcess as $fileToProcess) {
354
                        if (fnmatch($fileToProcess, $stat['name'])) {
355
                            $fileList[] = $stat['name'];
356
                        }
357
                    }
358
                }
359
            }
360
            $zip->close();
361
            $this->docxFileList = $fileList;
362
        } else {
363
            throw new DocxTemplateException(sprintf(
364
                'Can not open file [%s], error code [%s]',
365
                $this->templateTempFile,
366
                $open
367
            ));
368
        }
369
    }
370
371
    /**
372
     * Render the template (replace variables)
373
     * @param string $file
374
     * @return void
375
     */
376
    protected function renderTemplate(string $file): void
377
    {
378
        $templateFile = $this->filesystem->file($this->templateExtractDir . '/' . $file);
379
        if ($templateFile->exists()) {
380
            $content = $templateFile->read();
381
            $renderContent = $this->renderer->render($content, $this->data);
382
            $templateFile->write($renderContent);
383
        }
384
    }
385
386
    /**
387
     * Clean the temporary data after processed
388
     * @return void
389
     */
390
    protected function cleanTempData(): void
391
    {
392
        $this->filesystem->directory($this->templateExtractDir)->delete();
393
        $this->filesystem->file($this->templateTempFile)->delete();
394
    }
395
}
396