dimayakovlev.ru


Удобный метод для извлечения содержимого директории из zip архива

При разработке PHP приложений, работающих с файловыми zip архивами, я использую стандартное PHP расширение для сжатия и архивации Zip. Это расширение делает доступным использование класса ZipArchive, реализующего методы, позволяющие выполнять основные действия с zip архивами, которые требуются при решении большинства задач. Однако в ряде случаев, в классе ZipArchive мне не хватает метода, позволяющего извлечь содержимое заданной директории архива в указанное место, не сохраняя при этом родительскую структуру директорий.

Если прочитать справку класса ZipArchive, то может возникнуть справедливый вопрос: “Разве стандартный метод ZipArchive::extractTo не делает этого?” Действительно, доступный метод ZipArchive::extractTo позволяет извлечь не только всё содержимое архива, но и его часть. Однако при извлечении части архива в месте назначения будет создана родительская структура директорий для извлекаемого элемента.

К сожалению, метод ZipArchive::extractTo не позволяет изменить режим работы для извлечения элемента архива непосредственно в корень места назначения, что может создать определённые трудности. Именно поэтому я использую собственное решение.

<?php

class ZipArchiveExtended extends ZipArchive
{
  /**
   * Извлекает содержимое директории из zip архива
   *
   * @param string $destination Место назначения для сохранения извлечённых элементов
   * @param string $directory Директория в zip архиве, подлежащая извлечению
   * @return boolean|array Возвращает значение true в случае успешного выполнения операции, либо array, содержащий ошибки извлечения
   */
  public function extractDirTo($destination, $directory)
  {
    $errors = array();

    $destination = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, $destination);
    $directory = str_replace(array("/", "\\"), "/", $directory);
    
    if (substr($destination, mb_strlen(DIRECTORY_SEPARATOR, "UTF-8") * -1) != DIRECTORY_SEPARATOR) {
      $destination .= DIRECTORY_SEPARATOR;
    }

    if (substr($directory, -1) != "/") {
      $directory .= "/";
    }
    
    for ($i = 0; $i < $this->numFiles; $i++) {
      $filename = $this->getNameIndex($i);
      if (substr($filename, 0, mb_strlen($directory, "UTF-8")) == $directory) {
        $relativePath = substr($filename, mb_strlen($directory, "UTF-8"));
        $relativePath = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, $relativePath);        
        if (mb_strlen($relativePath, "UTF-8") > 0) {
          if (substr($filename, -1) == "/") {
            if (!is_dir($destination . $relativePath))
              if (!@mkdir($destination . $relativePath, 0755, true)) {
                $errors[$i] = $filename;
              }
          } else {
            if (dirname($relativePath) != ".") {
              if (!is_dir($destination . dirname($relativePath))) {
                @mkdir($destination . dirname($relativePath), 0755, true);
              }
            }            
            if (@file_put_contents($destination . $relativePath, $this->getFromIndex($i))  false) {
              $errors[$i] = $filename;
            }
          }
        }
      }
    }

    return count($errors) > 0 ? $errors : true;
  }
}

?>

Класс ZipArchiveExtended наследует класс ZipArchive, расширяя его методом ZipArchiveExtended::extractDirTo. В результате выполнения метода ZipArchiveExtended::extractDirTo будет выполнено извлечение содержимого заданной директории из zip архива без сохранения в месте назначения её родительской структуры директорий, но с сохранением её внутренней структуры. Параметры, принимаемые методом, а также возвращаемое значение, даны в описании метода.

Теперь извлечение содержимого директории из zip архива с использованием класса ZipArchiveExtended может быть выполнено следующим образом.

<?php
$file = 'photos.zip';
$dest = '/files/photos';
$dir = '/photo/01-07-2017';
$zip = new ZipArchiveExtended;
if ($zip->open($file)  true) {
  $res = $zip->extractDirTo($dest, $dir);
  $zip->close();
  if ($res  true) {
    echo 'Всё в порядке';
  } else {
    echo 'При извлечении возникли ошибки';
  }
}
?>

Используя метод ZipArchiveExtended::extractDirTo, извлечении содержимого директории zip архива непосредственно в корень места назначения больше не будет вызывать дополнительных сложностей.