<?php
/**
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*
* This class serves as a concrete zip file archive.
*
*/
namespace PHPZip\Zip\File;
use com\grandt\BinStringStatic;
use PHPZip\Zip\Core\AbstractZipArchive;
class Zip extends AbstractZipArchive {
const MEMORY_THRESHOLD = 1048576; // 1 MB - Auto create temp file if the zip data exceeds this
const STREAM_CHUNK_SIZE = 65536; // 64 KB
private $_zipData = null;
private $_zipFile = null;
/**
* Constructor.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*
* @param boolean $useZipFile Write temp zip data to tempFile? Default FALSE
*
* @throws \PHPZip\Zip\Exception\InvalidPhpConfiguration In case of errors
*/
public function __construct($useZipFile = false) {
parent::__construct(self::STREAM_CHUNK_SIZE);
if ($useZipFile) {
$this->_zipFile = tmpfile();
} else {
$this->_zipData = '';
}
}
/**
* Destructor.
* Perform clean up actions.
*
* @author A. Grandt <php@grandt.com>
*/
public function __destruct() {
if (is_resource($this->_zipFile)) {
fclose($this->_zipFile);
}
$this->_zipData = null;
}
/**
* Set zip file to write zip data to.
* This will cause all present and future data written to this class to be written to this file.
* This can be used at any time, even after the Zip Archive have been finalized. Any previous file will be closed.
* Warning: If the given file already exists, it will be overwritten.
*
* @author A. Grandt <php@grandt.com>
*
* @param string $fileName
*
* @return bool Success
*/
public function setZipFile($fileName) {
if (is_file($fileName)) {
unlink($fileName);
}
$fd = fopen($fileName, "x+b");
if (is_resource($this->_zipFile)) {
rewind($this->_zipFile);
while (!feof($this->_zipFile)) {
fwrite($fd, fread($this->_zipFile, $this->streamChunkSize));
}
fclose($this->_zipFile);
} else {
fwrite($fd, $this->_zipData);
$this->_zipData = null;
}
$this->_zipFile = $fd;
return true;
}
/**
* Alias for setZipFile
*
* @param $fileName
*
* @return bool
*/
public function saveZipFile($fileName) {
return $this->setZipFile($fileName);
}
/**
* Get the handle resource for the archive zip file.
* If the zip haven't been finalized yet, this will cause it to become finalized
*
* @author A. Grandt <php@grandt.com>
*
* @return resource zip file handle
*/
public function getZipFile() {
if (!$this->isFinalized) {
$this->finalize();
}
$this->zipFlush();
rewind($this->_zipFile);
return $this->_zipFile;
}
/**
* Send the archive as a zip download
*
* @author A. Grandt <php@grandt.com>
*
* @param String $fileName The name of the Zip archive, in ISO-8859-1 (or ASCII) encoding,
* ie. "archive.zip". Optional, defaults to null, which means that
* no ISO-8859-1 encoded file name will be specified.
* @param String $contentType Content mime type. Optional, defaults to "application/zip".
* @param String $utf8FileName The name of the Zip archive, in UTF-8 encoding. Optional, defaults
* to null, which means that no UTF-8 encoded file name will be specified.
* @param bool $inline Use Content-Disposition with "inline" instead of "attachment". Optional, defaults to false.
*
* @return bool $success
*/
public function sendZip($fileName = null, $contentType = self::CONTENT_TYPE, $utf8FileName = null, $inline = false) {
if (!$this->isFinalized) {
$this->finalize();
}
if ($this->buildResponseHeader($fileName, $contentType, $utf8FileName, $inline)) {
return true;
}
return false;
}
/**
* Get the zip file contents
* If the zip haven't been finalized yet, this will cause it to become finalized
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*
* @return string zip data
*/
public function getZipData() {
$result = null;
if (!$this->isFinalized) {
$this->finalize();
}
if (!is_resource($this->_zipFile)) {
$result = $this->_zipData;
} else {
rewind($this->_zipFile);
$stat = fstat($this->_zipFile);
$result = fread($this->_zipFile, $stat['size']);
}
return $result;
}
/**
* Return the current size of the archive
*
* @author A. Grandt <php@grandt.com>
*
* @return int Size of the archive
*/
public function getArchiveSize() {
if (!is_resource($this->_zipFile)) {
return BinStringStatic::_strlen($this->_zipData);
}
$stat = fstat($this->_zipFile);
return $stat['size'];
}
/*
* ************************************************************************
* Superclass callbacks.
* ************************************************************************
*/
/**
* Called by superclass when specialised action is needed
* while building a zip entry.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*
* @param array $params Array that contains zipEntry.
*/
public function onBuildZipEntry(array $params) {
$this->zipWrite($params['zipEntry']);
}
/**
* Called by superclass when specialised action is needed
* at the start of adding a file to the archive.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*
* @param array $params Array that contains gzLength.
*/
public function onBeginAddFile(array $params) {
if (!is_resource($this->_zipFile) && ($this->offset + $params['gzLength']) > self::MEMORY_THRESHOLD) {
$this->zipFlush();
}
}
/**
* Called by superclass when specialised action is needed
* at the end of adding a file to the archive.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*
* @param array $params Array that contains gzData.
*/
public function onEndAddFile(array $params) {
$this->zipWrite($params['gzData']);
}
/**
* Called by superclass when specialised action is needed
* at the start of sending the zip file response header.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*/
public function onBeginBuildResponseHeader() {
if (!$this->isFinalized) {
$this->finalize();
}
}
/**
* Called by superclass when specialised action is needed
* at the end of sending the zip file response header.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*/
public function onEndBuildResponseHeader() {
header('Connection: close');
header('Content-Length: ' . $this->getArchiveSize());
if (!is_resource($this->_zipFile)) {
echo $this->_zipData;
} else {
rewind($this->_zipFile);
while (!feof($this->_zipFile)) {
echo fread($this->_zipFile, $this->streamChunkSize);
}
}
}
/**
* Called by superclass when specialised action is needed
* while opening a stream.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*/
public function onOpenStream() {
$this->zipFlush();
}
/**
* Called by superclass when specialised action is needed
* while processing a file.
*
* @author A. Grandt <php@grandt.com>
* @author Greg Kappatos
*
* @param array $params Array that contains data.
*/
public function onProcessFile(array $params) {
$this->zipWrite($params['data']);
}
/**
* Verify if the memory buffer is about to be exceeded.
*
* @author A. Grandt <php@grandt.com>
*
* @param int $gzLength length of the pending data.
*/
public function zipVerifyMemBuffer($gzLength) {
if (!is_resource($this->_zipFile) && ($this->offset + $gzLength) > self::MEMORY_THRESHOLD) {
$this->zipFlush();
}
}
/**
*
* @author A. Grandt <php@grandt.com>
*
* @param string $data
*/
public function zipWrite($data) {
if (!is_resource($this->_zipFile)) {
$this->_zipData .= $data;
} else {
fwrite($this->_zipFile, $data);
fflush($this->_zipFile);
}
}
/**
* Flush Zip Data stored in memory, to a temp file.
*
* @author A. Grandt <php@grandt.com>
*
*/
public function zipFlush() {
if (!is_resource($this->_zipFile)) {
$this->_zipFile = tmpfile();
fwrite($this->_zipFile, $this->_zipData);
$this->_zipData = null;
}
}
/**
*
* @author A. Grandt <php@grandt.com>
*
*/
public function zipFlushBuffer() {
// Does nothing.
}
}