diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml
index c19400efef3..36d18307669 100644
--- a/lib/thirdpartylibs.xml
+++ b/lib/thirdpartylibs.xml
@@ -583,7 +583,7 @@ All rights reserved.
zipstream
ZipStream-PHP
PHP ZIP Streaming Library
- 3.1.0
+ 3.1.2
MIT
https://github.com/maennchen/ZipStream-PHP
diff --git a/lib/zipstream/composer.json b/lib/zipstream/composer.json
index 98c536a43d9..6ecd503a66c 100644
--- a/lib/zipstream/composer.json
+++ b/lib/zipstream/composer.json
@@ -22,18 +22,19 @@
}
],
"require": {
- "php-64bit": "^8.1",
+ "php-64bit": "^8.2",
"ext-mbstring": "*",
"ext-zlib": "*"
},
"require-dev": {
- "phpunit/phpunit": "^10.0",
+ "phpunit/phpunit": "^11.0",
"guzzlehttp/guzzle": "^7.5",
"ext-zip": "*",
"mikey179/vfsstream": "^1.6",
"php-coveralls/php-coveralls": "^2.5",
"friendsofphp/php-cs-fixer": "^3.16",
- "vimeo/psalm": "^5.0"
+ "vimeo/psalm": "^6.0",
+ "brianium/paratest": "^7.7"
},
"suggest": {
"psr/http-message": "^2.0",
@@ -46,13 +47,17 @@
"@test:formatted",
"@test:lint"
],
- "test:unit": "phpunit --coverage-clover=coverage.clover.xml --coverage-html cov",
+ "test:unit:setup-cov": "@putenv XDEBUG_MODE=coverage",
+ "test:unit": "paratest --functional",
+ "test:unit:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov"],
"test:unit:slow": "@test:unit --group slow",
+ "test:unit:slow:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov --group slow"],
"test:unit:fast": "@test:unit --exclude-group slow",
+ "test:unit:fast:cov": ["@test:unit:setup-cov", "@test:unit --coverage-clover=coverage.clover.xml --coverage-html cov --exclude-group slow"],
"test:formatted": "@format --dry-run --stop-on-violation --using-cache=no",
"test:lint": "psalm --stats --show-info=true --find-unused-psalm-suppress",
"coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json --insecure",
- "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656",
+ "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656 --trust-gpg-keys 0x8AC0BAA79732DD42",
"docs:generate": "tools/phpdocumentor --sourcecode"
},
"autoload": {
diff --git a/lib/zipstream/readme_moodle.txt b/lib/zipstream/readme_moodle.txt
index ed1c0364c74..844609adf40 100644
--- a/lib/zipstream/readme_moodle.txt
+++ b/lib/zipstream/readme_moodle.txt
@@ -2,7 +2,7 @@ Instructions to import ZipStream into Moodle:
1/ Download from https://github.com/maennchen/ZipStream-PHP/releases/
-2/ Copy the LICENSE file and the src folder into the lib/zipstream folder
+2/ Copy the LICENSE & composer.json files, and the src/ folder into the lib/zipstream folder
3/ Ensure any dependencies required are also imported, e.g.:
- php-64bit
diff --git a/lib/zipstream/src/Exception.php b/lib/zipstream/src/Exception.php
index 27f4f30106f..2e81e307bb3 100644
--- a/lib/zipstream/src/Exception.php
+++ b/lib/zipstream/src/Exception.php
@@ -4,6 +4,4 @@ declare(strict_types=1);
namespace ZipStream;
-abstract class Exception extends \Exception
-{
-}
+abstract class Exception extends \Exception {}
diff --git a/lib/zipstream/src/File.php b/lib/zipstream/src/File.php
index 3f35de4664d..1498e4d6be2 100644
--- a/lib/zipstream/src/File.php
+++ b/lib/zipstream/src/File.php
@@ -95,7 +95,7 @@ class File
if ($this->enableZeroHeader) {
// No calculation required
- } elseif ($this->isSimulation() && $forecastSize) {
+ } elseif ($this->isSimulation() && $forecastSize !== null) {
$this->uncompressedSize = $forecastSize;
$this->compressedSize = $forecastSize;
} else {
@@ -107,12 +107,14 @@ class File
$this->addFileHeader();
- $detectedSize = $forecastSize ?? $this->compressedSize;
+ $detectedSize = $forecastSize ?? ($this->compressedSize > 0 ? $this->compressedSize : null);
if (
$this->isSimulation() &&
- $detectedSize > 0
+ $detectedSize !== null
) {
+ $this->uncompressedSize = $detectedSize;
+ $this->compressedSize = $detectedSize;
($this->recordSentBytes)($detectedSize);
} else {
$this->readStream(send: true);
@@ -158,7 +160,7 @@ class File
if ($this->compressionMethod !== CompressionMethod::STORE) {
return null;
}
- if ($this->exactSize) {
+ if ($this->exactSize !== null) {
return $this->exactSize;
}
$fstat = fstat($this->unpackStream());
@@ -184,7 +186,7 @@ class File
$zip64Enabled = $footer !== '';
- if($zip64Enabled) {
+ if ($zip64Enabled) {
$this->version = Version::ZIP64;
}
@@ -331,6 +333,10 @@ class File
$data = fread($this->unpackStream(), $readLength);
+ if ($data === false) {
+ throw new ResourceActionException('fread', $this->unpackStream());
+ }
+
hash_update($hash, $data);
$this->uncompressedSize += strlen($data);
@@ -341,6 +347,10 @@ class File
$data,
feof($this->unpackStream()) ? ZLIB_FINISH : ZLIB_NO_FLUSH
);
+
+ if ($data === false) {
+ throw new RuntimeException('deflate_add failed');
+ }
}
$this->compressedSize += strlen($data);
@@ -350,7 +360,7 @@ class File
}
}
- if ($this->exactSize && $this->uncompressedSize !== $this->exactSize) {
+ if ($this->exactSize !== null && $this->uncompressedSize !== $this->exactSize) {
throw new FileSizeIncorrectException(expectedSize: $this->exactSize, actualSize: $this->uncompressedSize);
}
@@ -359,7 +369,7 @@ class File
private function compressionInit(): ?DeflateContext
{
- switch($this->compressionMethod) {
+ switch ($this->compressionMethod) {
case CompressionMethod::STORE:
// Noting to do
return null;
@@ -390,7 +400,7 @@ class File
return CentralDirectoryFileHeader::generate(
versionMadeBy: ZipStream::ZIP_VERSION_MADE_BY,
- versionNeededToExtract:$this->version->value,
+ versionNeededToExtract: $this->version->value,
generalPurposeBitFlag: $this->generalPurposeBitFlag,
compressionMethod: $this->compressionMethod,
lastModificationDateTime: $this->lastModificationDateTime,
diff --git a/lib/zipstream/src/PackField.php b/lib/zipstream/src/PackField.php
index 3370dd8d0ee..892b4009a62 100644
--- a/lib/zipstream/src/PackField.php
+++ b/lib/zipstream/src/PackField.php
@@ -19,8 +19,7 @@ class PackField
public function __construct(
public readonly string $format,
public readonly int|string $value
- ) {
- }
+ ) {}
/**
* Create a format string and argument list for pack(), then call
@@ -33,7 +32,7 @@ class PackField
}, '');
$args = array_map(function (self $field) {
- switch($field->format) {
+ switch ($field->format) {
case 'V':
if ($field->value > self::MAX_V) {
throw new RuntimeException(print_r($field->value, true) . ' is larger than 32 bits');
diff --git a/lib/zipstream/src/Time.php b/lib/zipstream/src/Time.php
index 4bfba3cc53d..1b4121ca9fc 100644
--- a/lib/zipstream/src/Time.php
+++ b/lib/zipstream/src/Time.php
@@ -26,20 +26,14 @@ abstract class Time
$dateTime = DateTimeImmutable::createFromInterface($dateTime)->sub(new DateInterval('P1980Y'));
- ['year' => $year,
- 'mon' => $month,
- 'mday' => $day,
- 'hours' => $hour,
- 'minutes' => $minute,
- 'seconds' => $second
- ] = getdate($dateTime->getTimestamp());
+ [$year, $month, $day, $hour, $minute, $second] = explode(' ', $dateTime->format('Y n j G i s'));
return
- ($year << 25) |
- ($month << 21) |
- ($day << 16) |
- ($hour << 11) |
- ($minute << 5) |
- ($second >> 1);
+ ((int) $year << 25) |
+ ((int) $month << 21) |
+ ((int) $day << 16) |
+ ((int) $hour << 11) |
+ ((int) $minute << 5) |
+ ((int) $second >> 1);
}
}
diff --git a/lib/zipstream/src/Zip64/ExtendedInformationExtraField.php b/lib/zipstream/src/Zip64/ExtendedInformationExtraField.php
index b647ce6cd54..aaac51c83c6 100644
--- a/lib/zipstream/src/Zip64/ExtendedInformationExtraField.php
+++ b/lib/zipstream/src/Zip64/ExtendedInformationExtraField.php
@@ -23,8 +23,7 @@ abstract class ExtendedInformationExtraField
new PackField(format: 'v', value: self::TAG),
new PackField(
format: 'v',
- value:
- ($originalSize === null ? 0 : 8) +
+ value: ($originalSize === null ? 0 : 8) +
($compressedSize === null ? 0 : 8) +
($relativeHeaderOffset === null ? 0 : 8) +
($diskStartNumber === null ? 0 : 4)
diff --git a/lib/zipstream/src/ZipStream.php b/lib/zipstream/src/ZipStream.php
index cd308a4932b..698ffbb32be 100644
--- a/lib/zipstream/src/ZipStream.php
+++ b/lib/zipstream/src/ZipStream.php
@@ -59,7 +59,7 @@ use ZipStream\Exception\ResourceActionException;
*
* // read and add each file to the archive
* foreach ($files as $path)
- * $zip->addFileFormPath(fileName: $path, $path);
+ * $zip->addFileFromPath(fileName: $path, $path);
*
* // write archive footer to stream
* $zip->finish();
@@ -263,7 +263,7 @@ class ZipStream
): void {
$this->addFileFromCallback(
fileName: $fileName,
- callback: fn () => $data,
+ callback: fn() => $data,
comment: $comment,
compressionMethod: $compressionMethod,
deflateLevel: $deflateLevel,
@@ -293,7 +293,7 @@ class ZipStream
* // add a file named 'bigfile.rar' from the local file
* // '/usr/share/bigfile.rar' with a comment and a last-modified
* // time of two hours ago
- * $zip->addFile(
+ * $zip->addFileFromPath(
* fileName: 'bigfile.rar',
* path: '/usr/share/bigfile.rar',
* comment: 'this is a comment about bigfile.rar',
@@ -330,7 +330,8 @@ class ZipStream
throw new FileNotReadableException($path);
}
- if ($fileTime = filemtime($path)) {
+ $fileTime = filemtime($path);
+ if ($fileTime !== false) {
$lastModificationDateTime ??= (new DateTimeImmutable())->setTimestamp($fileTime);
}
@@ -394,7 +395,7 @@ class ZipStream
): void {
$this->addFileFromCallback(
fileName: $fileName,
- callback: fn () => $stream,
+ callback: fn() => $stream,
comment: $comment,
compressionMethod: $compressionMethod,
deflateLevel: $deflateLevel,
@@ -473,7 +474,7 @@ class ZipStream
): void {
$this->addFileFromCallback(
fileName: $fileName,
- callback: fn () => $stream,
+ callback: fn() => $stream,
comment: $comment,
compressionMethod: $compressionMethod,
deflateLevel: $deflateLevel,
@@ -494,7 +495,7 @@ class ZipStream
*
* ```php
* foreach($files as $name => $size) {
- * $archive->addFileFromPsr7Stream(
+ * $archive->addFileFromCallback(
* fileName: 'streamfile.txt',
* exactSize: $size,
* callback: function() use($name): Psr\Http\Message\StreamInterface {
@@ -563,11 +564,11 @@ class ZipStream
dataCallback: function () use ($callback, $maxSize) {
$data = $callback();
- if(is_resource($data)) {
+ if (is_resource($data)) {
return $data;
}
- if($data instanceof StreamInterface) {
+ if ($data instanceof StreamInterface) {
return StreamWrapper::getResource($data);
}
@@ -611,7 +612,7 @@ class ZipStream
enableZeroHeader: $enableZeroHeader ?? $this->defaultEnableZeroHeader,
);
- if($this->operationMode !== OperationMode::NORMAL) {
+ if ($this->operationMode !== OperationMode::NORMAL) {
$this->recordedSimulation[] = $file;
}
@@ -629,7 +630,7 @@ class ZipStream
*
* ```php
* // add a directory named 'world/'
- * $zip->addFile(fileName: 'world/');
+ * $zip->addDirectory(fileName: 'world/');
* ```
*/
public function addDirectory(
@@ -676,11 +677,11 @@ class ZipStream
*/
public function executeSimulation(): void
{
- if($this->operationMode !== OperationMode::NORMAL) {
+ if ($this->operationMode !== OperationMode::NORMAL) {
throw new RuntimeException('Zip simulation is not finished.');
}
- foreach($this->recordedSimulation as $file) {
+ foreach ($this->recordedSimulation as $file) {
$this->centralDirectoryRecords[] = $file->cloneSimulationExecution()->process();
}
@@ -769,7 +770,13 @@ class ZipStream
if (is_resource($outputStream)) {
return $outputStream;
}
- return fopen('php://output', 'wb');
+ $resource = fopen('php://output', 'wb');
+
+ if ($resource === false) {
+ throw new RuntimeException('fopen of php://output failed');
+ }
+
+ return $resource;
}
/**
@@ -815,15 +822,15 @@ class ZipStream
}
}
- /**
- * Send HTTP headers for this stream.
- */
+ /**
+ * Send HTTP headers for this stream.
+ */
private function sendHttpHeaders(): void
{
// grab content disposition
$disposition = $this->contentDisposition;
- if ($this->outputName) {
+ if ($this->outputName !== null) {
// Various different browsers dislike various characters here. Strip them all for safety.
$safeOutput = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->outputName));
@@ -854,7 +861,7 @@ class ZipStream
$this->centralDirectoryRecords = [];
$this->offset = 0;
- if($this->operationMode === OperationMode::NORMAL) {
+ if ($this->operationMode === OperationMode::NORMAL) {
$this->ready = false;
$this->recordedSimulation = [];
} else {