Source for file zip.php
Documentation is available at zip.php
* @package Joomla.Platform
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
jimport('joomla.filesystem.folder');
* ZIP format adapter for the JArchive class
* The ZIP compression code is partially based on code from:
* Eric Mueller <eric@themepark.com>
* http://www.zend.com/codex.php?id=535&single=1
* Deins125 <webmaster@atlant.ru>
* http://www.zend.com/codex.php?id=470&single=1
* The ZIP compression date code is partially based on code from
* Peter Listiak <mlady@users.sourceforge.net>
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
* @contributor Chuck Hagenbuch <chuck@horde.org>
* @contributor Michael Slusarz <slusarz@horde.org>
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
* @package Joomla.Platform
* ZIP compression methods.
private $_methods =
array(0x0 =>
'None', 0x1 =>
'Shrunk', 0x2 =>
'Super Fast', 0x3 =>
'Fast', 0x4 =>
'Normal', 0x5 =>
'Maximum', 0x6 =>
'Imploded',
* Beginning of central directory record.
private $_ctrlDirHeader =
"\x50\x4b\x01\x02";
* End of central directory record.
private $_ctrlDirEnd =
"\x50\x4b\x05\x06\x00\x00\x00\x00";
* Beginning of file contents.
private $_fileHeader =
"\x50\x4b\x03\x04";
* ZIP file metadata array
private $_metadata =
null;
* Create a ZIP compressed file from an array of file data.
* @param string $archive Path to save archive.
* @param array $files Array of files to add to archive.
* @return boolean True if successful.
* @todo Finish Implementation
public function create($archive, $files)
foreach ($files as $file)
$this->_addToZIPFile($file, $contents, $ctrldir);
return $this->_createZIPFile($contents, $ctrldir, $archive);
* 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
* @throws RuntimeException
public function extract($archive, $destination, array $options =
array())
throw
new RuntimeException('Archive does not exist');
* Tests whether this adapter can unpack files on this computer.
* @return boolean True if supported
* 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.
if (strpos($data, $this->_fileHeader) ===
false)
* 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.
* @return mixed True if successful
* @throws RuntimeException
throw
new RuntimeException('Zlib not supported');
throw
new RuntimeException('Unable to read archive (zip)');
if (!$this->_readZipInfo($this->_data))
throw
new RuntimeException('Get ZIP Information failed');
for ($i =
0, $n =
count($this->_metadata); $i <
$n; $i++
)
$lastPathCharacter =
substr($this->_metadata[$i]['name'], -
1, 1);
if ($lastPathCharacter !==
'/' &&
$lastPathCharacter !==
'\\')
$buffer =
$this->_getFileData($i);
$path =
JPath::clean($destination .
'/' .
$this->_metadata[$i]['name']);
// Make sure the destination folder exists
throw
new RuntimeException('Unable to create destination');
throw
new RuntimeException('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
* @return boolean True on success
* @throws RuntimeException
// Make sure the destination folder exists
throw
new RuntimeException('Unable to create destination');
// Read files in the archive
throw
new RuntimeException('Unable to write entry');
throw
new RuntimeException('Unable to read entry');
throw
new RuntimeException('Unable to open archive');
* Get the list of files/data from a ZIP archive buffer.
* KEY: Position in zipfile
* VALUES: 'attr' -- File attributes
* 'csize' -- Compressed file size
* 'date' -- File modification time
* 'method'-- Compression method
* 'size' -- Original file size
* @param string &$data The ZIP archive buffer.
* @return boolean True on success
* @throws RuntimeException
private function _readZipInfo(&$data)
// Find the last central directory header entry
$fhLast =
strpos($data, $this->_ctrlDirEnd);
while (($fhLast =
strpos($data, $this->_ctrlDirEnd, $fhLast +
1)) !==
false);
// Find the central directory offset
$endOfCentralDirectory =
unpack(
'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' .
'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength',
$offset =
$endOfCentralDirectory['CentralDirectoryOffset'];
// Get details from central directory structure.
$fhStart =
strpos($data, $this->_ctrlDirHeader, $offset);
if ($dataLength <
$fhStart +
31)
throw
new RuntimeException('Invalid Zip Data');
$info =
unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart +
10, 20));
$name =
substr($data, $fhStart +
46, $info['Length']);
'csize' =>
$info['Compressed'],
'method' =>
$this->_methods[$info['Method']],
'_method' =>
$info['Method'],
'size' =>
$info['Uncompressed'],
$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 ($dataLength <
$fhStart +
43)
throw
new RuntimeException('Invalid ZIP data');
$info =
unpack('vInternal/VExternal/VOffset', substr($data, $fhStart +
36, 10));
$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' :
'-');
$entries[$name]['offset'] =
$info['Offset'];
// Get details from local file header since we have the offset
$lfhStart =
strpos($data, $this->_fileHeader, $entries[$name]['offset']);
if ($dataLength <
$lfhStart +
34)
throw
new RuntimeException('Invalid Zip Data');
$info =
unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $lfhStart +
8, 25));
$name =
substr($data, $lfhStart +
30, $info['Length']);
$entries[$name]['_dataStart'] =
$lfhStart +
30 +
$info['Length'] +
$info['ExtraLength'];
// Bump the max execution time because not using the built in php zip libs makes this process slow.
while ((($fhStart =
strpos($data, $this->_ctrlDirHeader, $fhStart +
46)) !==
false));
* Returns the file data for a file by offsest in the ZIP archive
* @param integer $key The position of the file in the archive.
* @return string Uncompressed file data buffer.
private function _getFileData($key)
if ($this->_metadata[$key]['_method'] ==
0x8)
return gzinflate(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']));
elseif ($this->_metadata[$key]['_method'] ==
0x0)
/* Files that aren't compressed. */
return substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']);
elseif ($this->_metadata[$key]['_method'] ==
0x12)
// If bz2 extension is loaded use it
return bzdecompress(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']));
* 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.
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.
* @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.
* @todo Review and finish implementation
private function _addToZIPFile(array &$file, array &$contents, array &$ctrldir)
$name =
str_replace('\\', '/', $file['name']);
/* See if time/date information has been provided. */
if (isset
($file['time']))
$dtime =
dechex($this->_unix2DosTime($ftime));
/* Begin creating the ZIP data. */
$fr =
$this->_fileHeader;
/* Version needed to extract. */
/* General purpose bit flag. */
/* Compression method. */
/* Last modification time/date. */
/* "Local file header" segment. */
/* CRC 32 information. */
/* Compressed filesize. */
$fr .=
pack('V', $c_len);
/* Uncompressed filesize. */
$fr .=
pack('V', $unc_len);
/* Length of filename. */
/* Extra field length. */
/* "File data" segment. */
/* Add this entry to array. */
/* Add to central directory record. */
$cdrec =
$this->_ctrlDirHeader;
/* Version needed to extract */
/* General purpose bit flag */
/* Last mod time/date. */
/* CRC 32 information. */
$cdrec .=
pack('V', $crc);
/* Compressed filesize. */
$cdrec .=
pack('V', $c_len);
/* Uncompressed filesize. */
$cdrec .=
pack('V', $unc_len);
/* Length of filename. */
/* Extra field length. */
/* File comment length. */
/* Internal file attributes. */
/* External file attributes -'archive' bit set. */
/* Relative offset of local header. */
$cdrec .=
pack('V', $old_offset);
/* Optional extra field, file comment goes here. */
/* Save to central directory array. */
* Official ZIP file format: http://www.pkware.com/appnote.txt
* @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
* @todo Review and finish implementation
private function _createZIPFile(array &$contents, array &$ctrlDir, $path)
$data =
implode('', $contents);
$buffer =
$data .
$dir .
$this->_ctrlDirEnd .
/* Total # of entries "on this disk". */
pack('v', count($ctrlDir)) .
/* Total # of entries overall. */
pack('v', count($ctrlDir)) .
/* Size of central directory. */
pack('V', strlen($dir)) .
/* Offset to start of central dir. */
pack('V', strlen($data)) .
/* ZIP file comment length. */
if (JFile::write($path, $buffer) ===
false)
Documentation generated on Tue, 19 Nov 2013 15:18:40 +0100 by phpDocumentor 1.4.3