php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #79874 procedural API of ext/zip depercated without replacement
Submitted: 2020-07-18 16:40 UTC Modified: 2020-07-23 08:49 UTC
From: bugreports2 at gmail dot com Assigned:
Status: Not a bug Package: Zip Related
PHP Version: 8.0Git-2020-07-18 (Git) OS:
Private report: No CVE-ID: None
 [2020-07-18 16:40 UTC] bugreports2 at gmail dot com
Description:
------------
http://git.php.net/?p=php-src.git;a=commitdiff;h=f3b1f342c887073012f4ca7c1bc1b5259ce4cedc

that's cool, but how do you expect switching to the object style when these is no ZipArchive::read(), ZipArchive::entry_name, ZipArchive::entry_filesize(), 
ZipArchive::entry_read()

ZipArchive::extractTo() is *no* replacement when you iterate and decide which files are supposed to be extracted, you need to reqrite the whole code and logic to delete stuff after extract

look at mysqli - there is everything and more in the object API and so it *can* be a dropin replacement without changing the whole program logic - and no please don#t be tempted to deprecate tthe old APi there too just because you can


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-07-18 17:21 UTC] bugreports2 at gmail dot com
someone my port that code running perfectly for many years to the limited ZipArchive class.....

 /**
  * Lokales oder hochgeladenes ZIP-Archiv rekursiv im CMS-Temp-Folder entpacken
  * Versteckte Dateien und Ordner werden ignoriert
  *
  * Wenn keine Dateiendungen uebergeben wurden aktuelle Systemeinstellungen benutzen
  * Wenn es sich um eine hochgeladene Datei handelt implizit move_uploaded_file() benutzen
  *
  * @param mixed $source          Pfad zum ZIP-File oder ein $_FILES-Item eines Dateiuploads
  * @param array $ext             Array mit erlaubten Dateiendungen oder Fallback auf CMS-defaults wenn leer
  * @param bool  $normalize_names Dateinamen webkonform normalisieren, Default: Aktiv
  *
  * @return array [folder, files, folders]
 */
 public function unpack_zip($source, array $ext=[], bool $normalize_names=TRUE): array
 {
  /** Abbrechen wenn kein Server-Support */
  if(extension_loaded('zip') === FALSE)
  {
   return [];
  }
  /** Maximale Skript-Laufzeit erhoehen und bei Verbindungsabbruch weiterlaufen lassen */
  ignore_user_abort(TRUE);
  set_time_limit(0);
  /** Wenn keine erlaubten Dateiendungen definiert wurden auf CMS-Defaults zurueckfallen */
  if(empty($ext))
  {
   global $uploadtypes;
   $ext = $uploadtypes;
  }
  /** Datei-Upload verarbeiten */
  if(is_array($source) === TRUE)
  {
   /** Check ob es sich um einen Upload handelt */
   if(empty($source['error']) && !empty($source['tmp_name']) && !empty($source['name']) && strtolower(substr($source['name'], -4)) === '.zip' && is_uploaded_file($source['tmp_name']) === TRUE)
   {
    /** Upload in CMS-Temp-Folder verschieben */
    $zip_upload_tmp_name = "{$this->cl_api->CONTENTLOUNGE_BASEDIR}/temp/unpack_zip_" . bin2hex(random_bytes(10)) . '.zip';
    if(move_uploaded_file($source['tmp_name'], $zip_upload_tmp_name) === FALSE)
    {
     return [];
    }
    else
    {
     /** Ab jetzt spielen wir der Funktion eine normale Datei als Source-Angabe vor */
     chmod($zip_upload_tmp_name, 0660);
     clearstatcache(TRUE, $zip_upload_tmp_name);
     $source = $zip_upload_tmp_name;
    }
   }
  }
  /** ZIP-Archiv oeffnen */
  if(!empty($source))
  {
   $zip_handle = zip_open($source);
   if(is_resource($zip_handle))
   {
    /** Eindeutigen Temp-Folder zum Entpacken */
    $zip_tmp_folder = "{$this->cl_api->CONTENTLOUNGE_BASEDIR}/temp/unpack_zip_" . bin2hex(random_bytes(10));
    /** Sicherstellen dass der Temp-Pfad nicht mit einem Slash endet */
    if(substr($zip_tmp_folder, -1) === '/')
    {
     $zip_tmp_folder = substr($zip_tmp_folder, 0, -1);
    }
    /** Temp-Dir anlegen */
    mkdir($zip_tmp_folder, 0770);
    /** ZIP-Archiv durchlaufen */
    $files_created = FALSE;
    while(($zip_file_handle = zip_read($zip_handle)) !== FALSE)
    {
     /** Nur Dateien mit der gelisteten Erweiterung verarbeiten */
     $zip_file_name = utf8_decode(str_replace("\\", '/', zip_entry_name($zip_file_handle)));
     $zip_file_ext  = pathinfo($zip_file_name, PATHINFO_EXTENSION);
     rh_debug_in_array(strtolower($zip_file_ext), $ext);
     if(in_array(strtolower($zip_file_ext), $ext, /**strict*/TRUE) === TRUE)
     {
      /** Sicherstellen dass wir keine versteckten und MACOSX-Meta Dateien verarbeiten */
      if(substr(basename($zip_file_name), 0, 1) !== '.' && strpos($zip_file_name, '/.') === FALSE && strpos($zip_file_name, '/__MACOSX') === FALSE)
      {
       /** Absoluten Pfad im Temp-Folder festlegen, einzelne Ordner-Komponenten dabei normalisieren */
       $folder_components = explode('/', dirname($zip_file_name));
       foreach($folder_components as $folder_key=>$folder_component)
       {
        $folder_components[$folder_key] = rh_misc::ConvertMyUploadName($folder_component);
       }
       /** Base-Dateinamen behandeln */
       switch($normalize_names)
       {
        /** Kompletten Dateinamen fuer Web normalisieren */
        case TRUE:
          $zip_file_name = implode('/', $folder_components) . '/' . rh_misc::ConvertMyUploadName(basename($zip_file_name));
          $zip_temp_filename = "{$zip_tmp_folder}/{$zip_file_name}";
          break;
        /** Dateiendung in jedem Fall auf Kleinbuchstaben */
        case FALSE:
          $zip_file_name = implode('/', $folder_components) . '/' . basename($zip_file_name);
          $zip_temp_filename = "{$zip_tmp_folder}/" . str_replace(".{$zip_file_ext}", '.' . strtolower($zip_file_ext), $zip_file_name);
          break;
       }
       /** Sicherstellen dass wir die Subfolder-Struktur beibehalten */
       if(is_dir(dirname($zip_temp_filename)) === FALSE)
       {
        mkdir(dirname($zip_temp_filename), 0770, TRUE);
       }
       /** Temp-File erstellen */
       if(zip_entry_filesize($zip_file_handle) > 0)
       {
        file_put_contents($zip_temp_filename, zip_entry_read($zip_file_handle, zip_entry_filesize($zip_file_handle)));
        $files_created = TRUE;
       }
      }
     }
    }
    /** ZIP-Archiv wieder schliessen */
    zip_close($zip_handle);
    /** GGf. vorhandene Upload-Datei wieder loeschen */
    if(!empty($zip_upload_tmp_name) && file_exists($zip_upload_tmp_name) === TRUE)
    {
     unlink($zip_upload_tmp_name);
    }
    /** Check ob verwertbare Dateien im Archiv gefunden wurden */
    switch($files_created)
    {
     /** Keine Dateien entpackt */
     case FALSE: (new rh_filesystem)->rmdir($zip_tmp_folder); return []; break;
     /** Finale Verarbeitung */
     case TRUE:
       /** Check ob erster Ordner alleinestehend UND leer ist und alles darunter eine Ebene nach oben schieben */
       $basedir_files = $this->cl_api->folders->readdir($zip_tmp_folder);
       $basedir_folders = $this->cl_api->folders->readdir_folders($zip_tmp_folder);
       if(count($basedir_folders) === 1)
       {
        $basedir_parent_folder = $basedir_folders[0];
        $basedir_folders = $this->cl_api->folders->readdir_folders($basedir_parent_folder);
        /** Subfolder vorhanden */
        if(!empty($basedir_folders))
        {
         foreach($basedir_folders as $basedir_current_folder)
         {
          $basedir_current_folder_new = $zip_tmp_folder . str_replace($basedir_parent_folder, '', $basedir_current_folder);
          rename($basedir_current_folder, $basedir_current_folder_new);
         }
        }
        /** Nur Dateien im Basis-Ordner */
        else
        {
         $basedir_files = $this->cl_api->folders->readdir($basedir_parent_folder);
         if(!empty($basedir_files))
         {
          foreach($basedir_files as $basedir_current_file)
          {
           $basedir_current_file_new = $zip_tmp_folder . str_replace($basedir_parent_folder, '', $basedir_current_file);
           rename($basedir_current_file, $basedir_current_file_new);
          }
         }
        }
       }
       /** Filesystem-Klasse instanzieren und Liste zurueckgeben, leere Ordner entfernen */
       $rh_fs = new rh_filesystem;
       $file_list = $rh_fs->ListFilesRecursive(/**path*/$zip_tmp_folder, /**details*/TRUE);
       foreach($file_list['folders'] as $current_folder)
       {
        if($rh_fs->is_dir_empty($current_folder) === TRUE)
        {
         rmdir($current_folder);
        }
       }
       $file_list = $rh_fs->ListFilesRecursive(/**path*/$zip_tmp_folder, /**details*/TRUE);
       $file_list['files'] = array_keys($file_list['files']);
       foreach($file_list['folders'] as $current_folder)
       {
        chmod($current_folder, 0770);
        clearstatcache(TRUE, $current_folder);
       }
       foreach($file_list['files'] as $current_file)
       {
        chmod($current_file, 0660);
        clearstatcache(TRUE, $current_file);
       }
       natsort($file_list['files']);
       natsort($file_list['folders']);
       return ['folder'=>$zip_tmp_folder, 'files'=>$file_list['files'], 'folders'=>$file_list['folders']];
       break;
    }
   }
   /** Offenbar kein ZIP-Archiv, abbrechen und ggf. Temp-File loeschen */
   else
   {
    if(!empty($zip_upload_tmp_name) && file_exists($zip_upload_tmp_name) === TRUE)
    {
     unlink($zip_upload_tmp_name);
    }
   }
  }
  /** Im Zweifel leeren Array zurueckgeben */
  return [];
 }
 [2020-07-18 20:23 UTC] requinix@php.net
-Type: Bug +Type: Documentation Problem -Package: *General Issues +Package: Zip Related
 [2020-07-18 20:23 UTC] requinix@php.net
The procedural API is being removed as part of a push to remove/deprecate using resources in places where other data types (mostly objects) are suitable.

You're right that ext/zip does not work like mysqli where the two API styles work the same way. But ZipArchive does look like it supports the same functionality *in a different way*. Can't do an open/read loop? Use a for loop with getNameIndex/statIndex.
https://www.php.net/manual/en/ziparchive.getnameindex.php
 [2020-07-18 20:54 UTC] cmb@php.net
@requinix, in my opinion, that is a poor replacement for the
procedural API wrt. iterating the archive entries, so I think it
would be good if we made ZipArchive iteratable, yielding ZipEntry
instances with methods such as ::name(), ::filesize() and
::read().
 [2020-07-18 20:59 UTC] requinix@php.net
I'd like to see that too, but it's pretty significant. Can it be done in the next two weeks before the 8.0 feature freeze?
 [2020-07-19 12:13 UTC] salathe@php.net
-Type: Documentation Problem +Type: Feature/Change Request
 [2020-07-23 08:49 UTC] remi@php.net
-Status: Open +Status: Not a bug
 [2020-07-23 08:49 UTC] remi@php.net
Sorry, but your problem does not imply a bug in PHP itself.  For a
list of more appropriate places to ask for help using PHP, please
visit http://www.php.net/support.php as this bug system is not the
appropriate forum for asking support questions.  Due to the volume
of reports we can not explain in detail here why your report is not
a bug.  The support channels will be able to provide an explanation
for you.

Thank you for your interest in PHP.

Documentation has been updated to state about deprecation and link to methods to use.

Deprecation doesn't mean the feature is removed now (only in the future, probably for PHP 9), so people have time to migrate. 

It also (and this is more important) means that the procedural API is not maintained, and suffers for lack of feature.

Iterating is as simple as 

        for ($idx=0 ; $s = $zip->statIndex($idx) ; $idx++) ...
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 13:01:28 2024 UTC