Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
74 / 74 |
|
100.00% |
17 / 17 |
CRAP | |
100.00% |
1 / 1 |
AbstractWriter | |
100.00% |
74 / 74 |
|
100.00% |
17 / 17 |
45 | |
100.00% |
1 / 1 |
getPhpWord | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setPhpWord | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getWriterPart | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
isUseDiskCaching | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setUseDiskCaching | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getDiskCachingDirectory | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTempDir | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setTempDir | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getTempFile | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
cleanupTempFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
clearTempDir | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getZipArchive | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
4 | |||
openFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
writeFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
addFilesToPackage | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
addFileToPackage | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
5 | |||
deleteDir | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * This file is part of PHPWord - A pure PHP library for reading and writing |
4 | * word processing documents. |
5 | * |
6 | * PHPWord is free software distributed under the terms of the GNU Lesser |
7 | * General Public License version 3 as published by the Free Software Foundation. |
8 | * |
9 | * For the full copyright and license information, please read the LICENSE |
10 | * file that was distributed with this source code. For the full list of |
11 | * contributors, visit https://github.com/PHPOffice/PHPWord/contributors. |
12 | * |
13 | * @see https://github.com/PHPOffice/PHPWord |
14 | * |
15 | * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 |
16 | */ |
17 | |
18 | namespace PhpOffice\PhpWord\Writer; |
19 | |
20 | use PhpOffice\PhpWord\Exception\CopyFileException; |
21 | use PhpOffice\PhpWord\Exception\Exception; |
22 | use PhpOffice\PhpWord\PhpWord; |
23 | use PhpOffice\PhpWord\Settings; |
24 | use PhpOffice\PhpWord\Shared\ZipArchive; |
25 | |
26 | /** |
27 | * Abstract writer class. |
28 | * |
29 | * @since 0.10.0 |
30 | */ |
31 | abstract class AbstractWriter implements WriterInterface |
32 | { |
33 | /** |
34 | * PHPWord object. |
35 | * |
36 | * @var \PhpOffice\PhpWord\PhpWord |
37 | */ |
38 | protected $phpWord; |
39 | |
40 | /** |
41 | * Part name and file name pairs. |
42 | * |
43 | * @var array |
44 | */ |
45 | protected $parts = []; |
46 | |
47 | /** |
48 | * Individual writers. |
49 | * |
50 | * @var array |
51 | */ |
52 | protected $writerParts = []; |
53 | |
54 | /** |
55 | * Paths to store media files. |
56 | * |
57 | * @var array |
58 | */ |
59 | protected $mediaPaths = ['image' => '', 'object' => '']; |
60 | |
61 | /** |
62 | * Use disk caching. |
63 | * |
64 | * @var bool |
65 | */ |
66 | private $useDiskCaching = false; |
67 | |
68 | /** |
69 | * Disk caching directory. |
70 | * |
71 | * @var string |
72 | */ |
73 | private $diskCachingDirectory = './'; |
74 | |
75 | /** |
76 | * Temporary directory. |
77 | * |
78 | * @var string |
79 | */ |
80 | private $tempDir = ''; |
81 | |
82 | /** |
83 | * Original file name. |
84 | * |
85 | * @var string |
86 | */ |
87 | private $originalFilename; |
88 | |
89 | /** |
90 | * Temporary file name. |
91 | * |
92 | * @var string |
93 | */ |
94 | private $tempFilename; |
95 | |
96 | /** |
97 | * Get PhpWord object. |
98 | * |
99 | * @return \PhpOffice\PhpWord\PhpWord |
100 | */ |
101 | public function getPhpWord() |
102 | { |
103 | if (null !== $this->phpWord) { |
104 | return $this->phpWord; |
105 | } |
106 | |
107 | throw new Exception('No PhpWord assigned.'); |
108 | } |
109 | |
110 | /** |
111 | * Set PhpWord object. |
112 | * |
113 | * @param \PhpOffice\PhpWord\PhpWord |
114 | * |
115 | * @return self |
116 | */ |
117 | public function setPhpWord(?PhpWord $phpWord = null) |
118 | { |
119 | $this->phpWord = $phpWord; |
120 | |
121 | return $this; |
122 | } |
123 | |
124 | /** |
125 | * Get writer part. |
126 | * |
127 | * @param string $partName Writer part name |
128 | * |
129 | * @return mixed |
130 | */ |
131 | public function getWriterPart($partName = '') |
132 | { |
133 | if ($partName != '' && isset($this->writerParts[strtolower($partName)])) { |
134 | return $this->writerParts[strtolower($partName)]; |
135 | } |
136 | |
137 | return null; |
138 | } |
139 | |
140 | /** |
141 | * Get use disk caching status. |
142 | * |
143 | * @return bool |
144 | */ |
145 | public function isUseDiskCaching() |
146 | { |
147 | return $this->useDiskCaching; |
148 | } |
149 | |
150 | /** |
151 | * Set use disk caching status. |
152 | * |
153 | * @param bool $value |
154 | * @param string $directory |
155 | * |
156 | * @return self |
157 | */ |
158 | public function setUseDiskCaching($value = false, $directory = null) |
159 | { |
160 | $this->useDiskCaching = $value; |
161 | |
162 | if (null !== $directory) { |
163 | if (is_dir($directory)) { |
164 | $this->diskCachingDirectory = $directory; |
165 | } else { |
166 | throw new Exception("Directory does not exist: $directory"); |
167 | } |
168 | } |
169 | |
170 | return $this; |
171 | } |
172 | |
173 | /** |
174 | * Get disk caching directory. |
175 | * |
176 | * @return string |
177 | */ |
178 | public function getDiskCachingDirectory() |
179 | { |
180 | return $this->diskCachingDirectory; |
181 | } |
182 | |
183 | /** |
184 | * Get temporary directory. |
185 | * |
186 | * @return string |
187 | */ |
188 | public function getTempDir() |
189 | { |
190 | return $this->tempDir; |
191 | } |
192 | |
193 | /** |
194 | * Set temporary directory. |
195 | * |
196 | * @param string $value |
197 | * |
198 | * @return self |
199 | */ |
200 | public function setTempDir($value) |
201 | { |
202 | if (!is_dir($value)) { |
203 | mkdir($value); |
204 | } |
205 | $this->tempDir = $value; |
206 | |
207 | return $this; |
208 | } |
209 | |
210 | /** |
211 | * Get temporary file name. |
212 | * |
213 | * If $filename is php://output or php://stdout, make it a temporary file |
214 | * |
215 | * @param string $filename |
216 | * |
217 | * @return string |
218 | */ |
219 | protected function getTempFile($filename) |
220 | { |
221 | // Temporary directory |
222 | $this->setTempDir(Settings::getTempDir() . uniqid('/PHPWordWriter_', true) . '/'); |
223 | |
224 | // Temporary file |
225 | $this->originalFilename = $filename; |
226 | if (strpos(strtolower($filename), 'php://') === 0) { |
227 | $filename = tempnam(Settings::getTempDir(), 'PhpWord'); |
228 | if (false === $filename) { |
229 | $filename = $this->originalFilename; // @codeCoverageIgnore |
230 | } // @codeCoverageIgnore |
231 | } |
232 | $this->tempFilename = $filename; |
233 | |
234 | return $this->tempFilename; |
235 | } |
236 | |
237 | /** |
238 | * Cleanup temporary file. |
239 | */ |
240 | protected function cleanupTempFile(): void |
241 | { |
242 | if ($this->originalFilename != $this->tempFilename) { |
243 | // @codeCoverageIgnoreStart |
244 | // Can't find any test case. Uncomment when found. |
245 | if (false === copy($this->tempFilename, $this->originalFilename)) { |
246 | throw new CopyFileException($this->tempFilename, $this->originalFilename); |
247 | } |
248 | // @codeCoverageIgnoreEnd |
249 | @unlink($this->tempFilename); |
250 | } |
251 | |
252 | $this->clearTempDir(); |
253 | } |
254 | |
255 | /** |
256 | * Clear temporary directory. |
257 | */ |
258 | protected function clearTempDir(): void |
259 | { |
260 | if (is_dir($this->tempDir)) { |
261 | $this->deleteDir($this->tempDir); |
262 | } |
263 | } |
264 | |
265 | /** |
266 | * Get ZipArchive object. |
267 | * |
268 | * @param string $filename |
269 | * |
270 | * @return \PhpOffice\PhpWord\Shared\ZipArchive |
271 | */ |
272 | protected function getZipArchive($filename) |
273 | { |
274 | // Remove any existing file |
275 | if (file_exists($filename)) { |
276 | unlink($filename); |
277 | } |
278 | |
279 | // Try opening the ZIP file |
280 | $zip = new ZipArchive(); |
281 | |
282 | // @codeCoverageIgnoreStart |
283 | // Can't find any test case. Uncomment when found. |
284 | if ($zip->open($filename, ZipArchive::OVERWRITE) !== true) { |
285 | if ($zip->open($filename, ZipArchive::CREATE) !== true) { |
286 | throw new \Exception("Could not open '{$filename}' for writing."); |
287 | } |
288 | } |
289 | // @codeCoverageIgnoreEnd |
290 | |
291 | return $zip; |
292 | } |
293 | |
294 | /** |
295 | * Open file for writing. |
296 | * |
297 | * @since 0.11.0 |
298 | * |
299 | * @param string $filename |
300 | * |
301 | * @return resource |
302 | */ |
303 | protected function openFile($filename) |
304 | { |
305 | $filename = $this->getTempFile($filename); |
306 | $fileHandle = fopen($filename, 'wb'); |
307 | // @codeCoverageIgnoreStart |
308 | // Can't find any test case. Uncomment when found. |
309 | if ($fileHandle === false) { |
310 | throw new \Exception("Could not open '{$filename}' for writing."); |
311 | } |
312 | // @codeCoverageIgnoreEnd |
313 | |
314 | return $fileHandle; |
315 | } |
316 | |
317 | /** |
318 | * Write content to file. |
319 | * |
320 | * @since 0.11.0 |
321 | * |
322 | * @param resource $fileHandle |
323 | * @param string $content |
324 | */ |
325 | protected function writeFile($fileHandle, $content): void |
326 | { |
327 | fwrite($fileHandle, $content); |
328 | fclose($fileHandle); |
329 | $this->cleanupTempFile(); |
330 | } |
331 | |
332 | /** |
333 | * Add files to package. |
334 | * |
335 | * @param mixed $elements |
336 | */ |
337 | protected function addFilesToPackage(ZipArchive $zip, $elements): void |
338 | { |
339 | foreach ($elements as $element) { |
340 | $type = $element['type']; // image|object|link |
341 | |
342 | // Skip nonregistered types and set target |
343 | if (!isset($this->mediaPaths[$type])) { |
344 | continue; |
345 | } |
346 | $target = $this->mediaPaths[$type] . $element['target']; |
347 | |
348 | // Retrive GD image content or get local media |
349 | if (isset($element['isMemImage']) && $element['isMemImage']) { |
350 | $imageContents = $element['imageString']; |
351 | $zip->addFromString($target, $imageContents); |
352 | } else { |
353 | $this->addFileToPackage($zip, $element['source'], $target); |
354 | } |
355 | } |
356 | } |
357 | |
358 | /** |
359 | * Add file to package. |
360 | * |
361 | * Get the actual source from an archive image. |
362 | * |
363 | * @param \PhpOffice\PhpWord\Shared\ZipArchive $zipPackage |
364 | * @param string $source |
365 | * @param string $target |
366 | */ |
367 | protected function addFileToPackage($zipPackage, $source, $target): void |
368 | { |
369 | $isArchive = strpos($source, 'zip://') !== false; |
370 | $actualSource = null; |
371 | if ($isArchive) { |
372 | $source = substr($source, 6); |
373 | [$zipFilename, $imageFilename] = explode('#', $source); |
374 | |
375 | $zip = new ZipArchive(); |
376 | if ($zip->open($zipFilename) !== false) { |
377 | if ($zip->locateName($imageFilename)) { |
378 | $zip->extractTo($this->getTempDir(), $imageFilename); |
379 | $actualSource = $this->getTempDir() . DIRECTORY_SEPARATOR . $imageFilename; |
380 | } |
381 | } |
382 | $zip->close(); |
383 | } else { |
384 | $actualSource = $source; |
385 | } |
386 | |
387 | if (null !== $actualSource) { |
388 | $zipPackage->addFile($actualSource, $target); |
389 | } |
390 | } |
391 | |
392 | /** |
393 | * Delete directory. |
394 | * |
395 | * @param string $dir |
396 | */ |
397 | private function deleteDir($dir): void |
398 | { |
399 | foreach (scandir($dir) as $file) { |
400 | if ($file === '.' || $file === '..') { |
401 | continue; |
402 | } elseif (is_file($dir . '/' . $file)) { |
403 | unlink($dir . '/' . $file); |
404 | } elseif (is_dir($dir . '/' . $file)) { |
405 | $this->deleteDir($dir . '/' . $file); |
406 | } |
407 | } |
408 | |
409 | rmdir($dir); |
410 | } |
411 | } |