Source code for file /joomla/filesystem/archive/zip.php
Documentation is available at zip.php
* @version $Id: archive.php 6138 2007-01-02 03:44:18Z eddiea $
* @package Joomla.Framework
* @copyright Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
* @license GNU/GPL, see LICENSE.php
* Joomla! is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
// Check to ensure this file is within the rest of the framework
* ZIP format adapter for the JArchive class
* The ZIP compression code is partially based on code from:
* http://www.zend.com/codex.php?id=535&single=1
* http://www.zend.com/codex.php?id=470&single=1
* The ZIP compression date code is partially based on code from
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
* @package Joomla.Framework
* ZIP compression methods.
* Beginning of central directory record.
* End of central directory record.
* Beginning of file contents.
* ZIP file metadata array
* Create a ZIP compressed file from an array of file data.
* @todo Finish Implementation
* @param string $archive Path to save archive
* @param array $files Array of files to add to archive
* @param array $options Compression options [unused]
* @return boolean True if successful
function create($archive, $files, $options =
array ())
foreach ($files as $file)
$this->_addToZIPFile($file, $contents, $ctrldir);
return $this->_createZIPFile($contents, $ctrldir);
* Extract a ZIP compressed file to a given path
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
* @param array $options Extraction options [unused]
* @return boolean True if successful
function extract($archive, $destination, $options =
array ())
return ($this->_extractNative($archive, $destination, $options))?
true :
JError::raiseWarning(100, $this->get('error.message'));
return ($this->_extract($archive, $destination, $options))?
true :
JError::raiseWarning(100, $this->get('error.message'));
* Method to determine if the server has native zip support for faster handling
* @return boolean True if php has native ZIP support
* Checks to see if the data is a valid ZIP file.
* @param string $data ZIP archive data buffer
* @return boolean True if valid, false if invalid.
* Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
* @param array $options Extraction options [unused]
* @return boolean True if successful
function _extract($archive, $destination, $options)
$this->set('error.message', 'Zlib Not Supported');
$this->set('error.message', 'Unable to read archive');
if (!$this->_getZipInfo($this->_data)) {
$buffer =
$this->_getFileData($i);
// Make sure the destination folder exists
$this->set('error.message', 'Unable to create destination');
$this->set('error.message', 'Unable to write entry');
* Extract a ZIP compressed file to a given path using native php api calls for speed
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
* @param array $options Extraction options [unused]
* @return boolean True if successful
function _extractNative($archive, $destination, $options)
if ($zip =
zip_open($archive)) {
// Make sure the destination folder exists
$this->set('error.message', 'Unable to create destination');
// Read files in the archive
while ($file =
zip_read($zip))
if (zip_entry_open($zip, $file, "r")) {
if (substr(zip_entry_name($file), strlen(zip_entry_name($file)) -
1) !=
"/") {
$buffer =
zip_entry_read($file, zip_entry_filesize($file));
if (JFile::write($destination.
DS.
zip_entry_name($file), $buffer) ===
false) {
$this->set('error.message', 'Unable to write entry');
$this->set('error.message', 'Unable to read entry');
$this->set('error.message', 'Unable to open archive');
* Get the list of files/data from a ZIP archive buffer.
* @param string $data The ZIP archive buffer.
* @return array Archive metadata array
* KEY: Position in zipfile
* VALUES: 'attr' -- File attributes
* 'csize' -- Compressed file size
* 'date' -- File modification time
* 'method' -- Compression method
* 'size' -- Original file size
function _getZipInfo(& $data)
// Get details from Central directory structure.
if (strlen($data) <
$fhStart +
31) {
$this->set('error.message', 'Invalid ZIP data');
$info =
unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart +
10, 20));
$name =
substr($data, $fhStart +
46, $info['Length']);
$entries[$name] =
array('attr' =>
null, 'crc' =>
sprintf("%08s", dechex($info['CRC32'] )), 'csize' =>
$info['Compressed'], 'date' =>
null, '_dataStart' =>
null, 'name' =>
$name, 'method' =>
$this->_methods[$info['Method']], '_method' =>
$info['Method'], 'size' =>
$info['Uncompressed'], 'type' =>
null);
$entries[$name]['date'] =
mktime((($info['Time'] >>
11) & 0x1f), (($info['Time'] >>
5) & 0x3f), (($info['Time'] <<
1) & 0x3e), (($info['Time'] >>
21) & 0x07), (($info['Time'] >>
16) & 0x1f), ((($info['Time'] >>
25) & 0x7f) +
1980));
if (strlen($data) <
$fhStart +
43) {
$this->set('error.message', 'Invalid ZIP data');
$info =
unpack('vInternal/VExternal', substr($data, $fhStart +
36, 6));
$entries[$name]['type'] =
($info['Internal'] & 0x01) ?
'text' :
'binary';
$entries[$name]['attr'] =
(($info['External'] & 0x10) ?
'D' :
'-') .
(($info['External'] & 0x20) ?
'A' :
'-') .
(($info['External'] & 0x03) ?
'S' :
'-') .
(($info['External'] & 0x02) ?
'H' :
'-') .
(($info['External'] & 0x01) ?
'R' :
'-');
// Get details from local file header.
if (strlen($data) <
$fhStart +
34) {
$this->set('error.message', 'Invalid ZIP data');
$info =
unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $fhStart +
8, 25));
$name =
substr($data, $fhStart +
30, $info['Length']);
$entries[$name]['_dataStart'] =
$fhStart +
30 +
$info['Length'] +
$info['ExtraLength'];
} while (strlen($data) >
$fhStart +
30 +
$info['Length'] &&
($fhStart =
strpos($data, $this->_fileHeader, $fhStart +
30 +
$info['Length'])) !==
false);
* Returns the file data for a file by offsest in the ZIP archive
* @param int $key The position of the file in the archive.
* @return string Uncompresed file data buffer
function _getFileData($key) {
if ($this->_metadata[$key]['_method'] ==
0x8) {
// If zlib extention is loaded use it
elseif ($this->_metadata[$key]['_method'] ==
0x0) {
/* Files that aren't compressed. */
} elseif ($this->_metadata[$key]['_method'] ==
0x12) {
// Is bz2 extension loaded? If not try to load it
// If bz2 extention is sucessfully loaded use it
* Converts a UNIX timestamp to a 4-byte DOS date and time format
* (date in high 2-bytes, time in low 2-bytes allowing magnitude
* @param int $unixtime The current UNIX timestamp.
* @return int The current date in a 4-byte DOS format.
function _unix2DOSTime($unixtime =
null) {
if ($timearray['year'] <
1980) {
$timearray['year'] =
1980;
$timearray['minutes'] =
0;
$timearray['seconds'] =
0;
return (($timearray['year'] -
1980) <<
25) |
($timearray['mon'] <<
21) |
($timearray['mday'] <<
16) |
($timearray['hours'] <<
11) |
($timearray['minutes'] <<
5) |
($timearray['seconds'] >>
1);
* Adds a "file" to the ZIP archive.
* @todo Review and finish implementation
* @param array $file File data array to add
* @param array $contents An array of existing zipped files.
* @param array $ctrldir An array of central directory information.
function _addToZIPFile(& $file, & $contents, & $ctrldir) {
/* See if time/date information has been provided. */
if (isset
($file['time'])) {
$dtime =
dechex($this->_unix2DosTime($ftime));
$hexdtime =
chr(hexdec($dtime[6] .
$dtime[7])) .
$fr =
$this->_fileHeader; /* Begin creating the ZIP data. */
$fr .=
"\x14\x00"; /* Version needed to extract. */
$fr .=
"\x00\x00"; /* General purpose bit flag. */
$fr .=
"\x08\x00"; /* Compression method. */
$fr .=
$hexdtime; /* Last modification time/date. */
/* "Local file header" segment. */
$fr .=
pack('V', $crc); /* CRC 32 information. */
$fr .=
pack('V', $c_len); /* Compressed filesize. */
$fr .=
pack('V', $unc_len); /* Uncompressed filesize. */
$fr .=
pack('v', strlen($name)); /* Length of filename. */
$fr .=
pack('v', 0); /* Extra field length. */
$fr .=
$name; /* File name. */
/* "File data" segment. */
/* Add this entry to array. */
/* Add to central directory record. */
$cdrec .=
"\x00\x00"; /* Version made by. */
$cdrec .=
"\x14\x00"; /* Version needed to extract */
$cdrec .=
"\x00\x00"; /* General purpose bit flag */
$cdrec .=
"\x08\x00"; /* Compression method */
$cdrec .=
$hexdtime; /* Last mod time/date. */
$cdrec .=
pack('V', $crc); /* CRC 32 information. */
$cdrec .=
pack('V', $c_len); /* Compressed filesize. */
$cdrec .=
pack('V', $unc_len); /* Uncompressed filesize. */
$cdrec .=
pack('v', strlen($name)); /* Length of filename. */
$cdrec .=
pack('v', 0); /* Extra field length. */
$cdrec .=
pack('v', 0); /* File comment length. */
$cdrec .=
pack('v', 0); /* Disk number start. */
$cdrec .=
pack('v', 0); /* Internal file attributes. */
$cdrec .=
pack('V', 32); /* External file attributes -
$cdrec .=
pack('V', $old_offset); /* Relative offset of local
$cdrec .=
$name; /* File name. */
/* Optional extra field, file comment goes here. */
// Save to central directory array. */
* Official ZIP file format: http://www.pkware.com/appnote.txt
* @todo Review and finish implementation
* @param array $contents An array of existing zipped files.
* @param array $ctrldir An array of central directory information.
* @param string $path The path to store the archive.
* @return boolean True if successful
function _createZIPFile(& $contents, & $ctrlDir, $path)
/* Total # of entries "on this disk". */
/* Total # of entries overall. */
/* Size of central directory. */
/* Offset to start of central dir. */
/* ZIP file comment length. */