Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
93.10% |
27 / 29 |
CRAP | |
98.10% |
155 / 158 |
Dir | |
0.00% |
0 / 1 |
|
93.10% |
27 / 29 |
101 | |
98.10% |
155 / 158 |
__construct | |
100.00% |
1 / 1 |
10 | |
100.00% |
20 / 20 |
|||
count | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getIterator | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
setAbsolute | |
100.00% |
1 / 1 |
3 | |
100.00% |
4 / 4 |
|||
setRelative | |
100.00% |
1 / 1 |
3 | |
100.00% |
4 / 4 |
|||
setRecursive | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
setFilesOnly | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
isAbsolute | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
isRelative | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
isRecursive | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
isFilesOnly | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getPath | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getFiles | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getTree | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
copyTo | |
100.00% |
1 / 1 |
6 | |
100.00% |
13 / 13 |
|||
fileExists | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
deleteFile | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
emptyDir | |
100.00% |
1 / 1 |
8 | |
100.00% |
13 / 13 |
|||
__get | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
__isset | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
__set | |
0.00% |
0 / 1 |
1.12 | |
50.00% |
1 / 2 |
|||
__unset | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
offsetExists | |
100.00% |
1 / 1 |
3 | |
100.00% |
3 / 3 |
|||
offsetGet | |
100.00% |
1 / 1 |
2 | |
100.00% |
1 / 1 |
|||
offsetSet | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
offsetUnset | |
0.00% |
0 / 1 |
7.18 | |
84.62% |
11 / 13 |
|||
traverse | |
100.00% |
1 / 1 |
18 | |
100.00% |
27 / 27 |
|||
traverseRecursively | |
100.00% |
1 / 1 |
19 | |
100.00% |
27 / 27 |
|||
buildTree | |
100.00% |
1 / 1 |
4 | |
100.00% |
10 / 10 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2021 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Dir; |
15 | |
16 | /** |
17 | * Directory class |
18 | * |
19 | * @category Pop |
20 | * @package Pop\Dir |
21 | * @author Nick Sagona, III <dev@nolainteractive.com> |
22 | * @copyright Copyright (c) 2009-2021 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 3.2.0 |
25 | */ |
26 | class Dir implements \ArrayAccess, \Countable, \IteratorAggregate |
27 | { |
28 | |
29 | /** |
30 | * The directory path |
31 | * @var string |
32 | */ |
33 | protected $path = null; |
34 | |
35 | /** |
36 | * The files within the directory |
37 | * @var array |
38 | */ |
39 | protected $files = []; |
40 | |
41 | /** |
42 | * The nested tree map of the directory and its files |
43 | * @var array |
44 | */ |
45 | protected $tree = []; |
46 | |
47 | /** |
48 | * Flag to store the absolute path. |
49 | * @var boolean |
50 | */ |
51 | protected $absolute = false; |
52 | |
53 | /** |
54 | * Flag to store the relative path. |
55 | * @var boolean |
56 | */ |
57 | protected $relative = false; |
58 | |
59 | /** |
60 | * Flag to dig recursively. |
61 | * @var boolean |
62 | */ |
63 | protected $recursive = false; |
64 | |
65 | /** |
66 | * Flag to include only files and no directories |
67 | * @var boolean |
68 | */ |
69 | protected $filesOnly = false; |
70 | |
71 | /** |
72 | * Constructor |
73 | * |
74 | * Instantiate a directory object |
75 | * |
76 | * @param string $dir |
77 | * @param array $options |
78 | * @throws Exception |
79 | */ |
80 | public function __construct($dir, array $options = []) |
81 | { |
82 | // Set the directory path. |
83 | if ((strpos($dir, "\\") !== false) && (DIRECTORY_SEPARATOR != "\\")) { |
84 | $this->path = str_replace("\\", '/', $dir); |
85 | } else { |
86 | $this->path = $dir; |
87 | } |
88 | |
89 | // Check to see if the directory exists. |
90 | if (!file_exists($this->path)) { |
91 | throw new Exception("Error: The directory '" . $this->path . "' does not exist"); |
92 | } |
93 | |
94 | // Trim the trailing slash. |
95 | if (strrpos($this->path, DIRECTORY_SEPARATOR) == (strlen($this->path) - 1)) { |
96 | $this->path = substr($this->path, 0, -1); |
97 | } |
98 | |
99 | if (isset($options['absolute'])) { |
100 | $this->setAbsolute($options['absolute']); |
101 | } |
102 | if (isset($options['relative'])) { |
103 | $this->setRelative($options['relative']); |
104 | } |
105 | if (isset($options['recursive'])) { |
106 | $this->setRecursive($options['recursive']); |
107 | } |
108 | if (isset($options['filesOnly'])) { |
109 | $this->setFilesOnly($options['filesOnly']); |
110 | } |
111 | |
112 | $this->tree[realpath($this->path)] = $this->buildTree(new \DirectoryIterator($this->path)); |
113 | |
114 | if ($this->recursive) { |
115 | $this->traverseRecursively(); |
116 | } else { |
117 | $this->traverse(); |
118 | } |
119 | } |
120 | |
121 | /** |
122 | * Method to get the count of files in the directory |
123 | * |
124 | * @return int |
125 | */ |
126 | public function count() |
127 | { |
128 | return count($this->files); |
129 | } |
130 | |
131 | /** |
132 | * Method to iterate over the files |
133 | * |
134 | * @return \ArrayIterator |
135 | */ |
136 | public function getIterator() |
137 | { |
138 | return new \ArrayIterator($this->files); |
139 | } |
140 | |
141 | /** |
142 | * Set absolute |
143 | * |
144 | * @param boolean $absolute |
145 | * @return Dir |
146 | */ |
147 | public function setAbsolute($absolute) |
148 | { |
149 | $this->absolute = (bool)$absolute; |
150 | if (($this->absolute) && ($this->isRelative())) { |
151 | $this->setRelative(false); |
152 | } |
153 | return $this; |
154 | } |
155 | |
156 | /** |
157 | * Set relative |
158 | * |
159 | * @param boolean $relative |
160 | * @return Dir |
161 | */ |
162 | public function setRelative($relative) |
163 | { |
164 | $this->relative = (bool)$relative; |
165 | if (($this->relative) && ($this->isAbsolute())) { |
166 | $this->setAbsolute(false); |
167 | } |
168 | return $this; |
169 | } |
170 | |
171 | /** |
172 | * Set recursive |
173 | * |
174 | * @param boolean $recursive |
175 | * @return Dir |
176 | */ |
177 | public function setRecursive($recursive) |
178 | { |
179 | $this->recursive = (bool)$recursive; |
180 | return $this; |
181 | } |
182 | |
183 | /** |
184 | * Set files only |
185 | * |
186 | * @param boolean $filesOnly |
187 | * @return Dir |
188 | */ |
189 | public function setFilesOnly($filesOnly) |
190 | { |
191 | $this->filesOnly = (bool)$filesOnly; |
192 | return $this; |
193 | } |
194 | |
195 | /** |
196 | * Is absolute |
197 | * |
198 | * @return boolean |
199 | */ |
200 | public function isAbsolute() |
201 | { |
202 | return $this->absolute; |
203 | } |
204 | |
205 | /** |
206 | * Is relative |
207 | * |
208 | * @return boolean |
209 | */ |
210 | public function isRelative() |
211 | { |
212 | return $this->relative; |
213 | } |
214 | |
215 | /** |
216 | * Is recursive |
217 | * |
218 | * @return boolean |
219 | */ |
220 | public function isRecursive() |
221 | { |
222 | return $this->recursive; |
223 | } |
224 | |
225 | /** |
226 | * Is files only |
227 | * |
228 | * @return boolean |
229 | */ |
230 | public function isFilesOnly() |
231 | { |
232 | return $this->filesOnly; |
233 | } |
234 | |
235 | /** |
236 | * Get the path |
237 | * |
238 | * @return string |
239 | */ |
240 | public function getPath() |
241 | { |
242 | return $this->path; |
243 | } |
244 | |
245 | /** |
246 | * Get the files |
247 | * |
248 | * @return array |
249 | */ |
250 | public function getFiles() |
251 | { |
252 | return $this->files; |
253 | } |
254 | |
255 | /** |
256 | * Get the tree |
257 | * |
258 | * @return array |
259 | */ |
260 | public function getTree() |
261 | { |
262 | return $this->tree; |
263 | } |
264 | |
265 | /** |
266 | * Copy an entire directory recursively to another destination directory |
267 | * |
268 | * @param string $destination |
269 | * @param boolean $full |
270 | * @return void |
271 | */ |
272 | public function copyTo($destination, $full = true) |
273 | { |
274 | if ($full) { |
275 | if (strpos($this->path, DIRECTORY_SEPARATOR) !== false) { |
276 | $folder = substr($this->path, (strrpos($this->path, DIRECTORY_SEPARATOR) + 1)); |
277 | } |
278 | |
279 | if (!file_exists($destination . DIRECTORY_SEPARATOR . $folder)) { |
280 | mkdir($destination . DIRECTORY_SEPARATOR . $folder); |
281 | } |
282 | $destination = $destination . DIRECTORY_SEPARATOR . $folder; |
283 | } |
284 | |
285 | foreach ( |
286 | $iterator = new \RecursiveIteratorIterator( |
287 | new \RecursiveDirectoryIterator($this->path, \RecursiveDirectoryIterator::SKIP_DOTS), |
288 | \RecursiveIteratorIterator::SELF_FIRST) as $item |
289 | ) { |
290 | if ($item->isDir()) { |
291 | mkdir($destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName()); |
292 | } else { |
293 | copy($item, $destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName()); |
294 | } |
295 | } |
296 | } |
297 | |
298 | /** |
299 | * File exists |
300 | * |
301 | * @param string $file |
302 | * @return boolean |
303 | */ |
304 | public function fileExists($file) |
305 | { |
306 | return $this->offsetExists($file); |
307 | } |
308 | |
309 | /** |
310 | * Delete a file |
311 | * |
312 | * @param string $file |
313 | * @throws Exception |
314 | * @return void |
315 | */ |
316 | public function deleteFile($file) |
317 | { |
318 | $this->offsetUnset($file); |
319 | } |
320 | |
321 | /** |
322 | * Empty an entire directory |
323 | * |
324 | * @param boolean $remove |
325 | * @param string $path |
326 | * @throws Exception |
327 | * @return void |
328 | */ |
329 | public function emptyDir($remove = false, $path = null) |
330 | { |
331 | if (null === $path) { |
332 | $path = $this->path; |
333 | } |
334 | |
335 | // Get a directory handle. |
336 | if (!($dh = @opendir($path))) { |
337 | throw new Exception('Error: Unable to open the directory path "' . $path . '"'); |
338 | } |
339 | |
340 | // Recursively dig through the directory, deleting files where applicable. |
341 | while (false !== ($obj = readdir($dh))) { |
342 | if ($obj == '.' || $obj == '..') { |
343 | continue; |
344 | } |
345 | if (!@unlink($path . DIRECTORY_SEPARATOR . $obj)) { |
346 | $this->emptyDir(true, $path . DIRECTORY_SEPARATOR . $obj); |
347 | } |
348 | } |
349 | |
350 | // Close the directory handle. |
351 | closedir($dh); |
352 | |
353 | // If the delete flag was passed, remove the top level directory. |
354 | if ($remove) { |
355 | @rmdir($path); |
356 | } |
357 | } |
358 | |
359 | /** |
360 | * Get a file |
361 | * |
362 | * @param string $name |
363 | * @return mixed |
364 | */ |
365 | public function __get($name) |
366 | { |
367 | return $this->offsetGet($name); |
368 | } |
369 | |
370 | /** |
371 | * Does file exist |
372 | * |
373 | * @param string $name |
374 | * @return boolean |
375 | */ |
376 | public function __isset($name) |
377 | { |
378 | return $this->offsetExists($name); |
379 | } |
380 | |
381 | /** |
382 | * Set method |
383 | * |
384 | * @param string $name |
385 | * @param mixed $value |
386 | * @throws Exception |
387 | * @return void |
388 | */ |
389 | public function __set($name, $value) |
390 | { |
391 | $this->offsetSet($name, $value); |
392 | } |
393 | |
394 | /** |
395 | * Unset method |
396 | * |
397 | * @param string $name |
398 | * @throws Exception |
399 | * @return void |
400 | */ |
401 | public function __unset($name) |
402 | { |
403 | $this->offsetUnset($name); |
404 | } |
405 | |
406 | /** |
407 | * ArrayAccess offsetExists |
408 | * |
409 | * @param mixed $offset |
410 | * @return boolean |
411 | */ |
412 | public function offsetExists($offset) |
413 | { |
414 | if (!is_numeric($offset) && in_array($offset, $this->files)) { |
415 | $offset = array_search($offset, $this->files); |
416 | } |
417 | return isset($this->files[$offset]); |
418 | } |
419 | |
420 | /** |
421 | * ArrayAccess offsetGet |
422 | * |
423 | * @param mixed $offset |
424 | * @return mixed |
425 | */ |
426 | public function offsetGet($offset) |
427 | { |
428 | return (isset($this->files[$offset])) ? $this->files[$offset] : null; |
429 | } |
430 | |
431 | /** |
432 | * ArrayAccess offsetSet |
433 | * |
434 | * @param string $offset |
435 | * @param mixed $value |
436 | * @throws Exception |
437 | * @return void |
438 | */ |
439 | public function offsetSet($offset, $value) |
440 | { |
441 | throw new Exception('Error: The directory object is read-only'); |
442 | } |
443 | |
444 | /** |
445 | * ArrayAccess offsetUnset |
446 | * |
447 | * @param string $offset |
448 | * @throws Exception |
449 | * @return void |
450 | */ |
451 | public function offsetUnset($offset) |
452 | { |
453 | if (!is_numeric($offset) && in_array($offset, $this->files)) { |
454 | $offset = array_search($offset, $this->files); |
455 | } |
456 | if (isset($this->files[$offset])) { |
457 | if (is_dir($this->path . DIRECTORY_SEPARATOR . $this->files[$offset])) { |
458 | throw new Exception("Error: The file '" . $this->path . DIRECTORY_SEPARATOR . $this->files[$offset] . "' is a directory"); |
459 | } else if (!file_exists($this->path . DIRECTORY_SEPARATOR . $this->files[$offset])) { |
460 | throw new Exception("Error: The file '" . $this->path . DIRECTORY_SEPARATOR . $this->files[$offset] . "' does not exist"); |
461 | } else if (!is_writable($this->path . DIRECTORY_SEPARATOR . $this->files[$offset])) { |
462 | throw new Exception("Error: The file '" . $this->path . DIRECTORY_SEPARATOR . $this->files[$offset] . "' is read-only"); |
463 | } else { |
464 | unlink($this->path . DIRECTORY_SEPARATOR . $this->files[$offset]); |
465 | unset($this->files[$offset]); |
466 | } |
467 | } else { |
468 | throw new Exception("Error: The file does not exist"); |
469 | } |
470 | } |
471 | |
472 | /** |
473 | * Traverse the directory |
474 | * |
475 | * @return void |
476 | */ |
477 | protected function traverse() |
478 | { |
479 | foreach (new \DirectoryIterator($this->path) as $fileInfo) { |
480 | if(!$fileInfo->isDot()) { |
481 | // If absolute path flag was passed, store the absolute path. |
482 | if ($this->absolute) { |
483 | $f = null; |
484 | if (!$this->filesOnly) { |
485 | $f = ($fileInfo->isDir()) ? |
486 | ($this->path . DIRECTORY_SEPARATOR . $fileInfo->getFilename() . DIRECTORY_SEPARATOR) : |
487 | ($this->path . DIRECTORY_SEPARATOR . $fileInfo->getFilename()); |
488 | } else if (!$fileInfo->isDir()) { |
489 | $f = $this->path . DIRECTORY_SEPARATOR . $fileInfo->getFilename(); |
490 | } |
491 | if (($f !== false) && (null !== $f)) { |
492 | $this->files[] = $f; |
493 | } |
494 | // If relative path flag was passed, store the relative path. |
495 | } else if ($this->relative) { |
496 | $f = null; |
497 | if (!$this->filesOnly) { |
498 | $f = ($fileInfo->isDir()) ? |
499 | ($this->path . DIRECTORY_SEPARATOR . $fileInfo->getFilename() . DIRECTORY_SEPARATOR) : |
500 | ($this->path . DIRECTORY_SEPARATOR . $fileInfo->getFilename()); |
501 | } else if (!$fileInfo->isDir()) { |
502 | $f = $this->path . DIRECTORY_SEPARATOR . $fileInfo->getFilename(); |
503 | } |
504 | if (($f !== false) && (null !== $f)) { |
505 | $this->files[] = substr($f, (strlen(realpath($this->path)) + 1)); |
506 | } |
507 | // Else, store only the directory or file name. |
508 | } else { |
509 | if (!$this->filesOnly) { |
510 | $this->files[] = ($fileInfo->isDir()) ? ($fileInfo->getFilename()) : $fileInfo->getFilename(); |
511 | } else if (!$fileInfo->isDir()) { |
512 | $this->files[] = $fileInfo->getFilename(); |
513 | } |
514 | } |
515 | } |
516 | } |
517 | } |
518 | |
519 | /** |
520 | * Traverse the directory recursively |
521 | * |
522 | * @return void |
523 | */ |
524 | protected function traverseRecursively() |
525 | { |
526 | $objects = new \RecursiveIteratorIterator( |
527 | new \RecursiveDirectoryIterator($this->path), \RecursiveIteratorIterator::SELF_FIRST |
528 | ); |
529 | foreach ($objects as $fileInfo) { |
530 | if (($fileInfo->getFilename() != '.') && ($fileInfo->getFilename() != '..')) { |
531 | // If absolute path flag was passed, store the absolute path. |
532 | if ($this->absolute) { |
533 | $f = null; |
534 | if (!$this->filesOnly) { |
535 | $f = ($fileInfo->isDir()) ? |
536 | (realpath($fileInfo->getPathname())) : realpath($fileInfo->getPathname()); |
537 | } else if (!$fileInfo->isDir()) { |
538 | $f = realpath($fileInfo->getPathname()); |
539 | } |
540 | if (($f !== false) && (null !== $f)) { |
541 | $this->files[] = $f; |
542 | } |
543 | // If relative path flag was passed, store the relative path. |
544 | } else if ($this->relative) { |
545 | $f = null; |
546 | if (!$this->filesOnly) { |
547 | $f = ($fileInfo->isDir()) ? |
548 | (realpath($fileInfo->getPathname())) : realpath($fileInfo->getPathname()); |
549 | } else if (!$fileInfo->isDir()) { |
550 | $f = realpath($fileInfo->getPathname()); |
551 | } |
552 | if (($f !== false) && (null !== $f)) { |
553 | $this->files[] = substr($f, (strlen(realpath($this->path)) + 1)); |
554 | } |
555 | // Else, store only the directory or file name. |
556 | } else { |
557 | if (!$this->filesOnly) { |
558 | $this->files[] = ($fileInfo->isDir()) ? ($fileInfo->getFilename()) : $fileInfo->getFilename(); |
559 | } else if (!$fileInfo->isDir()) { |
560 | $this->files[] = $fileInfo->getFilename(); |
561 | } |
562 | } |
563 | } |
564 | } |
565 | } |
566 | |
567 | /** |
568 | * Build the directory tree |
569 | * |
570 | * @param \DirectoryIterator $it |
571 | * @return array |
572 | */ |
573 | protected function buildTree(\DirectoryIterator $it) |
574 | { |
575 | $result = []; |
576 | |
577 | foreach ($it as $key => $child) { |
578 | if ($child->isDot()) { |
579 | continue; |
580 | } |
581 | |
582 | $name = $child->getBasename(); |
583 | |
584 | if ($child->isDir()) { |
585 | $subdir = new \DirectoryIterator($child->getPathname()); |
586 | $result[DIRECTORY_SEPARATOR . $name] = $this->buildTree($subdir); |
587 | } else { |
588 | $result[] = $name; |
589 | } |
590 | } |
591 | |
592 | return $result; |
593 | } |
594 | |
595 | } |