mirror of
https://github.com/moodle/moodle.git
synced 2025-04-15 13:33:52 +02:00
Merge branch 'master_MDL-76923' of https://github.com/mattporritt/moodle
This commit is contained in:
commit
6ebfc73e55
@ -584,7 +584,7 @@ All rights reserved.</copyright>
|
||||
<location>zipstream</location>
|
||||
<name>ZipStream-PHP</name>
|
||||
<description>PHP ZIP Streaming Library</description>
|
||||
<version>2.2.0</version>
|
||||
<version>2.4.0</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/maennchen/ZipStream-PHP</repository>
|
||||
<copyrights>
|
||||
|
@ -6,3 +6,9 @@ Instructions to import ZipStream into Moodle:
|
||||
|
||||
3/ Ensure any dependencies are also imported (eg psr/http-message and myclabs/php-enum).
|
||||
The dependencies will be listed in the "require" section of the library's composer.json file
|
||||
|
||||
Local changes:
|
||||
* 2023/03/03 - Version 3.0.0 of the ZipStream library uses "readonly", "enum" and "First-class Callable Syntax"
|
||||
properties that are only supported by PHP 8.1 and above.
|
||||
As Moodle 4.2 only requires PHP 8.0 version 2.4.0 (released 9/12/2022) has been used instead.
|
||||
When Moodle requires PHP 8.1 version 3.0.0 can be used.
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream;
|
||||
@ -22,22 +23,6 @@ class Bigint
|
||||
$this->fillBytes($value, 0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the bytes field with int
|
||||
*
|
||||
* @param int $value
|
||||
* @param int $start
|
||||
* @param int $count
|
||||
* @return void
|
||||
*/
|
||||
protected function fillBytes(int $value, int $start, int $count): void
|
||||
{
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF;
|
||||
$value >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance
|
||||
*
|
||||
@ -58,7 +43,7 @@ class Bigint
|
||||
*/
|
||||
public static function fromLowHigh(int $low, int $high): self
|
||||
{
|
||||
$bigint = new Bigint();
|
||||
$bigint = new self();
|
||||
$bigint->fillBytes($low, 0, 4);
|
||||
$bigint->fillBytes($high, 4, 4);
|
||||
return $bigint;
|
||||
@ -150,7 +135,7 @@ class Bigint
|
||||
* @param Bigint $other
|
||||
* @return Bigint
|
||||
*/
|
||||
public function add(Bigint $other): Bigint
|
||||
public function add(self $other): self
|
||||
{
|
||||
$result = clone $this;
|
||||
$overflow = false;
|
||||
@ -166,8 +151,24 @@ class Bigint
|
||||
}
|
||||
}
|
||||
if ($overflow) {
|
||||
throw new OverflowException;
|
||||
throw new OverflowException();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the bytes field with int
|
||||
*
|
||||
* @param int $value
|
||||
* @param int $start
|
||||
* @param int $count
|
||||
* @return void
|
||||
*/
|
||||
protected function fillBytes(int $value, int $start, int $count): void
|
||||
{
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF;
|
||||
$value >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,70 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class DeflateStream extends Stream
|
||||
{
|
||||
protected $filter;
|
||||
|
||||
/**
|
||||
* @var Option\File
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Rewind stream
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind(): void
|
||||
public function __construct($stream)
|
||||
{
|
||||
// deflate filter needs to be removed before rewind
|
||||
if ($this->filter) {
|
||||
$this->removeDeflateFilter();
|
||||
$this->seek(0);
|
||||
$this->addDeflateFilter($this->options);
|
||||
} else {
|
||||
rewind($this->stream);
|
||||
}
|
||||
parent::__construct($stream);
|
||||
trigger_error('Class ' . __CLASS__ . ' is deprecated, delation will be handled internally instead', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the deflate filter
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeDeflateFilter(): void
|
||||
{
|
||||
if (!$this->filter) {
|
||||
return;
|
||||
}
|
||||
stream_filter_remove($this->filter);
|
||||
$this->filter = null;
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a deflate filter
|
||||
*
|
||||
* @param Option\File $options
|
||||
* @return void
|
||||
*/
|
||||
public function addDeflateFilter(Option\File $options): void
|
||||
{
|
||||
$this->options = $options;
|
||||
// parameter 4 for stream_filter_append expects array
|
||||
// so we convert the option object in an array
|
||||
$optionsArr = [
|
||||
'comment' => $options->getComment(),
|
||||
'method' => $options->getMethod(),
|
||||
'deflateLevel' => $options->getDeflateLevel(),
|
||||
'time' => $options->getTime()
|
||||
];
|
||||
$this->filter = stream_filter_append(
|
||||
$this->stream,
|
||||
'zlib.deflate',
|
||||
STREAM_FILTER_READ,
|
||||
$optionsArr
|
||||
);
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Exception;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Exception;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Exception;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Exception;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Exception;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Exception;
|
||||
|
@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream;
|
||||
|
||||
use HashContext;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use RuntimeException;
|
||||
use ZipStream\Exception\EncodingException;
|
||||
use ZipStream\Exception\FileNotFoundException;
|
||||
use ZipStream\Exception\FileNotReadableException;
|
||||
use ZipStream\Exception\OverflowException;
|
||||
@ -15,13 +15,15 @@ use ZipStream\Option\Version;
|
||||
|
||||
class File
|
||||
{
|
||||
const HASH_ALGORITHM = 'crc32b';
|
||||
public const HASH_ALGORITHM = 'crc32b';
|
||||
|
||||
const BIT_ZERO_HEADER = 0x0008;
|
||||
const BIT_EFS_UTF8 = 0x0800;
|
||||
public const BIT_ZERO_HEADER = 0x0008;
|
||||
|
||||
const COMPUTE = 1;
|
||||
const SEND = 2;
|
||||
public const BIT_EFS_UTF8 = 0x0800;
|
||||
|
||||
public const COMPUTE = 1;
|
||||
|
||||
public const SEND = 2;
|
||||
|
||||
private const CHUNKED_READ_BLOCK_SIZE = 1048576;
|
||||
|
||||
@ -39,6 +41,7 @@ class File
|
||||
* @var Bigint
|
||||
*/
|
||||
public $len;
|
||||
|
||||
/**
|
||||
* @var Bigint
|
||||
*/
|
||||
@ -78,7 +81,7 @@ class File
|
||||
private $deflate;
|
||||
|
||||
/**
|
||||
* @var \HashContext
|
||||
* @var HashContext
|
||||
*/
|
||||
private $hash;
|
||||
|
||||
@ -117,7 +120,7 @@ class File
|
||||
} else {
|
||||
$this->method = $this->zip->opt->getLargeFileMethod();
|
||||
|
||||
$stream = new DeflateStream(fopen($path, 'rb'));
|
||||
$stream = new Stream(fopen($path, 'rb'));
|
||||
$this->processStream($stream);
|
||||
$stream->close();
|
||||
}
|
||||
@ -162,21 +165,17 @@ class File
|
||||
// Sets Bit 11: Language encoding flag (EFS). If this bit is set,
|
||||
// the filename and comment fields for this file
|
||||
// MUST be encoded using UTF-8. (see APPENDIX D)
|
||||
if (!mb_check_encoding($name, 'UTF-8') ||
|
||||
!mb_check_encoding($comment, 'UTF-8')) {
|
||||
throw new EncodingException(
|
||||
'File name and comment should use UTF-8 ' .
|
||||
'if one of them does not fit into ASCII range.'
|
||||
);
|
||||
if (mb_check_encoding($name, 'UTF-8') &&
|
||||
mb_check_encoding($comment, 'UTF-8')) {
|
||||
$this->bits |= self::BIT_EFS_UTF8;
|
||||
}
|
||||
$this->bits |= self::BIT_EFS_UTF8;
|
||||
}
|
||||
|
||||
if ($this->method->equals(Method::DEFLATE())) {
|
||||
$this->version = Version::DEFLATE();
|
||||
}
|
||||
|
||||
$force = (boolean)($this->bits & self::BIT_ZERO_HEADER) &&
|
||||
$force = (bool)($this->bits & self::BIT_ZERO_HEADER) &&
|
||||
$this->zip->opt->isEnableZip64();
|
||||
|
||||
$footer = $this->buildZip64ExtraBlock($force);
|
||||
@ -228,6 +227,97 @@ class File
|
||||
return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and send data descriptor footer for this file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addFileFooter(): void
|
||||
{
|
||||
if ($this->bits & self::BIT_ZERO_HEADER) {
|
||||
// compressed and uncompressed size
|
||||
$sizeFormat = 'V';
|
||||
if ($this->zip->opt->isEnableZip64()) {
|
||||
$sizeFormat = 'P';
|
||||
}
|
||||
$fields = [
|
||||
['V', ZipStream::DATA_DESCRIPTOR_SIGNATURE],
|
||||
['V', $this->crc], // CRC32
|
||||
[$sizeFormat, $this->zlen], // Length of compressed data
|
||||
[$sizeFormat, $this->len], // Length of original data
|
||||
];
|
||||
|
||||
$footer = ZipStream::packFields($fields);
|
||||
$this->zip->send($footer);
|
||||
} else {
|
||||
$footer = '';
|
||||
}
|
||||
$this->totalLength = $this->hlen->add($this->zlen)->add(Bigint::init(strlen($footer)));
|
||||
$this->zip->addToCdr($this);
|
||||
}
|
||||
|
||||
public function processStream(StreamInterface $stream): void
|
||||
{
|
||||
$this->zlen = new Bigint();
|
||||
$this->len = new Bigint();
|
||||
|
||||
if ($this->zip->opt->isZeroHeader()) {
|
||||
$this->processStreamWithZeroHeader($stream);
|
||||
} else {
|
||||
$this->processStreamWithComputedHeader($stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send CDR record for specified file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCdrFile(): string
|
||||
{
|
||||
$name = static::filterFilename($this->name);
|
||||
|
||||
// get attributes
|
||||
$comment = $this->opt->getComment();
|
||||
|
||||
// get dos timestamp
|
||||
$time = static::dosTime($this->opt->getTime()->getTimestamp());
|
||||
|
||||
$footer = $this->buildZip64ExtraBlock();
|
||||
|
||||
$fields = [
|
||||
['V', ZipStream::CDR_FILE_SIGNATURE], // Central file header signature
|
||||
['v', ZipStream::ZIP_VERSION_MADE_BY], // Made by version
|
||||
['v', $this->version->getValue()], // Extract by version
|
||||
['v', $this->bits], // General purpose bit flags - data descriptor flag set
|
||||
['v', $this->method->getValue()], // Compression method
|
||||
['V', $time], // Timestamp (DOS Format)
|
||||
['V', $this->crc], // CRC32
|
||||
['V', $this->zlen->getLowFF()], // Compressed Data Length
|
||||
['V', $this->len->getLowFF()], // Original Data Length
|
||||
['v', strlen($name)], // Length of filename
|
||||
['v', strlen($footer)], // Extra data len (see above)
|
||||
['v', strlen($comment)], // Length of comment
|
||||
['v', 0], // Disk number
|
||||
['v', 0], // Internal File Attributes
|
||||
['V', 32], // External File Attributes
|
||||
['V', $this->ofs->getLowFF()], // Relative offset of local header
|
||||
];
|
||||
|
||||
// pack fields, then append name and comment
|
||||
$header = ZipStream::packFields($fields);
|
||||
|
||||
return $header . $name . $footer . $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Bigint
|
||||
*/
|
||||
public function getTotalLength(): Bigint
|
||||
{
|
||||
return $this->totalLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a UNIX timestamp to a DOS timestamp.
|
||||
*
|
||||
@ -241,14 +331,14 @@ class File
|
||||
|
||||
// set lower-bound on dates
|
||||
if ($d['year'] < 1980) {
|
||||
$d = array(
|
||||
$d = [
|
||||
'year' => 1980,
|
||||
'mon' => 1,
|
||||
'mday' => 1,
|
||||
'hours' => 0,
|
||||
'minutes' => 0,
|
||||
'seconds' => 0
|
||||
);
|
||||
'seconds' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
// remove extra years from 1980
|
||||
@ -266,7 +356,6 @@ class File
|
||||
|
||||
protected function buildZip64ExtraBlock(bool $force = false): string
|
||||
{
|
||||
|
||||
$fields = [];
|
||||
if ($this->len->isOver32($force)) {
|
||||
$fields[] = ['P', $this->len]; // Length of original data
|
||||
@ -303,49 +392,6 @@ class File
|
||||
return ZipStream::packFields($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and send data descriptor footer for this file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
public function addFileFooter(): void
|
||||
{
|
||||
|
||||
if ($this->bits & self::BIT_ZERO_HEADER) {
|
||||
// compressed and uncompressed size
|
||||
$sizeFormat = 'V';
|
||||
if ($this->zip->opt->isEnableZip64()) {
|
||||
$sizeFormat = 'P';
|
||||
}
|
||||
$fields = [
|
||||
['V', ZipStream::DATA_DESCRIPTOR_SIGNATURE],
|
||||
['V', $this->crc], // CRC32
|
||||
[$sizeFormat, $this->zlen], // Length of compressed data
|
||||
[$sizeFormat, $this->len], // Length of original data
|
||||
];
|
||||
|
||||
$footer = ZipStream::packFields($fields);
|
||||
$this->zip->send($footer);
|
||||
} else {
|
||||
$footer = '';
|
||||
}
|
||||
$this->totalLength = $this->hlen->add($this->zlen)->add(Bigint::init(strlen($footer)));
|
||||
$this->zip->addToCdr($this);
|
||||
}
|
||||
|
||||
public function processStream(StreamInterface $stream): void
|
||||
{
|
||||
$this->zlen = new Bigint();
|
||||
$this->len = new Bigint();
|
||||
|
||||
if ($this->zip->opt->isZeroHeader()) {
|
||||
$this->processStreamWithZeroHeader($stream);
|
||||
} else {
|
||||
$this->processStreamWithComputedHeader($stream);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processStreamWithZeroHeader(StreamInterface $stream): void
|
||||
{
|
||||
$this->bits |= self::BIT_ZERO_HEADER;
|
||||
@ -363,7 +409,7 @@ class File
|
||||
$data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE);
|
||||
$total += strlen($data);
|
||||
if ($size > 0 && $total > $size) {
|
||||
$data = substr($data, 0 , strlen($data)-($total - $size));
|
||||
$data = substr($data, 0, strlen($data)-($total - $size));
|
||||
}
|
||||
$this->deflateData($stream, $data, $options);
|
||||
if ($options & self::SEND) {
|
||||
@ -417,71 +463,8 @@ class File
|
||||
$this->readStream($stream, self::COMPUTE);
|
||||
$stream->rewind();
|
||||
|
||||
// incremental compression with deflate_add
|
||||
// makes this second read unnecessary
|
||||
// but it is only available from PHP 7.0
|
||||
if (!$this->deflate && $stream instanceof DeflateStream && $this->method->equals(Method::DEFLATE())) {
|
||||
$stream->addDeflateFilter($this->opt);
|
||||
$this->zlen = new Bigint();
|
||||
while (!$stream->eof()) {
|
||||
$data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE);
|
||||
$this->zlen = $this->zlen->add(Bigint::init(strlen($data)));
|
||||
}
|
||||
$stream->rewind();
|
||||
}
|
||||
|
||||
$this->addFileHeader();
|
||||
$this->readStream($stream, self::SEND);
|
||||
$this->addFileFooter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send CDR record for specified file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCdrFile(): string
|
||||
{
|
||||
$name = static::filterFilename($this->name);
|
||||
|
||||
// get attributes
|
||||
$comment = $this->opt->getComment();
|
||||
|
||||
// get dos timestamp
|
||||
$time = static::dosTime($this->opt->getTime()->getTimestamp());
|
||||
|
||||
$footer = $this->buildZip64ExtraBlock();
|
||||
|
||||
$fields = [
|
||||
['V', ZipStream::CDR_FILE_SIGNATURE], // Central file header signature
|
||||
['v', ZipStream::ZIP_VERSION_MADE_BY], // Made by version
|
||||
['v', $this->version->getValue()], // Extract by version
|
||||
['v', $this->bits], // General purpose bit flags - data descriptor flag set
|
||||
['v', $this->method->getValue()], // Compression method
|
||||
['V', $time], // Timestamp (DOS Format)
|
||||
['V', $this->crc], // CRC32
|
||||
['V', $this->zlen->getLowFF()], // Compressed Data Length
|
||||
['V', $this->len->getLowFF()], // Original Data Length
|
||||
['v', strlen($name)], // Length of filename
|
||||
['v', strlen($footer)], // Extra data len (see above)
|
||||
['v', strlen($comment)], // Length of comment
|
||||
['v', 0], // Disk number
|
||||
['v', 0], // Internal File Attributes
|
||||
['V', 32], // External File Attributes
|
||||
['V', $this->ofs->getLowFF()] // Relative offset of local header
|
||||
];
|
||||
|
||||
// pack fields, then append name and comment
|
||||
$header = ZipStream::packFields($fields);
|
||||
|
||||
return $header . $name . $footer . $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Bigint
|
||||
*/
|
||||
public function getTotalLength(): Bigint
|
||||
{
|
||||
return $this->totalLength;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Option;
|
||||
@ -7,11 +8,13 @@ use Psr\Http\Message\StreamInterface;
|
||||
|
||||
final class Archive
|
||||
{
|
||||
const DEFAULT_DEFLATE_LEVEL = 6;
|
||||
public const DEFAULT_DEFLATE_LEVEL = 6;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $comment = '';
|
||||
|
||||
/**
|
||||
* Size, in bytes, of the largest file to try
|
||||
* and load into memory (used by
|
||||
@ -22,6 +25,7 @@ final class Archive
|
||||
* @var int
|
||||
*/
|
||||
private $largeFileSize = 20 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* How to handle large files. Legal values are
|
||||
* Method::STORE() (the default), or
|
||||
@ -34,6 +38,7 @@ final class Archive
|
||||
* @var Method
|
||||
*/
|
||||
private $largeFileMethod;
|
||||
|
||||
/**
|
||||
* Boolean indicating whether or not to send
|
||||
* the HTTP headers for this file.
|
||||
@ -41,12 +46,14 @@ final class Archive
|
||||
* @var bool
|
||||
*/
|
||||
private $sendHttpHeaders = false;
|
||||
|
||||
/**
|
||||
* The method called to send headers
|
||||
*
|
||||
* @var Callable
|
||||
*/
|
||||
private $httpHeaderCallback = 'header';
|
||||
|
||||
/**
|
||||
* Enable Zip64 extension, supporting very large
|
||||
* archives (any size > 4 GB or file count > 64k)
|
||||
@ -54,6 +61,7 @@ final class Archive
|
||||
* @var bool
|
||||
*/
|
||||
private $enableZip64 = true;
|
||||
|
||||
/**
|
||||
* Enable streaming files with single read where
|
||||
* general purpose bit 3 indicates local file header
|
||||
@ -64,6 +72,7 @@ final class Archive
|
||||
* @var bool
|
||||
*/
|
||||
private $zeroHeader = false;
|
||||
|
||||
/**
|
||||
* Enable reading file stat for determining file size.
|
||||
* When a 32-bit system reads file size that is
|
||||
@ -77,11 +86,13 @@ final class Archive
|
||||
* @var bool
|
||||
*/
|
||||
private $statFiles = true;
|
||||
|
||||
/**
|
||||
* Enable flush after every write to output stream.
|
||||
* @var bool
|
||||
*/
|
||||
private $flushOutput = false;
|
||||
|
||||
/**
|
||||
* HTTP Content-Disposition. Defaults to
|
||||
* 'attachment', where
|
||||
@ -93,6 +104,7 @@ final class Archive
|
||||
* @var string
|
||||
*/
|
||||
private $contentDisposition = 'attachment';
|
||||
|
||||
/**
|
||||
* Note that this does nothing if you are
|
||||
* not sending HTTP headers.
|
||||
@ -100,6 +112,7 @@ final class Archive
|
||||
* @var string
|
||||
*/
|
||||
private $contentType = 'application/x-zip';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
@ -159,12 +172,12 @@ final class Archive
|
||||
$this->sendHttpHeaders = $sendHttpHeaders;
|
||||
}
|
||||
|
||||
public function getHttpHeaderCallback(): Callable
|
||||
public function getHttpHeaderCallback(): callable
|
||||
{
|
||||
return $this->httpHeaderCallback;
|
||||
}
|
||||
|
||||
public function setHttpHeaderCallback(Callable $httpHeaderCallback): void
|
||||
public function setHttpHeaderCallback(callable $httpHeaderCallback): void
|
||||
{
|
||||
$this->httpHeaderCallback = $httpHeaderCallback;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Option;
|
||||
@ -12,18 +13,22 @@ final class File
|
||||
* @var string
|
||||
*/
|
||||
private $comment = '';
|
||||
|
||||
/**
|
||||
* @var Method
|
||||
*/
|
||||
private $method;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $deflateLevel;
|
||||
|
||||
/**
|
||||
* @var DateTimeInterface
|
||||
*/
|
||||
private $time;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Option;
|
||||
@ -11,9 +12,12 @@ use MyCLabs\Enum\Enum;
|
||||
* @method static STORE(): Method
|
||||
* @method static DEFLATE(): Method
|
||||
* @psalm-immutable
|
||||
* @psalm-template int
|
||||
* @extends Enum<int>
|
||||
*/
|
||||
class Method extends Enum
|
||||
{
|
||||
const STORE = 0x00;
|
||||
const DEFLATE = 0x08;
|
||||
public const STORE = 0x00;
|
||||
|
||||
public const DEFLATE = 0x08;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream\Option;
|
||||
@ -13,10 +14,14 @@ use MyCLabs\Enum\Enum;
|
||||
* @method static DEFLATE(): Version
|
||||
* @method static ZIP64(): Version
|
||||
* @psalm-immutable
|
||||
* @psalm-template int
|
||||
* @extends Enum<int>
|
||||
*/
|
||||
class Version extends Enum
|
||||
{
|
||||
const STORE = 0x000A; // 1.00
|
||||
const DEFLATE = 0x0014; // 2.00
|
||||
const ZIP64 = 0x002D; // 4.50
|
||||
public const STORE = 0x000A; // 1.00
|
||||
|
||||
public const DEFLATE = 0x0014; // 2.00
|
||||
|
||||
public const ZIP64 = 0x002D; // 4.50
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream;
|
||||
|
||||
use function mb_strlen;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use RuntimeException;
|
||||
|
||||
@ -22,6 +25,29 @@ class Stream implements StreamInterface
|
||||
$this->stream = $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all data from the stream into a string, from the beginning to end.
|
||||
*
|
||||
* This method MUST attempt to seek to the beginning of the stream before
|
||||
* reading data and read the stream until the end is reached.
|
||||
*
|
||||
* Warning: This could attempt to load a large amount of data into memory.
|
||||
*
|
||||
* This method MUST NOT raise an exception in order to conform with PHP's
|
||||
* string casting operations.
|
||||
*
|
||||
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
$this->seek(0);
|
||||
} catch (RuntimeException $e) {
|
||||
}
|
||||
return (string) stream_get_contents($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the stream and any underlying resources.
|
||||
*
|
||||
@ -49,28 +75,6 @@ class Stream implements StreamInterface
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all data from the stream into a string, from the beginning to end.
|
||||
*
|
||||
* This method MUST attempt to seek to the beginning of the stream before
|
||||
* reading data and read the stream until the end is reached.
|
||||
*
|
||||
* Warning: This could attempt to load a large amount of data into memory.
|
||||
*
|
||||
* This method MUST NOT raise an exception in order to conform with PHP's
|
||||
* string casting operations.
|
||||
*
|
||||
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
$this->seek(0);
|
||||
} catch (RuntimeException $e) {}
|
||||
return (string) stream_get_contents($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a position in the stream.
|
||||
*
|
||||
@ -86,10 +90,10 @@ class Stream implements StreamInterface
|
||||
public function seek($offset, $whence = SEEK_SET): void
|
||||
{
|
||||
if (!$this->isSeekable()) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (fseek($this->stream, $offset, $whence) !== 0) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +146,7 @@ class Stream implements StreamInterface
|
||||
{
|
||||
$position = ftell($this->stream);
|
||||
if ($position === false) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return $position;
|
||||
}
|
||||
@ -182,12 +186,12 @@ class Stream implements StreamInterface
|
||||
public function write($string): int
|
||||
{
|
||||
if (!$this->isWritable()) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (fwrite($this->stream, $string) === false) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return \mb_strlen($string);
|
||||
return mb_strlen($string);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,16 +216,16 @@ class Stream implements StreamInterface
|
||||
* call returns fewer bytes.
|
||||
* @return string Returns the data read from the stream, or an empty string
|
||||
* if no bytes are available.
|
||||
* @throws \RuntimeException if an error occurs.
|
||||
* @throws RuntimeException if an error occurs.
|
||||
*/
|
||||
public function read($length): string
|
||||
{
|
||||
if (!$this->isReadable()) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
$result = fread($this->stream, $length);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@ -244,17 +248,17 @@ class Stream implements StreamInterface
|
||||
* Returns the remaining contents in a string
|
||||
*
|
||||
* @return string
|
||||
* @throws \RuntimeException if unable to read or an error occurs while
|
||||
* @throws RuntimeException if unable to read or an error occurs while
|
||||
* reading.
|
||||
*/
|
||||
public function getContents(): string
|
||||
{
|
||||
if (!$this->isReadable()) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
$result = stream_get_contents($this->stream);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException;
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZipStream;
|
||||
@ -80,19 +81,24 @@ class ZipStream
|
||||
* to prevent file permissions issues upon extract (see #84)
|
||||
* 0x603 is 00000110 00000011 in binary, so 6 and 3
|
||||
*/
|
||||
const ZIP_VERSION_MADE_BY = 0x603;
|
||||
public const ZIP_VERSION_MADE_BY = 0x603;
|
||||
|
||||
/**
|
||||
* The following signatures end with 0x4b50, which in ASCII is PK,
|
||||
* The following signatures end with 0x4b50, which in ASCII is PK,
|
||||
* the initials of the inventor Phil Katz.
|
||||
* See https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
|
||||
*/
|
||||
const FILE_HEADER_SIGNATURE = 0x04034b50;
|
||||
const CDR_FILE_SIGNATURE = 0x02014b50;
|
||||
const CDR_EOF_SIGNATURE = 0x06054b50;
|
||||
const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
|
||||
const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50;
|
||||
const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50;
|
||||
public const FILE_HEADER_SIGNATURE = 0x04034b50;
|
||||
|
||||
public const CDR_FILE_SIGNATURE = 0x02014b50;
|
||||
|
||||
public const CDR_EOF_SIGNATURE = 0x06054b50;
|
||||
|
||||
public const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
|
||||
|
||||
public const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50;
|
||||
|
||||
public const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50;
|
||||
|
||||
/**
|
||||
* Global Options
|
||||
@ -293,7 +299,7 @@ class ZipStream
|
||||
$options->defaultTo($this->opt);
|
||||
|
||||
$file = new File($this, $name, $options);
|
||||
$file->processStream(new DeflateStream($stream));
|
||||
$file->processStream(new Stream($stream));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -375,34 +381,6 @@ class ZipStream
|
||||
$this->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addCdr64Eof(): void
|
||||
{
|
||||
$num_files = count($this->files);
|
||||
$cdr_length = $this->cdr_ofs;
|
||||
$cdr_offset = $this->ofs;
|
||||
|
||||
$fields = [
|
||||
['V', static::ZIP64_CDR_EOF_SIGNATURE], // ZIP64 end of central file header signature
|
||||
['P', 44], // Length of data below this header (length of block - 12) = 44
|
||||
['v', static::ZIP_VERSION_MADE_BY], // Made by version
|
||||
['v', Version::ZIP64], // Extract by version
|
||||
['V', 0x00], // disk number
|
||||
['V', 0x00], // no of disks
|
||||
['P', $num_files], // no of entries on disk
|
||||
['P', $num_files], // no of entries in cdr
|
||||
['P', $cdr_length], // CDR size
|
||||
['P', $cdr_offset], // CDR offset
|
||||
];
|
||||
|
||||
$ret = static::packFields($fields);
|
||||
$this->send($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a format string and argument list for pack(), then call
|
||||
* pack() and return the result.
|
||||
@ -476,6 +454,62 @@ class ZipStream
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this file larger than large_file_size?
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function isLargeFile(string $path): bool
|
||||
{
|
||||
if (!$this->opt->isStatFiles()) {
|
||||
return false;
|
||||
}
|
||||
$stat = stat($path);
|
||||
return $stat['size'] > $this->opt->getLargeFileSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save file attributes for trailing CDR record.
|
||||
*
|
||||
* @param File $file
|
||||
* @return void
|
||||
*/
|
||||
public function addToCdr(File $file): void
|
||||
{
|
||||
$file->ofs = $this->ofs;
|
||||
$this->ofs = $this->ofs->add($file->getTotalLength());
|
||||
$this->files[] = $file->getCdrFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addCdr64Eof(): void
|
||||
{
|
||||
$num_files = count($this->files);
|
||||
$cdr_length = $this->cdr_ofs;
|
||||
$cdr_offset = $this->ofs;
|
||||
|
||||
$fields = [
|
||||
['V', static::ZIP64_CDR_EOF_SIGNATURE], // ZIP64 end of central file header signature
|
||||
['P', 44], // Length of data below this header (length of block - 12) = 44
|
||||
['v', static::ZIP_VERSION_MADE_BY], // Made by version
|
||||
['v', Version::ZIP64], // Extract by version
|
||||
['V', 0x00], // disk number
|
||||
['V', 0x00], // no of disks
|
||||
['P', $num_files], // no of entries on disk
|
||||
['P', $num_files], // no of entries in cdr
|
||||
['P', $cdr_length], // CDR size
|
||||
['P', $cdr_offset], // CDR offset
|
||||
];
|
||||
|
||||
$ret = static::packFields($fields);
|
||||
$this->send($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send HTTP headers for this stream.
|
||||
*
|
||||
@ -495,13 +529,13 @@ class ZipStream
|
||||
$disposition .= "; filename*=UTF-8''{$urlencoded}";
|
||||
}
|
||||
|
||||
$headers = array(
|
||||
$headers = [
|
||||
'Content-Type' => $this->opt->getContentType(),
|
||||
'Content-Disposition' => $disposition,
|
||||
'Pragma' => 'public',
|
||||
'Cache-Control' => 'public, must-revalidate',
|
||||
'Content-Transfer-Encoding' => 'binary'
|
||||
);
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
];
|
||||
|
||||
$call = $this->opt->getHttpHeaderCallback();
|
||||
foreach ($headers as $key => $val) {
|
||||
@ -571,32 +605,4 @@ class ZipStream
|
||||
$this->cdr_ofs = new Bigint();
|
||||
$this->opt = new ArchiveOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this file larger than large_file_size?
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function isLargeFile(string $path): bool
|
||||
{
|
||||
if (!$this->opt->isStatFiles()) {
|
||||
return false;
|
||||
}
|
||||
$stat = stat($path);
|
||||
return $stat['size'] > $this->opt->getLargeFileSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save file attributes for trailing CDR record.
|
||||
*
|
||||
* @param File $file
|
||||
* @return void
|
||||
*/
|
||||
public function addToCdr(File $file): void
|
||||
{
|
||||
$file->ofs = $this->ofs;
|
||||
$this->ofs = $this->ofs->add($file->getTotalLength());
|
||||
$this->files[] = $file->getCdrFile();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user