ILIAS  trunk Revision v11.0_alpha-1749-g1a06bdef097
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
Unzip.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
26 
30 class Unzip
31 {
32  use PathHelper;
33 
34  protected const URI = 'uri';
36  public const DS_UNIX = "/";
37  public const DS_WIN = "\\";
38  public const BASE_DIR = '.';
39  protected \ZipArchive $zip;
40  protected bool $error_reading_zip = false;
41  protected string $path_to_zip;
42  private int $amount_of_entries = 0;
43 
44  public function __construct(
45  protected UnzipOptions $options,
46  protected FileStream $stream
47  ) {
48  $this->path_to_zip = $this->stream->getMetadata(self::URI);
49  $this->zip = new \ZipArchive();
50  try {
51  $this->zip->open($this->path_to_zip, \ZipArchive::RDONLY);
52  $this->amount_of_entries = $this->zip->count();
53  } catch (\Throwable) {
54  $this->error_reading_zip = true;
55  }
56  }
57 
61  protected function pathToStreamGenerator(): \Closure
62  {
63  return function (\Generator $paths): \Generator {
64  foreach ($paths as $path) {
65  $resource = $this->zip->getStream($path);
66 
67  yield Streams::ofResource($resource);
68  }
69  };
70  }
71 
75  public function getPaths(): \Generator
76  {
77  if (!$this->error_reading_zip) {
78  for ($i = 0, $i_max = $this->amount_of_entries; $i < $i_max; $i++) {
79  $path = $this->zip->getNameIndex($i, \ZipArchive::FL_UNCHANGED);
80  if ($this->isPathIgnored($path, $this->options)) {
81  continue;
82  }
83  yield $path;
84  }
85  }
86  }
87 
91  public function getStreams(): \Generator
92  {
93  $paths_to_stream_generator = $this->pathToStreamGenerator();
94 
95  if ($this->options->getDirectoryHandling() === ZipDirectoryHandling::FLAT_STRUCTURE) {
96  yield from $paths_to_stream_generator($this->getFiles());
97  } else {
98  yield from $paths_to_stream_generator($this->getPaths());
99  }
100  }
101 
105  public function getFileStreams(): \Generator
106  {
107  $paths_to_stream_generator = $this->pathToStreamGenerator();
108 
109  yield from $paths_to_stream_generator($this->getFiles());
110  }
111 
112  public function getAmountOfDirectories(): int
113  {
114  return iterator_count($this->getDirectories());
115  }
116 
122  public function getDirectories(): \Generator
123  {
124  $directories = [];
125  foreach ($this->getPaths() as $path) {
126  if (substr($path, -1) === self::DS_UNIX || substr($path, -1) === self::DS_WIN) {
127  $directories[] = $path;
128  continue;
129  }
130  if ((str_contains($path, self::DS_UNIX) || str_contains($path, self::DS_WIN))) {
131  $directories[] = dirname($path) . self::DIRECTORY_SEPARATOR;
132  }
133  }
134 
135  $directories_with_parents = [];
136 
137  foreach ($directories as $directory) {
138  $parent = dirname($directory) . self::DIRECTORY_SEPARATOR;
139  if ($parent !== self::BASE_DIR . self::DIRECTORY_SEPARATOR && !in_array($parent, $directories, true)) {
140  $directories_with_parents[] = $parent;
141  }
142  $directories_with_parents[] = $directory;
143  }
144 
145  $directories_with_parents = array_unique($directories_with_parents);
146  sort($directories_with_parents);
147  yield from $directories_with_parents;
148  }
149 
150  public function getAmountOfFiles(): int
151  {
152  return iterator_count($this->getFiles());
153  }
154 
159  public function getFiles(): \Generator
160  {
161  $files = [];
162  foreach ($this->getPaths() as $path) {
163  if (substr($path, -1) !== self::DS_UNIX && substr($path, -1) !== self::DS_WIN) {
164  $files[] = $path;
165  }
166  }
167  sort($files);
168  yield from $files;
169  }
170 
171  public function hasMultipleRootEntriesInZip(): bool
172  {
173  $amount = 0;
174  foreach ($this->getDirectories() as $zip_directory) {
175  $dirname = dirname($zip_directory);
176  if ($dirname === self::BASE_DIR) {
177  $amount++;
178  }
179  if ($amount > 1) {
180  return true;
181  }
182  }
183  foreach ($this->getFiles() as $zip_file) {
184  $dirname = dirname($zip_file);
185  if ($dirname === self::BASE_DIR) {
186  $amount++;
187  }
188  if ($amount > 1) {
189  return true;
190  }
191  }
192  return false;
193  }
194 
195  public function extract(): bool
196  {
197  if ($this->error_reading_zip) {
198  return false;
199  }
200 
201  $destination_path = $this->options->getZipOutputPath();
202  if ($destination_path === null) {
203  return false;
204  }
205 
206  switch ($this->options->getDirectoryHandling()) {
207  case ZipDirectoryHandling::KEEP_STRUCTURE:
208  break;
209  case ZipDirectoryHandling::ENSURE_SINGLE_TOP_DIR:
210  // top directory with same name as the ZIP without suffix
211  $zip_path = $this->stream->getMetadata(self::URI);
212  $sufix = '.' . pathinfo((string) $zip_path, PATHINFO_EXTENSION);
213  $top_directory = basename((string) $zip_path, $sufix);
214 
215  // first we check if the ZIP contains the top directory
216  $has_top_directory = true;
217  foreach ($this->getPaths() as $path) {
218  $has_top_directory = str_starts_with($path, $top_directory) && $has_top_directory;
219  }
220 
221  // if not, we prepend the top directory to the destination path
222  if (!$has_top_directory) {
223  $destination_path .= self::DIRECTORY_SEPARATOR . $top_directory;
224  }
225  break;
227  if (!is_dir($destination_path) && (!mkdir($destination_path, 0777, true) && !is_dir($destination_path))) {
228  throw new \RuntimeException(sprintf('Directory "%s" was not created', $destination_path));
229  }
230 
231  foreach ($this->getStreams() as $stream) {
232  $uri = $stream->getMetadata(self::URI);
233  if (substr((string) $uri, -1) === self::DIRECTORY_SEPARATOR) {
234  continue; // Skip directories
235  }
236  $file_name = Util::sanitizeFileName($destination_path . self::DIRECTORY_SEPARATOR . basename((string) $uri));
237  file_put_contents(
238  $file_name,
239  $stream->getContents()
240  );
241  }
242  return true; // Stop here
243  }
244 
245  $this->zip->extractTo($destination_path, iterator_to_array($this->getPaths()));
246 
247  return true;
248  }
249 
250  public function hasZipReadingError(): bool
251  {
253  }
254 }
Will keep the top directory of the ZIP file if there is one (simple unzip).
getDirectories()
Yields the directory-paths of the currently open zip-archive.
Definition: Unzip.php:122
getFiles()
Yields the file-paths of the currently open zip-archive.
Definition: Unzip.php:159
sort()
description: > Example for rendering a Sort Glyph.
Definition: sort.php:41
$path
Definition: ltiservices.php:29
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
static sanitizeFileName(string $filename)
Definition: Util.php:48
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(protected UnzipOptions $options, protected FileStream $stream)
Definition: Unzip.php:44
The base interface for all filesystem streams.
Definition: FileStream.php:31