19 declare(strict_types=1);
36 protected \ZipArchive
$zip;
50 $this->streams = array_filter($streams, fn($stream):
bool => $stream instanceof
FileStream);
53 $this->zip_output_file = $this->ensureDirectorySeperator(
62 $system_limit = (
int) shell_exec(
'ulimit -n') ?: 0;
64 $this->iteration_limit = $system_limit < 10 ? 100 : min(
69 $this->zip = new \ZipArchive();
70 if (!file_exists($this->zip_output_file)) {
71 touch($this->zip_output_file);
73 if ($this->zip->open($this->zip_output_file, \ZipArchive::OVERWRITE) !==
true) {
74 throw new \Exception(
"cannot open <$this->zip_output_file>\n");
80 $directory = defined(
'CLIENT_DATA_DIR') ?
\CLIENT_DATA_DIR .
'/temp' : sys_get_temp_dir();
81 $tempnam = tempnam($directory,
'zip');
82 if (is_file($tempnam)) {
85 if (is_dir($tempnam)) {
94 register_shutdown_function($c);
99 foreach ($this->streams as $path_inside_zip => $stream) {
100 $path = $stream->getMetadata(
'uri');
101 if ($this->store_counter === 0) {
102 $this->zip->open($this->zip_output_file);
104 if (is_int($path_inside_zip)) {
105 $path_inside_zip = basename((
string)
$path);
108 if (
$path ===
'php://memory') {
109 $this->zip->addFromString($path_inside_zip, (
string) $stream);
111 } elseif (is_file(
$path)) {
112 $this->zip->addFile(
$path, $path_inside_zip);
119 $this->store_counter === $this->iteration_limit
120 || count(get_resources(
'stream')) > ($this->iteration_limit * 0.9)
123 $this->store_counter = 0;
125 $this->store_counter++;
136 return Streams::ofResource(fopen($this->zip_output_file,
'rb'));
146 if (file_exists($this->zip_output_file)) {
147 unlink($this->zip_output_file);
159 $path_inside_zip ??= basename($path);
162 $this->zip->addEmptyDir(rtrim(dirname($path_inside_zip),
'/') .
'/');
165 Streams::ofResource(fopen($path,
'rb')),
173 if (count($this->streams) === 1 && isset($this->streams[self::DOT_EMPTY])) {
174 unset($this->streams[self::DOT_EMPTY]);
178 $this->streams[$path_inside_zip] = $stream;
181 $this->path_counter === $this->iteration_limit
182 || count(get_resources(
'stream')) > ($this->iteration_limit * 0.9)
186 $this->path_counter = 0;
188 $this->path_counter++;
198 public function addDirectory(
string $directory_to_zip):
void 200 $directory_to_zip = $this->normalizePath(rtrim($directory_to_zip,
'/'));
202 $files = new \RecursiveIteratorIterator(
204 \RecursiveIteratorIterator::SELF_FIRST
207 switch ($this->options->getDirectoryHandling()) {
208 case ZipDirectoryHandling::KEEP_STRUCTURE:
212 case ZipDirectoryHandling::ENSURE_SINGLE_TOP_DIR:
213 $prefix = basename($directory_to_zip) .
'/';
214 $pattern =
'/^' . preg_quote($prefix,
'/') .
'/';
218 foreach ($files as $file) {
219 $pathname = $file->getPathname();
220 $path_inside_zip = str_replace($directory_to_zip .
'/',
'', $pathname);
221 if ($pattern !==
null) {
222 $path_inside_zip = $prefix . preg_replace($pattern,
'', $path_inside_zip);
226 if ($file->isDir()) {
228 $sub_items = array_filter(scandir($pathname),
static fn(
$d):
bool => !str_contains((
string)
$d,
'.DS_Store'));
229 if (count($sub_items) === 2) {
230 $this->zip->addEmptyDir($path_inside_zip);
235 if ($this->isPathIgnored($pathname, $this->options)) {
239 $this->
addPath(realpath($pathname), $path_inside_zip);
destroy()
Explicitly close the zip file and remove the file from the filesystem.
addStream(FileStream $stream, string $path_inside_zip)
registerShutdownFunction(\Closure $c)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
addPath(string $path, ?string $path_inside_zip=null)
The base interface for all filesystem streams.
__construct(protected ZipOptions $options,... $streams)