Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Zip
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
2 / 2
21
100.00% covered (success)
100.00%
1 / 1
 pack
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
1 / 1
16
 unpack
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Utils\IO\Zip
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Utils\IO\Zip;
16
17use phpOMS\System\File\FileUtils;
18use phpOMS\System\File\Local\Directory;
19
20/**
21 * Zip class for handling zip files.
22 *
23 * Providing basic zip support
24 *
25 * @package phpOMS\Utils\IO\Zip
26 * @license OMS License 2.0
27 * @link    https://jingga.app
28 * @since   1.0.0
29 */
30class Zip implements ArchiveInterface
31{
32    /**
33     * {@inheritdoc}
34     */
35    public static function pack(string | array $sources, string $destination, bool $overwrite = false) : bool
36    {
37        $destination = FileUtils::absolute(\strtr($destination, '\\', '/'));
38        if ((!$overwrite && \is_file($destination))
39            || \is_dir($destination)
40        ) {
41            return false;
42        }
43
44        $zip = new \ZipArchive();
45        if (!$zip->open($destination, $overwrite ? \ZipArchive::CREATE | \ZipArchive::OVERWRITE : \ZipArchive::CREATE)) {
46            return false; // @codeCoverageIgnore
47        }
48
49        if (\is_string($sources)) {
50            $sources = [$sources => ''];
51        }
52
53        /**
54         * @var string $relative
55         */
56        foreach ($sources as $source => $relative) {
57            if (\is_int($source)) {
58                $source = $relative;
59            }
60
61            $source   = FileUtils::absolute(\strtr($source, '\\', '/'));
62            $relative = \strtr($relative, '\\', '/');
63
64            if (\is_dir($source)) {
65                /** @var string[] $files */
66                $files = new \RecursiveIteratorIterator(
67                    new \RecursiveDirectoryIterator($source, \FilesystemIterator::CURRENT_AS_PATHNAME),
68                    \RecursiveIteratorIterator::SELF_FIRST
69                );
70
71                foreach ($files as $file) {
72                    $file = \strtr($file, '\\', '/');
73
74                    /* Ignore . and .. */
75                    if (($pos = \mb_strrpos($file, '/')) === false
76                        || \in_array(\mb_substr($file, $pos + 1), ['.', '..'])
77                    ) {
78                        continue;
79                    }
80
81                    $absolute = \realpath($file);
82                    $absolute = \str_replace('\\', '/', (string) $absolute);
83                    $dir      = \ltrim(\rtrim($relative, '/\\') . '/' . \ltrim(\str_replace($source . '/', '', $absolute), '/\\'), '/\\');
84
85                    if (\is_dir($absolute)) {
86                        $zip->addEmptyDir($dir . '/');
87                    } elseif (\is_file($absolute)) {
88                        $zip->addFile($absolute, $dir);
89                    }
90                }
91            } elseif (\is_file($source)) {
92                $zip->addFile($source, $relative);
93            } else {
94                continue;
95            }
96        }
97
98        return $zip->close();
99    }
100
101    /**
102     * {@inheritdoc}
103     */
104    public static function unpack(string $source, string $destination) : bool
105    {
106        if (!\is_file($source)) {
107            return false;
108        }
109
110        if (!\is_dir($destination)) {
111            Directory::create($destination, recursive: true);
112        }
113
114        $destination = \strtr($destination, '\\', '/');
115        $destination = \rtrim($destination, '/');
116
117        try {
118            $zip = new \ZipArchive();
119            if (!$zip->open($source)) {
120                return false; // @codeCoverageIgnore
121            }
122
123            $zip->extractTo($destination . '/');
124
125            return $zip->close();
126        } catch (\Throwable $_) {
127            return false;
128        }
129    }
130}