Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Le funzioni per la gestione del file Zip con PHP 5.2

Aprire, creare, leggere i file compressi in formato Zip con PHP 5.2
Aprire, creare, leggere i file compressi in formato Zip con PHP 5.2
Link copiato negli appunti

Nella nuova release ufficiale di PHP, la 5.2, è stata introdotta come libreria di default l'estensione, attualmente disponibile tramite il repository PECL, per la gestione completa dei file Zip. La gestione degli archivi per un progetto Web è un'operazione spesso sottovalutata ma di indubbia utilità nel momento in cui ci si trova a sviluppare applicazioni abbastanza complesse. Gestire gli archivi in modo semplice a livello di scripting permette di creare backup compressi delle informazioni, gestire l'importazione e l'esportazione di package specifici contenenti estensioni per le nostre applicazioni, far scaricare molteplici file in un unico archivio e molto altro ancora.

La soluzione inclusa in PHP 5.2 permetterà di fare tutto questo e molto altro con delle ottime performance dovute dal fatto che l'estensione è implementata nativamente ed è molto più completa rispetto a molte soluzioni implementate in PHP che possiamo trovare sulla rete.

L'estensione per gestire archivi compressi

L'estensione per la gestione dei file compressi espone un oggetto chiamato ZipArchive che rappresenta un archivio zip, ed una serie di funzioni che permettono di leggere e scrivere all'interno di archivi a chiunque preferisse un approccio procedurale. In questo articolo e negli esempi successivi utilizzeremo sempre l'approccio ad oggetti ove possibile, passando a quello procedurale solo quando e se strettamente necessario.

Per una corretta gestione degli archivi zip la libreria espone una serie di costanti che possono o devono essere utilizzate durante la chiamata a determinati metodi; queste costanti sono esaustivamente spiegate nella documentazione ufficiale. La grande potenza dell'estensione comunque non sta solo nella gestione esplicita degli archivi zip: difatti è possibile sfruttare gli stream introdotti nelle ultime versioni di PHP per poter accedere direttamente a determinati file all'interno degli archivi, e lavoraci come se fossero stati acceduti direttamente all'interno del filesystem.

Questa operazione è molto interessante perchè evita la copia dei contenuti all'interno di variabili temporanee ed oltretutto ci permette di sfruttare tutte le funzionalità che supportano gli stream ma non la lettura dei contenuti da buffer.

Utilizzo degli stream

Passiamo ora a qualche esempio pratico che ci mostri come utilizzare le funzionalità esposte dall'estensione. Per prima cosa introduciamo brevemente l'utilizzo dello stream zip:// che, come accennato prima, ci permette di accedere direttamente a file specifici all'interno di un archivio compresso.

Nell'esempio, largamente ispirato a quello disponibile nella documentazione ufficiale, accediamo al file dei metadati di un documento ODT scritto utilizzando OpenOffice (i documenti ODT sono veri e propri file compressi in formato Zip). L'esempio, oltre ad essere autoesplicativo, è particolarmente utile per chi desideri scrivere software che si integri correttamente con i documenti scritti utilizzando la famosa suite open source:

<?php

$reader = new XMLReader();

/* Utilizzando gli stream apro esplicitamente il file meta.xml
* contenuto all'interno dell'archivio.
*/
$reader->open('zip:///documento.odt#meta.xml');
$odt_meta = array();
while ($reader->read())
{
  if ($reader->nodeType == XMLREADER::ELEMENT)
    $elm = $reader->name;
  else
  {
    if ($reader->nodeType == XMLREADER::END_ELEMENT && $reader->name == 'office:meta')
      break;
    if (!trim($reader->value))
      continue;
    $odt_meta[$elm] = $reader->value;
  }
}

echo "<ul>";
foreach($odt_meta as $meta => $value)
{
  echo "<li><strong>".$meta."</strong>: ".$value."</li>";
}
echo "</ul>";

?>

L'utilizzo dello stream è molto semplice: è sufficiente passare come parametro a qualunque funzione supporti gli stream il nome del file compresso anteposto da zip:// e seguito dal nome del file da accedere all'interno dell'archivio separato dal precedente con un cancelletto (#). L'estensione poi si occuperà del resto, restituendo un errore se il file non può essere acceduto.

La lettura degli archivi

Passiamo ora all'utilizzo della classe ZipArchive. Per prima cosa vedremo come accedere in lettura ad un file compresso per elencare all'interno di una lista tutti i file e le cartelle contenute nell'archivio:

<?php

/* Un singolo elemento (directory o file) dell'albero dei file
* contenuti in un archivio compresso.
*/
class Node
{
  public $name;
  public $children;
  
  public function __construct($name)
  {
    $this->name = $name;
    $this->children = array();
  }
  
  public function addChild($child)
  {
    $this->children[] = $child;
  }
  
  public function hasChild($name)
  {
    foreach($this->children as $child)
    {
      if($child->name == $name)
        return true;
    }
    
    return false;
  }
  
  public function getChild($name)
  {
    foreach($this->children as $child)
    {
      if($child->name == $name)
        return $child;
    }
    
    return null;
  }
  
  public function render()
  {
    if(count($this->children) > 0)
    {
      echo "<li>".$this->name;
      echo "<ul>";
      foreach($this->children as $child)
        $child->render();
      echo "</ul>";
      echo "</li>";
    }else
      echo "<li>".$this->name."</li>";
  }
}

/* La struttura che rappresenta l'albero dei file contenuti all'interno
* dell'archivio.
*/
class Tree
{
  public $root;
  
  public function __construct()
  {
    $this->root = new Node('root');
  }
  
  public function fill($path)
  {
    $dirs = explode("/", $path);
    $file = array_pop($dirs);
    
    $current = $this->root;
    foreach($dirs as $dir)
    {
      if(!$current->hasChild($dir))
      {
        $node = new Node($dir);
        $current->addChild($node);
        $current = $node;
      }else
        $current = $current->getChild($dir);
    }

    $node = new Node($file);
    $current->addChild($node);
  }
  
  public function render()
  {
    echo "<ul>";
    $this->root->render();
    echo "</ul>";
  }
}

$archive = new ZipArchive();
$archive->open('archivio_di_prova.zip');

$tree = new Tree();

for ($i=0; $i < $za->numFiles; ++$i)
{
  $stat = $archive->statIndex($i);
  $tree->fill($stat['name']);
}

$tree->render();

?>

Il codice è leggermente lungo al fine di facilitare la creazione dell'albero che rappresenta il contenuto del file compresso. Dopo aver creato una nuova istanza della classe ZipArchive procediamo con l'apertura del documento compresso e successivamente iteriamo su tutti gli elementi contenuti recuperando le statistiche necessarie alla costruzione dell'albero. La proprietà numFiles dell'oggetto ZipArchive contiene il numero dei file inclusi nell'archivio mentre attraverso il metodo statIndex recuperiamo le informazioni riguardanti il file indicato da un determinato indice numerico.

Creazione di archivi

Passiamo ora al processo di creazione ed aggiunti di file ad un archivio. Il procedimento è molto semplice ed anche esso si basa sull'utilizzo dell'oggetto ZipArchive e di una semplice funzione ricorsiva per creare un archivio compresso del contenuto della directory contenente un file di indice che elenca tutti i file inclusi:

<?php

/* Restituisce un array che rappresenta il path completo
dei file contenuti nell'archivio.
*/
function listdir($path)
{
  $content = array();
  $dir = dir($path);
  while($item = $dir->read())
  {
    if(in_array($item, array(".", "..")))
      continue;
      
    $file = realpath($path."/".$item);
    if(is_dir($file))
      $content = array_merge($content, listdir($file));
    else
      $content[] = $file;
  }
  
  return $content;
}

function create_archive($name, $dir)
{
  if(!file_exists($dir) || !is_dir($dir))
    throws new Exception($dir." non è una directory!");
  
  $archive = new ZipArchive();

  if ($archive->open($name, ZIPARCHIVE::CREATE) !== true)
    throws new Exception("Impossibile creare l'archivio!");
  
  $list = listdir($dir);
  foreach($list as $file)
  {
    $archive->addFile($file, str_replace($dir, "", $file));
  }

  $archive->addFromString("INDEX.txt", implode("n", $list);

  $archive->close();

}

create_archive("test.zip", "/Users/gabrielefarina/articoli/23/zip/test");

?>

Come possiamo notare la creazione di un nuovo archivio è un'operazione molto semplice: per prima cosa costruiamo l'oggetto ZipArchive ed apriamo un nuovo archivio (ricordandoci di specificare ZIPARCHIVE::CREATE come secondo parametro al fine di informare il metodo open del fatto che vogliamo creare il file che rappresenterà l'archivio); successivamente possiamo sfruttare i metodi addFile ed addFromString per aggiungere i file all'archivio.

Il metodo addFile accetta come parametro il path del file di da aggiungere e, come opzione, il nome con il quale sarà indicizzato all'interno dell'archivio; il metodo addFromString invece accetta come parametri il nome ed il contenuto, in formato stringa, del file da aggiungere all'archivio. Come ultima operazione è sempre opportuno richiamare il metodo close al fine di finalizzare tutte le operazioni effettuate.

Conclusioni

L'estensione built-in per la creazione e la gestione degli archivi compressi in formato Zip è molto potente, semplice e veloce. Sicuramente un'ottima aggiunta alla dotazione standard ed un eccellente passo verso un set di librerie base di ottimo livello.

Ti consigliamo anche