summaryrefslogtreecommitdiffstats
path: root/vendor/maennchen/zipstream-php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/maennchen/zipstream-php')
-rw-r--r--vendor/maennchen/zipstream-php/.phive/phars.xml4
-rw-r--r--vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php71
-rw-r--r--vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig15
-rw-r--r--vendor/maennchen/zipstream-php/.tool-versions1
-rw-r--r--vendor/maennchen/zipstream-php/LICENSE24
-rw-r--r--vendor/maennchen/zipstream-php/README.md114
-rw-r--r--vendor/maennchen/zipstream-php/composer.json76
-rw-r--r--vendor/maennchen/zipstream-php/guides/ContentLength.rst79
-rw-r--r--vendor/maennchen/zipstream-php/guides/FlySystem.rst33
-rw-r--r--vendor/maennchen/zipstream-php/guides/Nginx.rst16
-rw-r--r--vendor/maennchen/zipstream-php/guides/Options.rst61
-rw-r--r--vendor/maennchen/zipstream-php/guides/PSR7Streams.rst18
-rw-r--r--vendor/maennchen/zipstream-php/guides/StreamOutput.rst33
-rw-r--r--vendor/maennchen/zipstream-php/guides/Symfony.rst126
-rw-r--r--vendor/maennchen/zipstream-php/guides/Varnish.rst22
-rw-r--r--vendor/maennchen/zipstream-php/guides/index.rst85
-rw-r--r--vendor/maennchen/zipstream-php/phpdoc.dist.xml39
-rw-r--r--vendor/maennchen/zipstream-php/phpunit.xml.dist14
-rw-r--r--vendor/maennchen/zipstream-php/psalm.xml53
-rw-r--r--vendor/maennchen/zipstream-php/src/Bigint.php174
-rw-r--r--vendor/maennchen/zipstream-php/src/DeflateStream.php71
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception.php12
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/EncodingException.php14
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php23
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php23
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php14
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/OverflowException.php18
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php23
-rw-r--r--vendor/maennchen/zipstream-php/src/File.php483
-rw-r--r--vendor/maennchen/zipstream-php/src/Option/Archive.php276
-rw-r--r--vendor/maennchen/zipstream-php/src/Option/File.php122
-rw-r--r--vendor/maennchen/zipstream-php/src/Option/Method.php21
-rw-r--r--vendor/maennchen/zipstream-php/src/Option/Version.php25
-rw-r--r--vendor/maennchen/zipstream-php/src/Stream.php265
-rw-r--r--vendor/maennchen/zipstream-php/src/ZipStream.php608
-rw-r--r--vendor/maennchen/zipstream-php/test/BigintTest.php66
-rw-r--r--vendor/maennchen/zipstream-php/test/ZipStreamTest.php650
-rw-r--r--vendor/maennchen/zipstream-php/test/bootstrap.php7
-rw-r--r--vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php40
39 files changed, 3819 insertions, 0 deletions
diff --git a/vendor/maennchen/zipstream-php/.phive/phars.xml b/vendor/maennchen/zipstream-php/.phive/phars.xml
new file mode 100644
index 0000000..569106a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.phive/phars.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phive xmlns="https://phar.io/phive">
+ <phar name="phpdocumentor" version="^3.3.1" installed="3.3.1" location="./tools/phpdocumentor" copy="false"/>
+</phive>
diff --git a/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php b/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..3ba86a4
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * PHP-CS-Fixer config for ZipStream-PHP
+ * @author Nicolas CARPi <nico-git@deltablot.email>
+ * @copyright 2022 Nicolas CARPi
+ * @see https://github.com/maennchen/ZipStream-PHP
+ * @license MIT
+ * @package maennchen/ZipStream-PHP
+ */
+
+use PhpCsFixer\Config;
+use PhpCsFixer\Finder;
+
+$finder = Finder::create()
+ ->exclude('.github')
+ ->exclude('.phpdoc')
+ ->exclude('docs')
+ ->exclude('tools')
+ ->exclude('vendor')
+ ->in(__DIR__);
+
+$config = new Config();
+return $config->setRules([
+ '@PER' => true,
+ '@PER:risky' => true,
+ '@PHP81Migration' => true,
+ '@PHPUnit84Migration:risky' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'class_attributes_separation' => true,
+ 'declare_strict_types' => true,
+ 'dir_constant' => true,
+ 'is_null' => true,
+ 'no_homoglyph_names' => true,
+ 'no_null_property_initialization' => true,
+ 'no_php4_constructor' => true,
+ 'no_unused_imports' => true,
+ 'no_useless_else' => true,
+ 'non_printable_character' => true,
+ 'ordered_imports' => true,
+ 'ordered_class_elements' => true,
+ 'php_unit_construct' => true,
+ 'pow_to_exponentiation' => true,
+ 'psr_autoloading' => true,
+ 'random_api_migration' => true,
+ 'return_assignment' => true,
+ 'self_accessor' => true,
+ 'semicolon_after_instruction' => true,
+ 'short_scalar_cast' => true,
+ 'simplified_null_return' => true,
+ 'single_blank_line_before_namespace' => true,
+ 'single_class_element_per_statement' => true,
+ 'single_line_comment_style' => true,
+ 'single_quote' => true,
+ 'space_after_semicolon' => true,
+ 'standardize_not_equals' => true,
+ 'strict_param' => true,
+ 'ternary_operator_spaces' => true,
+ 'trailing_comma_in_multiline' => true,
+ 'trim_array_spaces' => true,
+ 'unary_operator_spaces' => true,
+ 'global_namespace_import' => [
+ 'import_classes' => true,
+ 'import_functions' => true,
+ 'import_constants' => true,
+ ],
+ ])
+ ->setFinder($finder)
+ ->setRiskyAllowed(true); \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig b/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig
new file mode 100644
index 0000000..b7507fb
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.phpdoc/template/base.html.twig
@@ -0,0 +1,15 @@
+{% extends 'layout.html.twig' %}
+
+{% set topMenu = {
+ "menu": [
+ { "name": "Guides", "url": "https://maennchen.dev/ZipStream-PHP/guide/index.html"},
+ { "name": "API", "url": "https://maennchen.dev/ZipStream-PHP/classes/ZipStream-ZipStream.html"},
+ { "name": "Issues", "url": "https://github.com/maennchen/ZipStream-PHP/issues"},
+ ],
+ "social": [
+ { "iconClass": "fab fa-github", "url": "https://github.com/maennchen/ZipStream-PHP"},
+ { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/maennchen/ZipStream-PHP/discussions"},
+ { "iconClass": "fas fa-money-bill", "url": "https://opencollective.com/zipstream"},
+ ]
+}
+%} \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/.tool-versions b/vendor/maennchen/zipstream-php/.tool-versions
new file mode 100644
index 0000000..ebe7f12
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.tool-versions
@@ -0,0 +1 @@
+php 8.1.13
diff --git a/vendor/maennchen/zipstream-php/LICENSE b/vendor/maennchen/zipstream-php/LICENSE
new file mode 100644
index 0000000..ebe7fe2
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/LICENSE
@@ -0,0 +1,24 @@
+MIT License
+
+Copyright (C) 2007-2009 Paul Duncan <pabs@pablotron.org>
+Copyright (C) 2014 Jonatan Männchen <jonatan@maennchen.ch>
+Copyright (C) 2014 Jesse G. Donat <donatj@gmail.com>
+Copyright (C) 2018 Nicolas CARPi <nicolas.carpi@curie.fr>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/maennchen/zipstream-php/README.md b/vendor/maennchen/zipstream-php/README.md
new file mode 100644
index 0000000..155a265
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/README.md
@@ -0,0 +1,114 @@
+# ZipStream-PHP
+
+[![Main Branch](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml/badge.svg)](https://github.com/maennchen/ZipStream-PHP/actions/workflows/branch_main.yml)
+[![Coverage Status](https://coveralls.io/repos/github/maennchen/ZipStream-PHP/badge.svg?branch=main)](https://coveralls.io/github/maennchen/ZipStream-PHP?branch=main)
+[![Latest Stable Version](https://poser.pugx.org/maennchen/zipstream-php/v/stable)](https://packagist.org/packages/maennchen/zipstream-php)
+[![Total Downloads](https://poser.pugx.org/maennchen/zipstream-php/downloads)](https://packagist.org/packages/maennchen/zipstream-php)
+[![Financial Contributors on Open Collective](https://opencollective.com/zipstream/all/badge.svg?label=financial+contributors)](https://opencollective.com/zipstream) [![License](https://img.shields.io/github/license/maennchen/zipstream-php.svg)](LICENSE)
+
+## Unstable Branch
+
+The `main` branch is not stable. Please see the
+[releases](https://github.com/maennchen/ZipStream-PHP/releases) for a stable
+version.
+
+## Overview
+
+A fast and simple streaming zip file downloader for PHP. Using this library will save you from having to write the Zip to disk. You can directly send it to the user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
+
+Please see the [LICENSE](LICENSE) file for licensing and warranty information.
+
+## Installation
+
+Simply add a dependency on maennchen/zipstream-php to your project's composer.json file if you use Composer to manage the dependencies of your project. Use following command to add the package to your project's dependencies:
+
+```bash
+composer require maennchen/zipstream-php
+```
+
+## Usage
+
+For detailed instructions, please check the
+[Documentation](https://maennchen.dev/ZipStream-PHP/).
+
+Here's a simple example:
+
+```php
+// Autoload the dependencies
+require 'vendor/autoload.php';
+
+// enable output of HTTP headers
+$options = new ZipStream\Option\Archive();
+$options->setSendHttpHeaders(true);
+
+// create a new zipstream object
+$zip = new ZipStream\ZipStream('example.zip', $options);
+
+// create a file named 'hello.txt'
+$zip->addFile('hello.txt', 'This is the contents of hello.txt');
+
+// add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
+$zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
+
+// finish the zip stream
+$zip->finish();
+```
+
+## Upgrade to version 2.0.0
+
+- Only the self opened streams will be closed (#139)
+ If you were relying on ZipStream to close streams that the library didn't open,
+ you'll need to close them yourself now.
+
+## Upgrade to version 1.0.0
+
+- All options parameters to all function have been moved from an `array` to structured option objects. See [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Available-options) for examples.
+- The whole library has been refactored. The minimal PHP requirement has been raised to PHP 7.1.
+
+## Usage with Symfony and S3
+
+You can find example code on [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Symfony-example).
+
+## Contributing
+
+ZipStream-PHP is a collaborative project. Please take a look at the
+[.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) file.
+
+## About the Authors
+
+- Paul Duncan <pabs@pablotron.org> - https://pablotron.org/
+- Jonatan Männchen <jonatan@maennchen.ch> - https://maennchen.dev
+- Jesse G. Donat <donatj@gmail.com> - https://donatstudios.com
+- Nicolas CARPi <nico-git@deltablot.email> - https://www.deltablot.com
+- Nik Barham <nik@brokencube.co.uk> - https://www.brokencube.co.uk
+
+## Contributors
+
+### Code Contributors
+
+This project exists thanks to all the people who contribute.
+[[Contribute](.github/CONTRIBUTING.md)].
+<a href="https://github.com/maennchen/ZipStream-PHP/graphs/contributors"><img src="https://opencollective.com/zipstream/contributors.svg?width=890&button=false" /></a>
+
+### Financial Contributors
+
+Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/zipstream/contribute)]
+
+#### Individuals
+
+<a href="https://opencollective.com/zipstream"><img src="https://opencollective.com/zipstream/individuals.svg?width=890"></a>
+
+#### Organizations
+
+Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/zipstream/contribute)]
+
+<a href="https://opencollective.com/zipstream/organization/0/website"><img src="https://opencollective.com/zipstream/organization/0/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/1/website"><img src="https://opencollective.com/zipstream/organization/1/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/2/website"><img src="https://opencollective.com/zipstream/organization/2/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/3/website"><img src="https://opencollective.com/zipstream/organization/3/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/4/website"><img src="https://opencollective.com/zipstream/organization/4/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/5/website"><img src="https://opencollective.com/zipstream/organization/5/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/6/website"><img src="https://opencollective.com/zipstream/organization/6/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/7/website"><img src="https://opencollective.com/zipstream/organization/7/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/8/website"><img src="https://opencollective.com/zipstream/organization/8/avatar.svg"></a>
+<a href="https://opencollective.com/zipstream/organization/9/website"><img src="https://opencollective.com/zipstream/organization/9/avatar.svg"></a>
diff --git a/vendor/maennchen/zipstream-php/composer.json b/vendor/maennchen/zipstream-php/composer.json
new file mode 100644
index 0000000..bb4ffbb
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/composer.json
@@ -0,0 +1,76 @@
+{
+ "name": "maennchen/zipstream-php",
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": ["zip", "stream"],
+ "type": "library",
+ "license": "MIT",
+ "authors": [{
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "psr/http-message": "^1.0",
+ "myclabs/php-enum": "^1.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+ "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+ "ext-zip": "*",
+ "mikey179/vfsstream": "^1.6",
+ "vimeo/psalm": "^4.1",
+ "php-coveralls/php-coveralls": "^2.4",
+ "friendsofphp/php-cs-fixer": "^3.9"
+ },
+ "scripts": {
+ "format": "php-cs-fixer fix",
+ "test": "composer run test:unit && composer run test:formatted && composer run test:lint",
+ "test:unit": "phpunit --coverage-clover=coverage.clover.xml",
+ "test:formatted": "composer run format -- --dry-run --stop-on-violation --using-cache=no",
+ "test:lint": "psalm --stats --show-info --find-unused-psalm-suppress",
+ "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json -v",
+ "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656",
+ "docs:generate": "tools/phpdocumentor --sourcecode"
+ },
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "archive": {
+ "exclude": [
+ "/composer.lock",
+ "/docs",
+ "/.gitattributes",
+ "/.github",
+ "/.gitignore",
+ "/guides",
+ "/.phive",
+ "/.php-cs-fixer.cache",
+ "/.php-cs-fixer.dist.php",
+ "/.phpdoc",
+ "/phpdoc.dist.xml",
+ "/.phpunit.result.cache",
+ "/phpunit.xml.dist",
+ "/psalm.xml",
+ "/test",
+ "/tools",
+ "/.tool-versions",
+ "/vendor"
+ ]
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/guides/ContentLength.rst b/vendor/maennchen/zipstream-php/guides/ContentLength.rst
new file mode 100644
index 0000000..e51e692
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/ContentLength.rst
@@ -0,0 +1,79 @@
+Adding Content-Length header
+=============
+
+Adding a ``Content-Length`` header for ``ZipStream`` is not trivial since the
+size is not known beforehand.
+
+The following workaround adds an approximated header:
+
+.. code-block:: php
+
+ class Zip
+ {
+ /** @var string */
+ private $name;
+
+ private $files = [];
+
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function addFile($name, $data)
+ {
+ $this->files[] = ['type' => 'addFile', 'name' => $name, 'data' => $data];
+ }
+
+ public function addFileFromPath($name, $path)
+ {
+ $this->files[] = ['type' => 'addFileFromPath', 'name' => $name, 'path' => $path];
+ }
+
+ public function getEstimate()
+ {
+ $estimate = 22;
+ foreach ($this->files as $file) {
+ $estimate += 76 + 2 * strlen($file['name']);
+ if ($file['type'] === 'addFile') {
+ $estimate += strlen($file['data']);
+ }
+ if ($file['type'] === 'addFileFromPath') {
+ $estimate += filesize($file['path']);
+ }
+ }
+ return $estimate;
+ }
+
+ public function finish()
+ {
+ header('Content-Length: ' . $this->getEstimate());
+ $options = new \ZipStream\Option\Archive();
+ $options->setSendHttpHeaders(true);
+ $options->setEnableZip64(false);
+ $options->setDeflateLevel(-1);
+ $zip = new \ZipStream\ZipStream($this->name, $options);
+
+ $fileOptions = new \ZipStream\Option\File();
+ $fileOptions->setMethod(\ZipStream\Option\Method::STORE());
+ foreach ($this->files as $file) {
+ if ($file['type'] === 'addFile') {
+ $zip->addFile($file['name'], $file['data'], $fileOptions);
+ }
+ if ($file['type'] === 'addFileFromPath') {
+ $zip->addFileFromPath($file['name'], $file['path'], $fileOptions);
+ }
+ }
+ $zip->finish();
+ exit;
+ }
+ }
+
+It only works with the following constraints:
+
+- All file content is known beforehand.
+- Content Deflation is disabled
+
+Thanks to
+`partiellkorrekt <https://github.com/maennchen/ZipStream-PHP/issues/89#issuecomment-1047949274>`_
+for this workaround. \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/FlySystem.rst b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
new file mode 100644
index 0000000..0243f24
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
@@ -0,0 +1,33 @@
+Usage with FlySystem
+===============
+
+For saving or uploading the generated zip, you can use the
+`Flysystem <https://flysystem.thephpleague.com>`_ package, and its many
+adapters.
+
+For that you will need to provide another stream than the ``php://output``
+default one, and pass it to Flysystem ``putStream`` method.
+
+.. code-block:: php
+
+ // Open Stream only once for read and write since it's a memory stream and
+ // the content is lost when closing the stream / opening another one
+ $tempStream = fopen('php://memory', 'w+');
+
+ // Init Options
+ $zipStreamOptions = new Archive();
+ $zipStreamOptions->setOutputStream($tempStream);
+
+ // Create Zip Archive
+ $zipStream = new ZipStream('test.zip', $zipStreamOptions);
+ $zipStream->addFile('test.txt', 'text');
+ $zipStream->finish();
+
+ // Store File (see Flysystem documentation, and all its framework integration)
+ $adapter = new Local(__DIR__.'/path/to/folder'); // Can be any adapter (AWS, Google, Ftp, etc.)
+ $filesystem = new Filesystem($adapter);
+
+ $filesystem->putStream('test.zip', $tempStream)
+
+ // Close Stream
+ fclose($tempStream); \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Nginx.rst b/vendor/maennchen/zipstream-php/guides/Nginx.rst
new file mode 100644
index 0000000..c53d300
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Nginx.rst
@@ -0,0 +1,16 @@
+Usage with nginx
+=============
+
+If you are using nginx as a webserver, it will try to buffer the response.
+So you'll want to disable this with a custom header:
+
+.. code-block:: php
+ header('X-Accel-Buffering: no');
+ # or with the Response class from Symfony
+ $response->headers->set('X-Accel-Buffering', 'no');
+
+Alternatively, you can tweak the
+`fastcgi cache parameters <https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffers>`_
+within nginx config.
+
+See `original issue <https://github.com/maennchen/ZipStream-PHP/issues/77>`_. \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Options.rst b/vendor/maennchen/zipstream-php/guides/Options.rst
new file mode 100644
index 0000000..eabaa6f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Options.rst
@@ -0,0 +1,61 @@
+Available options
+===============
+
+Here is the full list of options available to you. You can also have a look at
+``src/Option/Archive.php`` file.
+
+First, an instance of ``ZipStream\Option\Archive`` needs to be created, and
+after that you use setters methods to modify the values.
+
+.. code-block:: php
+ use ZipStream\ZipStream;
+ use ZipStream\Option\Archive as ArchiveOptions;
+
+ require_once 'vendor/autoload.php';
+
+ $opt = new ArchiveOptions();
+
+ // Define output stream (argument is of type resource)
+ $opt->setOutputStream($fd);
+
+ // Set the deflate level (default is 6; use -1 to disable it)
+ $opt->setDeflateLevel(6);
+
+ // Add a comment to the zip file
+ $opt->setComment('This is a comment.');
+
+ // Size, in bytes, of the largest file to try and load into memory (used by addFileFromPath()). Large files may also be compressed differently; see the 'largeFileMethod' option.
+ $opt->setLargeFileSize(30000000);
+
+ // How to handle large files. Legal values are STORE (the default), or DEFLATE. Store sends the file raw and is significantly faster, while DEFLATE compresses the file and is much, much slower. Note that deflate must compress the file twice and is extremely slow.
+ $opt->setLargeFileMethod(ZipStream\Option\Method::STORE());
+ $opt->setLargeFileMethod(ZipStream\Option\Method::DEFLATE());
+
+ // Send http headers (default is false)
+ $opt->setSendHttpHeaders(false);
+
+ // HTTP Content-Disposition. Defaults to 'attachment', where FILENAME is the specified filename. Note that this does nothing if you are not sending HTTP headers.
+ $opt->setContentDisposition('attachment');
+
+ // Set the content type (does nothing if you are not sending HTTP headers)
+ $opt->setContentType('application/x-zip');
+
+ // Set the function called for setting headers. Default is the `header()` of PHP
+ $opt->setHttpHeaderCallback('header');
+
+ // Enable streaming files with single read where general purpose bit 3 indicates local file header contain zero values in crc and size fields, these appear only after file contents in data descriptor block. Default is false. Set to true if your input stream is remote (used with addFileFromStream()).
+ $opt->setZeroHeader(false);
+
+ // Enable reading file stat for determining file size. When a 32-bit system reads file size that is over 2 GB, invalid value appears in file size due to integer overflow. Should be disabled on 32-bit systems with method addFileFromPath if any file may exceed 2 GB. In this case file will be read in blocks and correct size will be determined from content. Default is true.
+ $opt->setStatFiles(true);
+
+ // Enable zip64 extension, allowing very large archives (> 4Gb or file count > 64k)
+ // default is true
+ $opt->setEnableZip64(true);
+
+ // Flush output buffer after every write
+ // default is false
+ $opt->setFlushOutput(true);
+
+ // Now that everything is set you can pass the options to the ZipStream instance
+ $zip = new ZipStream('example.zip', $opt);
diff --git a/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
new file mode 100644
index 0000000..4b4ca4b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
@@ -0,0 +1,18 @@
+Usage with PSR 7 Streams
+===============
+
+PSR-7 streams are `standardized streams <https://www.php-fig.org/psr/psr-7/>`_.
+
+ZipStream-PHP supports working with these streams with the function
+``addFileFromPsr7Stream``.
+
+For all parameters of the function see the API documentation.
+
+Example
+---------------
+
+.. code-block:: php
+
+ $stream = $response->getBody();
+ // add a file named 'streamfile.txt' from the content of the stream
+ $zip->addFileFromPsr7Stream('streamfile.txt', $stream);
diff --git a/vendor/maennchen/zipstream-php/guides/StreamOutput.rst b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
new file mode 100644
index 0000000..1a0495f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
@@ -0,0 +1,33 @@
+Stream Output
+===============
+
+Stream to S3 Bucket
+---------------
+
+.. code-block:: php
+ use Aws\S3\S3Client;
+ use Aws\Credentials\CredentialProvider;
+ use ZipStream\Option\Archive;
+ use ZipStream\ZipStream;
+
+ $bucket = 'your bucket name';
+ $client = new S3Client([
+ 'region' => 'your region',
+ 'version' => 'latest',
+ 'bucketName' => $bucket,
+ 'credentials' => CredentialProvider::defaultProvider(),
+ ]);
+ $client->registerStreamWrapper();
+
+ $zipFile = fopen("s3://$bucket/example.zip", 'w');
+
+ $options = new Archive();
+ $options->setEnableZip64(false);
+ $options->setOutputStream($zipFile);
+
+ $zip = new ZipStream(null, $options);
+ $zip->addFile('file1.txt', 'File1 data');
+ $zip->addFile('file2.txt', 'File2 data');
+ $zip->finish();
+
+ fclose($zipFile); \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Symfony.rst b/vendor/maennchen/zipstream-php/guides/Symfony.rst
new file mode 100644
index 0000000..18f9059
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Symfony.rst
@@ -0,0 +1,126 @@
+Usage with Symfony
+===============
+
+Overview for using ZipStream in Symfony
+--------
+
+Using ZipStream in Symfony requires use of Symfony's ``StreamedResponse`` when
+used in controller actions.
+
+Wrap your call to the relevant ``ZipStream`` stream method (i.e. ``addFile``,
+``addFileFromPath``, ``addFileFromStream``) in Symfony's ``StreamedResponse``
+function passing in any required arguments for your use case.
+
+Using Symfony's ``StreamedResponse`` will allow Symfony to stream output from
+ZipStream correctly to users' browsers and avoid a corrupted final zip landing
+on the users' end.
+
+Example for using ``ZipStream`` in a controller action to zip stream files
+stored in an AWS S3 bucket by key:
+
+.. code-block:: php
+
+ use Symfony\Component\HttpFoundation\StreamedResponse;
+ use Aws\S3\S3Client;
+ use ZipStream;
+
+ //...
+
+ /**
+ * @Route("/zipstream", name="zipstream")
+ */
+ public function zipStreamAction()
+ {
+ //sample test file on s3
+ $s3keys = array(
+ "ziptestfolder/file1.txt"
+ );
+
+ $s3Client = $this->get('app.amazon.s3'); //s3client service
+ $s3Client->registerStreamWrapper(); //required
+
+ //using StreamedResponse to wrap ZipStream functionality for files on AWS s3.
+ $response = new StreamedResponse(function() use($s3keys, $s3Client)
+ {
+ // Define suitable options for ZipStream Archive.
+ $options = new \ZipStream\Option\Archive();
+ $options->setContentType('application/octet-stream');
+ // this is needed to prevent issues with truncated zip files
+ $options->setZeroHeader(true);
+ $options->setComment('test zip file.');
+
+ //initialise zipstream with output zip filename and options.
+ $zip = new ZipStream\ZipStream('test.zip', $options);
+
+ //loop keys - useful for multiple files
+ foreach ($s3keys as $key) {
+ // Get the file name in S3 key so we can save it to the zip
+ //file using the same name.
+ $fileName = basename($key);
+
+ //concatenate s3path.
+ $bucket = 'bucketname'; //replace with your bucket name or get from parameters file.
+ $s3path = "s3://" . $bucket . "/" . $key;
+
+ //addFileFromStream
+ if ($streamRead = fopen($s3path, 'r')) {
+ $zip->addFileFromStream($fileName, $streamRead);
+ } else {
+ die('Could not open stream for reading');
+ }
+ }
+
+ $zip->finish();
+
+ });
+
+ return $response;
+ }
+
+In the above example, files on AWS S3 are being streamed from S3 to the Symfon
+application via ``fopen`` call when the s3Client has ``registerStreamWrapper``
+applied. This stream is then passed to ``ZipStream`` via the
+``addFileFromStream`` function, which ZipStream then streams as a zip to the
+client browser via Symfony's ``StreamedResponse``. No Zip is created server
+side, which makes this approach a more efficient solution for streaming zips to
+the client browser especially for larger files.
+
+For the above use case you will need to have installed
+`aws/aws-sdk-php-symfony <https://github.com/aws/aws-sdk-php-symfony>`_ to
+support accessing S3 objects in your Symfony web application. This is not
+required for locally stored files on you server you intend to stream via
+``ZipStream``.
+
+See official Symfony documentation for details on
+`Symfony's StreamedResponse <https://symfony.com/doc/current/components/http_foundation.html#streaming-a-response>`_
+``Symfony\Component\HttpFoundation\StreamedResponse``.
+
+Note from `S3 documentation <https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-stream-wrapper.html>`_:
+
+ Streams opened in "r" mode only allow data to be read from the stream, and
+ are not seekable by default. This is so that data can be downloaded from
+ Amazon S3 in a truly streaming manner, where previously read bytes do not
+ need to be buffered into memory. If you need a stream to be seekable, you
+ can pass seekable into the stream context options of a function.
+
+Make sure to configure your S3 context correctly!
+
+Uploading a file
+--------
+
+You need to add correct permissions
+(see `#120 <https://github.com/maennchen/ZipStream-PHP/issues/120>`_)
+
+**example code**
+
+
+.. code-block:: php
+
+ $path = "s3://{$adapter->getBucket()}/{$this->getArchivePath()}";
+
+ // the important bit
+ $outputContext = stream_context_create([
+ 's3' => ['ACL' => 'public-read'],
+ ]);
+
+ fopen($path, 'w', null, $outputContext); \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/Varnish.rst b/vendor/maennchen/zipstream-php/guides/Varnish.rst
new file mode 100644
index 0000000..952d287
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Varnish.rst
@@ -0,0 +1,22 @@
+Usage with Varnish
+=============
+
+Serving a big zip with varnish in between can cause random stream close.
+This can be solved by adding attached code to the vcl file.
+
+To avoid the problem, add the following to your varnish config file:
+
+.. code-block::
+ sub vcl_recv {
+ # Varnish can’t intercept the discussion anymore
+ # helps for streaming big zips
+ if (req.url ~ "\.(tar|gz|zip|7z|exe)$") {
+ return (pipe);
+ }
+ }
+ # Varnish can’t intercept the discussion anymore
+ # helps for streaming big zips
+ sub vcl_pipe {
+ set bereq.http.connection = "close";
+ return (pipe);
+ }
diff --git a/vendor/maennchen/zipstream-php/guides/index.rst b/vendor/maennchen/zipstream-php/guides/index.rst
new file mode 100644
index 0000000..a68fa94
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/index.rst
@@ -0,0 +1,85 @@
+ZipStream PHP
+=============
+
+A fast and simple streaming zip file downloader for PHP. Using this library will
+save you from having to write the Zip to disk. You can directly send it to the
+user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
+
+.. toctree::
+
+ index
+ Symfony
+ Options
+ StreamOutput
+ FlySystem
+ PSR7Streams
+ Nginx
+ Varnish
+ ContentLength
+
+Installation
+---------------
+
+Simply add a dependency on ``maennchen/zipstream-php`` to your project's
+``composer.json`` file if you use Composer to manage the dependencies of your
+project. Use following command to add the package to your project's dependencies:
+
+.. code-block:: sh
+ composer require maennchen/zipstream-php
+
+Usage Intro
+---------------
+
+Here's a simple example:
+
+.. code-block:: php
+
+ // Autoload the dependencies
+ require 'vendor/autoload.php';
+
+ // enable output of HTTP headers
+ $options = new ZipStream\Option\Archive();
+ $options->setSendHttpHeaders(true);
+
+ // create a new zipstream object
+ $zip = new ZipStream\ZipStream('example.zip', $options);
+
+ // create a file named 'hello.txt'
+ $zip->addFile('hello.txt', 'This is the contents of hello.txt');
+
+ // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
+ $zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
+
+ // add a file named 'goodbye.txt' from an open stream resource
+ $fp = tmpfile();
+ fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
+ rewind($fp);
+ $zip->addFileFromStream('goodbye.txt', $fp);
+ fclose($fp);
+
+ // finish the zip stream
+ $zip->finish();
+
+You can also add comments, modify file timestamps, and customize (or
+disable) the HTTP headers. It is also possible to specify the storage method
+when adding files, the current default storage method is ``DEFLATE``
+i.e files are stored with Compression mode 0x08.
+
+Known Issues
+---------------
+
+The native Mac OS archive extraction tool prior to macOS 10.15 might not open
+archives in some conditions. A workaround is to disable the Zip64 feature with
+the option ``enableZip64: false``. This limits the archive to 4 Gb and 64k files
+but will allow users on macOS 10.14 and below to open them without issue.
+See `#116 <https://github.com/maennchen/ZipStream-PHP/issues/146>`_.
+
+The linux ``unzip`` utility might not handle properly unicode characters.
+It is recommended to extract with another tool like
+`7-zip <https://www.7-zip.org/>`_.
+See `#146 <https://github.com/maennchen/ZipStream-PHP/issues/146>`_.
+
+It is the responsability of the client code to make sure that files are not
+saved with the same path, as it is not possible for the library to figure it out
+while streaming a zip.
+See `#154 <https://github.com/maennchen/ZipStream-PHP/issues/154>`_. \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/phpdoc.dist.xml b/vendor/maennchen/zipstream-php/phpdoc.dist.xml
new file mode 100644
index 0000000..fd7eb57
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/phpdoc.dist.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<phpdocumentor
+ configVersion="3"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="https://www.phpdoc.org"
+ xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/phpDocumentor/phpDocumentor/master/data/xsd/phpdoc.xsd"
+>
+ <title>ZipStream-PHP</title>
+ <paths>
+ <output>docs</output>
+ </paths>
+ <version number="3.0.0">
+ <folder>latest</folder>
+ <api>
+ <source dsn=".">
+ <path>src</path>
+ </source>
+ <output>api</output>
+ <ignore hidden="true" symlinks="true">
+ <path>tests/**/*</path>
+ <path>vendor/**/*</path>
+ </ignore>
+ <extensions>
+ <extension>php</extension>
+ </extensions>
+ <visibility>public</visibility>
+ <default-package-name>ZipStream</default-package-name>
+ <include-source>true</include-source>
+ </api>
+ <guide>
+ <source dsn=".">
+ <path>guides</path>
+ </source>
+ <output>guide</output>
+ </guide>
+ </version>
+ <setting name="guides.enabled" value="true"/>
+ <template name="default" />
+</phpdocumentor> \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/phpunit.xml.dist b/vendor/maennchen/zipstream-php/phpunit.xml.dist
new file mode 100644
index 0000000..8a2f318
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/phpunit.xml.dist
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="test/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
+ <coverage processUncoveredFiles="true">
+ <include>
+ <directory suffix=".php">src</directory>
+ </include>
+ </coverage>
+ <testsuites>
+ <testsuite name="Application">
+ <directory>test</directory>
+ </testsuite>
+ </testsuites>
+ <logging/>
+</phpunit>
diff --git a/vendor/maennchen/zipstream-php/psalm.xml b/vendor/maennchen/zipstream-php/psalm.xml
new file mode 100644
index 0000000..4e4c4f6
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/psalm.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<psalm
+ resolveFromConfigFile="true"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="https://getpsalm.org/schema/config"
+ xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
+>
+ <projectFiles>
+ <directory name="src" />
+ <ignoreFiles>
+ <directory name="vendor" />
+ </ignoreFiles>
+ </projectFiles>
+
+ <issueHandlers>
+ <LessSpecificReturnType errorLevel="info" />
+
+ <!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->
+
+ <DeprecatedMethod errorLevel="info" />
+ <DeprecatedProperty errorLevel="info" />
+ <DeprecatedClass errorLevel="info" />
+ <DeprecatedConstant errorLevel="info" />
+ <DeprecatedFunction errorLevel="info" />
+ <DeprecatedInterface errorLevel="info" />
+ <DeprecatedTrait errorLevel="info" />
+
+ <InternalMethod errorLevel="info" />
+ <InternalProperty errorLevel="info" />
+ <InternalClass errorLevel="info" />
+
+ <MissingClosureReturnType errorLevel="info" />
+ <MissingReturnType errorLevel="info" />
+ <MissingPropertyType errorLevel="info" />
+ <InvalidDocblock errorLevel="info" />
+
+ <PropertyNotSetInConstructor errorLevel="info" />
+ <MissingConstructor errorLevel="info" />
+ <MissingClosureParamType errorLevel="info" />
+ <MissingParamType errorLevel="info" />
+
+ <RedundantCondition errorLevel="info" />
+
+ <DocblockTypeContradiction errorLevel="info" />
+ <RedundantConditionGivenDocblockType errorLevel="info" />
+
+ <UnresolvableInclude errorLevel="info" />
+
+ <RawObjectIteration errorLevel="info" />
+
+ <InvalidStringClass errorLevel="info" />
+ </issueHandlers>
+</psalm>
diff --git a/vendor/maennchen/zipstream-php/src/Bigint.php b/vendor/maennchen/zipstream-php/src/Bigint.php
new file mode 100644
index 0000000..f2565e9
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Bigint.php
@@ -0,0 +1,174 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use OverflowException;
+
+class Bigint
+{
+ /**
+ * @var int[]
+ */
+ private $bytes = [0, 0, 0, 0, 0, 0, 0, 0];
+
+ /**
+ * Initialize the bytes array
+ *
+ * @param int $value
+ */
+ public function __construct(int $value = 0)
+ {
+ $this->fillBytes($value, 0, 8);
+ }
+
+ /**
+ * Get an instance
+ *
+ * @param int $value
+ * @return Bigint
+ */
+ public static function init(int $value = 0): self
+ {
+ return new self($value);
+ }
+
+ /**
+ * Fill bytes from low to high
+ *
+ * @param int $low
+ * @param int $high
+ * @return Bigint
+ */
+ public static function fromLowHigh(int $low, int $high): self
+ {
+ $bigint = new self();
+ $bigint->fillBytes($low, 0, 4);
+ $bigint->fillBytes($high, 4, 4);
+ return $bigint;
+ }
+
+ /**
+ * Get high 32
+ *
+ * @return int
+ */
+ public function getHigh32(): int
+ {
+ return $this->getValue(4, 4);
+ }
+
+ /**
+ * Get value from bytes array
+ *
+ * @param int $end
+ * @param int $length
+ * @return int
+ */
+ public function getValue(int $end = 0, int $length = 8): int
+ {
+ $result = 0;
+ for ($i = $end + $length - 1; $i >= $end; $i--) {
+ $result <<= 8;
+ $result |= $this->bytes[$i];
+ }
+ return $result;
+ }
+
+ /**
+ * Get low FF
+ *
+ * @param bool $force
+ * @return float
+ */
+ public function getLowFF(bool $force = false): float
+ {
+ if ($force || $this->isOver32()) {
+ return (float)0xFFFFFFFF;
+ }
+ return (float)$this->getLow32();
+ }
+
+ /**
+ * Check if is over 32
+ *
+ * @psalm-suppress ArgumentTypeCoercion
+ * @param bool $force
+ * @return bool
+ */
+ public function isOver32(bool $force = false): bool
+ {
+ // value 0xFFFFFFFF already needs a Zip64 header
+ return $force ||
+ max(array_slice($this->bytes, 4, 4)) > 0 ||
+ min(array_slice($this->bytes, 0, 4)) === 0xFF;
+ }
+
+ /**
+ * Get low 32
+ *
+ * @return int
+ */
+ public function getLow32(): int
+ {
+ return $this->getValue(0, 4);
+ }
+
+ /**
+ * Get hexadecimal
+ *
+ * @return string
+ */
+ public function getHex64(): string
+ {
+ $result = '0x';
+ for ($i = 7; $i >= 0; $i--) {
+ $result .= sprintf('%02X', $this->bytes[$i]);
+ }
+ return $result;
+ }
+
+ /**
+ * Add
+ *
+ * @param Bigint $other
+ * @return Bigint
+ */
+ public function add(self $other): self
+ {
+ $result = clone $this;
+ $overflow = false;
+ for ($i = 0; $i < 8; $i++) {
+ $result->bytes[$i] += $other->bytes[$i];
+ if ($overflow) {
+ $result->bytes[$i]++;
+ $overflow = false;
+ }
+ if ($result->bytes[$i] & 0x100) {
+ $overflow = true;
+ $result->bytes[$i] &= 0xFF;
+ }
+ }
+ if ($overflow) {
+ 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;
+ }
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/DeflateStream.php b/vendor/maennchen/zipstream-php/src/DeflateStream.php
new file mode 100644
index 0000000..8e1e579
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/DeflateStream.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+class DeflateStream extends Stream
+{
+ protected $filter;
+
+ /**
+ * @var Option\File
+ */
+ protected $options;
+
+ /**
+ * Rewind stream
+ *
+ * @return void
+ */
+ public function rewind(): void
+ {
+ // deflate filter needs to be removed before rewind
+ if ($this->filter) {
+ $this->removeDeflateFilter();
+ $this->seek(0);
+ $this->addDeflateFilter($this->options);
+ } else {
+ rewind($this->stream);
+ }
+ }
+
+ /**
+ * Remove the deflate filter
+ *
+ * @return void
+ */
+ public function removeDeflateFilter(): void
+ {
+ if (!$this->filter) {
+ return;
+ }
+ stream_filter_remove($this->filter);
+ $this->filter = null;
+ }
+
+ /**
+ * 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
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception.php b/vendor/maennchen/zipstream-php/src/Exception.php
new file mode 100644
index 0000000..03a8767
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * This class is only for inheriting
+ */
+abstract class Exception extends \Exception
+{
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/EncodingException.php b/vendor/maennchen/zipstream-php/src/Exception/EncodingException.php
new file mode 100644
index 0000000..5b0267d
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/EncodingException.php
@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if file or comment encoding is incorrect
+ */
+class EncodingException extends Exception
+{
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php b/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php
new file mode 100644
index 0000000..eb82001
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a file wasn't found
+ */
+class FileNotFoundException extends Exception
+{
+ /**
+ * Constructor of the Exception
+ *
+ * @param String $path - The path which wasn't found
+ */
+ public function __construct(string $path)
+ {
+ parent::__construct("The file with the path $path wasn't found.");
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php b/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php
new file mode 100644
index 0000000..1fbfdc5
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a file wasn't found
+ */
+class FileNotReadableException extends Exception
+{
+ /**
+ * Constructor of the Exception
+ *
+ * @param String $path - The path which wasn't found
+ */
+ public function __construct(string $path)
+ {
+ parent::__construct("The file with the path $path isn't readable.");
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php b/vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php
new file mode 100644
index 0000000..2f1a7ef
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php
@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if options are incompatible
+ */
+class IncompatibleOptionsException extends Exception
+{
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php b/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php
new file mode 100644
index 0000000..a1bc4d0
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php
@@ -0,0 +1,18 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a counter value exceeds storage size
+ */
+class OverflowException extends Exception
+{
+ public function __construct()
+ {
+ parent::__construct('File size exceeds limit of 32 bit integer. Please enable "zip64" option.');
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php b/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php
new file mode 100644
index 0000000..e676e37
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if `fread` fails on a stream.
+ */
+class StreamNotReadableException extends Exception
+{
+ /**
+ * Constructor of the Exception
+ *
+ * @param string $fileName - The name of the file which the stream belongs to.
+ */
+ public function __construct(string $fileName)
+ {
+ parent::__construct("The stream for $fileName could not be read.");
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/File.php b/vendor/maennchen/zipstream-php/src/File.php
new file mode 100644
index 0000000..586d3d1
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/File.php
@@ -0,0 +1,483 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use HashContext;
+use Psr\Http\Message\StreamInterface;
+use ZipStream\Exception\FileNotFoundException;
+use ZipStream\Exception\FileNotReadableException;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Option\File as FileOptions;
+use ZipStream\Option\Method;
+use ZipStream\Option\Version;
+
+class File
+{
+ public const HASH_ALGORITHM = 'crc32b';
+
+ public const BIT_ZERO_HEADER = 0x0008;
+
+ public const BIT_EFS_UTF8 = 0x0800;
+
+ public const COMPUTE = 1;
+
+ public const SEND = 2;
+
+ private const CHUNKED_READ_BLOCK_SIZE = 1048576;
+
+ /**
+ * @var string
+ */
+ public $name;
+
+ /**
+ * @var FileOptions
+ */
+ public $opt;
+
+ /**
+ * @var Bigint
+ */
+ public $len;
+
+ /**
+ * @var Bigint
+ */
+ public $zlen;
+
+ /** @var int */
+ public $crc;
+
+ /**
+ * @var Bigint
+ */
+ public $hlen;
+
+ /**
+ * @var Bigint
+ */
+ public $ofs;
+
+ /**
+ * @var int
+ */
+ public $bits;
+
+ /**
+ * @var Version
+ */
+ public $version;
+
+ /**
+ * @var ZipStream
+ */
+ public $zip;
+
+ /**
+ * @var resource
+ */
+ private $deflate;
+
+ /**
+ * @var HashContext
+ */
+ private $hash;
+
+ /**
+ * @var Method
+ */
+ private $method;
+
+ /**
+ * @var Bigint
+ */
+ private $totalLength;
+
+ public function __construct(ZipStream $zip, string $name, ?FileOptions $opt = null)
+ {
+ $this->zip = $zip;
+
+ $this->name = $name;
+ $this->opt = $opt ?: new FileOptions();
+ $this->method = $this->opt->getMethod();
+ $this->version = Version::STORE();
+ $this->ofs = new Bigint();
+ }
+
+ public function processPath(string $path): void
+ {
+ if (!is_readable($path)) {
+ if (!file_exists($path)) {
+ throw new FileNotFoundException($path);
+ }
+ throw new FileNotReadableException($path);
+ }
+ if ($this->zip->isLargeFile($path) === false) {
+ $data = file_get_contents($path);
+ $this->processData($data);
+ } else {
+ $this->method = $this->zip->opt->getLargeFileMethod();
+
+ $stream = new DeflateStream(fopen($path, 'rb'));
+ $this->processStream($stream);
+ $stream->close();
+ }
+ }
+
+ public function processData(string $data): void
+ {
+ $this->len = new Bigint(strlen($data));
+ $this->crc = crc32($data);
+
+ // compress data if needed
+ if ($this->method->equals(Method::DEFLATE())) {
+ $data = gzdeflate($data);
+ }
+
+ $this->zlen = new Bigint(strlen($data));
+ $this->addFileHeader();
+ $this->zip->send($data);
+ $this->addFileFooter();
+ }
+
+ /**
+ * Create and send zip header for this file.
+ *
+ * @return void
+ * @throws \ZipStream\Exception\EncodingException
+ */
+ public function addFileHeader(): void
+ {
+ $name = static::filterFilename($this->name);
+
+ // calculate name length
+ $nameLength = strlen($name);
+
+ // create dos timestamp
+ $time = static::dosTime($this->opt->getTime()->getTimestamp());
+
+ $comment = $this->opt->getComment();
+
+ if (!mb_check_encoding($name, 'ASCII') ||
+ !mb_check_encoding($comment, 'ASCII')) {
+ // 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')) {
+ $this->bits |= self::BIT_EFS_UTF8;
+ }
+ }
+
+ if ($this->method->equals(Method::DEFLATE())) {
+ $this->version = Version::DEFLATE();
+ }
+
+ $force = (bool)($this->bits & self::BIT_ZERO_HEADER) &&
+ $this->zip->opt->isEnableZip64();
+
+ $footer = $this->buildZip64ExtraBlock($force);
+
+ // If this file will start over 4GB limit in ZIP file,
+ // CDR record will have to use Zip64 extension to describe offset
+ // to keep consistency we use the same value here
+ if ($this->zip->ofs->isOver32()) {
+ $this->version = Version::ZIP64();
+ }
+
+ $fields = [
+ ['V', ZipStream::FILE_HEADER_SIGNATURE],
+ ['v', $this->version->getValue()], // Version needed to Extract
+ ['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 of data (0 -> moved to data descriptor footer)
+ ['V', $this->zlen->getLowFF($force)], // Length of compressed data (forced to 0xFFFFFFFF for zero header)
+ ['V', $this->len->getLowFF($force)], // Length of original data (forced to 0xFFFFFFFF for zero header)
+ ['v', $nameLength], // Length of filename
+ ['v', strlen($footer)], // Extra data (see above)
+ ];
+
+ // pack fields and calculate "total" length
+ $header = ZipStream::packFields($fields);
+
+ // print header and filename
+ $data = $header . $name . $footer;
+ $this->zip->send($data);
+
+ // save header length
+ $this->hlen = Bigint::init(strlen($data));
+ }
+
+ /**
+ * Strip characters that are not legal in Windows filenames
+ * to prevent compatibility issues
+ *
+ * @param string $filename Unprocessed filename
+ * @return string
+ */
+ public static function filterFilename(string $filename): string
+ {
+ // strip leading slashes from file name
+ // (fixes bug in windows archive viewer)
+ $filename = preg_replace('/^\\/+/', '', $filename);
+
+ 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.
+ *
+ * @param int $when
+ * @return int DOS Timestamp
+ */
+ final protected static function dosTime(int $when): int
+ {
+ // get date array for timestamp
+ $d = getdate($when);
+
+ // set lower-bound on dates
+ if ($d['year'] < 1980) {
+ $d = [
+ 'year' => 1980,
+ 'mon' => 1,
+ 'mday' => 1,
+ 'hours' => 0,
+ 'minutes' => 0,
+ 'seconds' => 0,
+ ];
+ }
+
+ // remove extra years from 1980
+ $d['year'] -= 1980;
+
+ // return date string
+ return
+ ($d['year'] << 25) |
+ ($d['mon'] << 21) |
+ ($d['mday'] << 16) |
+ ($d['hours'] << 11) |
+ ($d['minutes'] << 5) |
+ ($d['seconds'] >> 1);
+ }
+
+ protected function buildZip64ExtraBlock(bool $force = false): string
+ {
+ $fields = [];
+ if ($this->len->isOver32($force)) {
+ $fields[] = ['P', $this->len]; // Length of original data
+ }
+
+ if ($this->len->isOver32($force)) {
+ $fields[] = ['P', $this->zlen]; // Length of compressed data
+ }
+
+ if ($this->ofs->isOver32()) {
+ $fields[] = ['P', $this->ofs]; // Offset of local header record
+ }
+
+ if (!empty($fields)) {
+ if (!$this->zip->opt->isEnableZip64()) {
+ throw new OverflowException();
+ }
+
+ array_unshift(
+ $fields,
+ ['v', 0x0001], // 64 bit extension
+ ['v', count($fields) * 8] // Length of data block
+ );
+ $this->version = Version::ZIP64();
+ }
+
+ if ($this->bits & self::BIT_EFS_UTF8) {
+ // Put the tricky entry to
+ // force Linux unzip to lookup EFS flag.
+ $fields[] = ['v', 0x5653]; // Choose 'ZS' for proprietary usage
+ $fields[] = ['v', 0x0000]; // zero length
+ }
+
+ return ZipStream::packFields($fields);
+ }
+
+ protected function processStreamWithZeroHeader(StreamInterface $stream): void
+ {
+ $this->bits |= self::BIT_ZERO_HEADER;
+ $this->addFileHeader();
+ $this->readStream($stream, self::COMPUTE | self::SEND);
+ $this->addFileFooter();
+ }
+
+ protected function readStream(StreamInterface $stream, ?int $options = null): void
+ {
+ $this->deflateInit();
+ $total = 0;
+ $size = $this->opt->getSize();
+ while (!$stream->eof() && ($size === 0 || $total < $size)) {
+ $data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE);
+ $total += strlen($data);
+ if ($size > 0 && $total > $size) {
+ $data = substr($data, 0, strlen($data)-($total - $size));
+ }
+ $this->deflateData($stream, $data, $options);
+ if ($options & self::SEND) {
+ $this->zip->send($data);
+ }
+ }
+ $this->deflateFinish($options);
+ }
+
+ protected function deflateInit(): void
+ {
+ $hash = hash_init(self::HASH_ALGORITHM);
+ $this->hash = $hash;
+ if ($this->method->equals(Method::DEFLATE())) {
+ $this->deflate = deflate_init(
+ ZLIB_ENCODING_RAW,
+ ['level' => $this->opt->getDeflateLevel()]
+ );
+ }
+ }
+
+ protected function deflateData(StreamInterface $stream, string &$data, ?int $options = null): void
+ {
+ if ($options & self::COMPUTE) {
+ $this->len = $this->len->add(Bigint::init(strlen($data)));
+ hash_update($this->hash, $data);
+ }
+ if ($this->deflate) {
+ $data = deflate_add(
+ $this->deflate,
+ $data,
+ $stream->eof()
+ ? ZLIB_FINISH
+ : ZLIB_NO_FLUSH
+ );
+ }
+ if ($options & self::COMPUTE) {
+ $this->zlen = $this->zlen->add(Bigint::init(strlen($data)));
+ }
+ }
+
+ protected function deflateFinish(?int $options = null): void
+ {
+ if ($options & self::COMPUTE) {
+ $this->crc = hexdec(hash_final($this->hash));
+ }
+ }
+
+ protected function processStreamWithComputedHeader(StreamInterface $stream): void
+ {
+ $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();
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Archive.php b/vendor/maennchen/zipstream-php/src/Option/Archive.php
new file mode 100644
index 0000000..374dd1d
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/Archive.php
@@ -0,0 +1,276 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Option;
+
+use Psr\Http\Message\StreamInterface;
+
+final class Archive
+{
+ 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
+ * addFileFromPath()). Large files may also
+ * be compressed differently; see the
+ * 'largeFileMethod' option. Default is ~20 Mb.
+ *
+ * @var int
+ */
+ private $largeFileSize = 20 * 1024 * 1024;
+
+ /**
+ * How to handle large files. Legal values are
+ * Method::STORE() (the default), or
+ * Method::DEFLATE(). STORE sends the file
+ * raw and is significantly
+ * faster, while DEFLATE compresses the file
+ * and is much, much slower. Note that DEFLATE
+ * must compress the file twice and is extremely slow.
+ *
+ * @var Method
+ */
+ private $largeFileMethod;
+
+ /**
+ * Boolean indicating whether or not to send
+ * the HTTP headers for this file.
+ *
+ * @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)
+ *
+ * @var bool
+ */
+ private $enableZip64 = true;
+
+ /**
+ * Enable streaming files with single read where
+ * general purpose bit 3 indicates local file header
+ * contain zero values in crc and size fields,
+ * these appear only after file contents
+ * in data descriptor block.
+ *
+ * @var bool
+ */
+ private $zeroHeader = false;
+
+ /**
+ * Enable reading file stat for determining file size.
+ * When a 32-bit system reads file size that is
+ * over 2 GB, invalid value appears in file size
+ * due to integer overflow. Should be disabled on
+ * 32-bit systems with method addFileFromPath
+ * if any file may exceed 2 GB. In this case file
+ * will be read in blocks and correct size will be
+ * determined from content.
+ *
+ * @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
+ * FILENAME is the specified filename.
+ *
+ * Note that this does nothing if you are
+ * not sending HTTP headers.
+ *
+ * @var string
+ */
+ private $contentDisposition = 'attachment';
+
+ /**
+ * Note that this does nothing if you are
+ * not sending HTTP headers.
+ *
+ * @var string
+ */
+ private $contentType = 'application/x-zip';
+
+ /**
+ * @var int
+ */
+ private $deflateLevel = 6;
+
+ /**
+ * @var StreamInterface|resource
+ */
+ private $outputStream;
+
+ /**
+ * Options constructor.
+ */
+ public function __construct()
+ {
+ $this->largeFileMethod = Method::STORE();
+ $this->outputStream = fopen('php://output', 'wb');
+ }
+
+ public function getComment(): string
+ {
+ return $this->comment;
+ }
+
+ public function setComment(string $comment): void
+ {
+ $this->comment = $comment;
+ }
+
+ public function getLargeFileSize(): int
+ {
+ return $this->largeFileSize;
+ }
+
+ public function setLargeFileSize(int $largeFileSize): void
+ {
+ $this->largeFileSize = $largeFileSize;
+ }
+
+ public function getLargeFileMethod(): Method
+ {
+ return $this->largeFileMethod;
+ }
+
+ public function setLargeFileMethod(Method $largeFileMethod): void
+ {
+ $this->largeFileMethod = $largeFileMethod;
+ }
+
+ public function isSendHttpHeaders(): bool
+ {
+ return $this->sendHttpHeaders;
+ }
+
+ public function setSendHttpHeaders(bool $sendHttpHeaders): void
+ {
+ $this->sendHttpHeaders = $sendHttpHeaders;
+ }
+
+ public function getHttpHeaderCallback(): callable
+ {
+ return $this->httpHeaderCallback;
+ }
+
+ public function setHttpHeaderCallback(callable $httpHeaderCallback): void
+ {
+ $this->httpHeaderCallback = $httpHeaderCallback;
+ }
+
+ public function isEnableZip64(): bool
+ {
+ return $this->enableZip64;
+ }
+
+ public function setEnableZip64(bool $enableZip64): void
+ {
+ $this->enableZip64 = $enableZip64;
+ }
+
+ public function isZeroHeader(): bool
+ {
+ return $this->zeroHeader;
+ }
+
+ public function setZeroHeader(bool $zeroHeader): void
+ {
+ $this->zeroHeader = $zeroHeader;
+ }
+
+ public function isFlushOutput(): bool
+ {
+ return $this->flushOutput;
+ }
+
+ public function setFlushOutput(bool $flushOutput): void
+ {
+ $this->flushOutput = $flushOutput;
+ }
+
+ public function isStatFiles(): bool
+ {
+ return $this->statFiles;
+ }
+
+ public function setStatFiles(bool $statFiles): void
+ {
+ $this->statFiles = $statFiles;
+ }
+
+ public function getContentDisposition(): string
+ {
+ return $this->contentDisposition;
+ }
+
+ public function setContentDisposition(string $contentDisposition): void
+ {
+ $this->contentDisposition = $contentDisposition;
+ }
+
+ public function getContentType(): string
+ {
+ return $this->contentType;
+ }
+
+ public function setContentType(string $contentType): void
+ {
+ $this->contentType = $contentType;
+ }
+
+ /**
+ * @return StreamInterface|resource
+ */
+ public function getOutputStream()
+ {
+ return $this->outputStream;
+ }
+
+ /**
+ * @param StreamInterface|resource $outputStream
+ */
+ public function setOutputStream($outputStream): void
+ {
+ $this->outputStream = $outputStream;
+ }
+
+ /**
+ * @return int
+ */
+ public function getDeflateLevel(): int
+ {
+ return $this->deflateLevel;
+ }
+
+ /**
+ * @param int $deflateLevel
+ */
+ public function setDeflateLevel(int $deflateLevel): void
+ {
+ $this->deflateLevel = $deflateLevel;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/File.php b/vendor/maennchen/zipstream-php/src/Option/File.php
new file mode 100644
index 0000000..37e37ce
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/File.php
@@ -0,0 +1,122 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Option;
+
+use DateTime;
+use DateTimeInterface;
+
+final class File
+{
+ /**
+ * @var string
+ */
+ private $comment = '';
+
+ /**
+ * @var Method
+ */
+ private $method;
+
+ /**
+ * @var int
+ */
+ private $deflateLevel;
+
+ /**
+ * @var DateTimeInterface
+ */
+ private $time;
+
+ /**
+ * @var int
+ */
+ private $size = 0;
+
+ public function defaultTo(Archive $archiveOptions): void
+ {
+ $this->deflateLevel = $this->deflateLevel ?: $archiveOptions->getDeflateLevel();
+ $this->time = $this->time ?: new DateTime();
+ }
+
+ /**
+ * @return string
+ */
+ public function getComment(): string
+ {
+ return $this->comment;
+ }
+
+ /**
+ * @param string $comment
+ */
+ public function setComment(string $comment): void
+ {
+ $this->comment = $comment;
+ }
+
+ /**
+ * @return Method
+ */
+ public function getMethod(): Method
+ {
+ return $this->method ?: Method::DEFLATE();
+ }
+
+ /**
+ * @param Method $method
+ */
+ public function setMethod(Method $method): void
+ {
+ $this->method = $method;
+ }
+
+ /**
+ * @return int
+ */
+ public function getDeflateLevel(): int
+ {
+ return $this->deflateLevel ?: Archive::DEFAULT_DEFLATE_LEVEL;
+ }
+
+ /**
+ * @param int $deflateLevel
+ */
+ public function setDeflateLevel(int $deflateLevel): void
+ {
+ $this->deflateLevel = $deflateLevel;
+ }
+
+ /**
+ * @return DateTimeInterface
+ */
+ public function getTime(): DateTimeInterface
+ {
+ return $this->time;
+ }
+
+ /**
+ * @param DateTimeInterface $time
+ */
+ public function setTime(DateTimeInterface $time): void
+ {
+ $this->time = $time;
+ }
+
+ /**
+ * @return int
+ */
+ public function getSize(): int
+ {
+ return $this->size;
+ }
+
+ /**
+ * @param int $size
+ */
+ public function setSize(int $size): void
+ {
+ $this->size = $size;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Method.php b/vendor/maennchen/zipstream-php/src/Option/Method.php
new file mode 100644
index 0000000..343b258
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/Method.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Option;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * Methods enum
+ *
+ * @method static STORE(): Method
+ * @method static DEFLATE(): Method
+ * @psalm-immutable
+ */
+class Method extends Enum
+{
+ public const STORE = 0x00;
+
+ public const DEFLATE = 0x08;
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Version.php b/vendor/maennchen/zipstream-php/src/Option/Version.php
new file mode 100644
index 0000000..cb664ca
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Option/Version.php
@@ -0,0 +1,25 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Option;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * Class Version
+ * @package ZipStream\Option
+ *
+ * @method static STORE(): Version
+ * @method static DEFLATE(): Version
+ * @method static ZIP64(): Version
+ * @psalm-immutable
+ */
+class Version extends Enum
+{
+ public const STORE = 0x000A; // 1.00
+
+ public const DEFLATE = 0x0014; // 2.00
+
+ public const ZIP64 = 0x002D; // 4.50
+}
diff --git a/vendor/maennchen/zipstream-php/src/Stream.php b/vendor/maennchen/zipstream-php/src/Stream.php
new file mode 100644
index 0000000..d80e70f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Stream.php
@@ -0,0 +1,265 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use function mb_strlen;
+
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+
+/**
+ * Describes a data stream.
+ *
+ * Typically, an instance will wrap a PHP stream; this interface provides
+ * a wrapper around the most common operations, including serialization of
+ * the entire stream to a string.
+ */
+class Stream implements StreamInterface
+{
+ protected $stream;
+
+ public function __construct($stream)
+ {
+ $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.
+ *
+ * @return void
+ */
+ public function close(): void
+ {
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ $this->detach();
+ }
+
+ /**
+ * Separates any underlying resources from the stream.
+ *
+ * After the stream has been detached, the stream is in an unusable state.
+ *
+ * @return resource|null Underlying PHP stream, if any
+ */
+ public function detach()
+ {
+ $result = $this->stream;
+ $this->stream = null;
+ return $result;
+ }
+
+ /**
+ * Seek to a position in the stream.
+ *
+ * @link http://www.php.net/manual/en/function.fseek.php
+ * @param int $offset Stream offset
+ * @param int $whence Specifies how the cursor position will be calculated
+ * based on the seek offset. Valid values are identical to the built-in
+ * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
+ * offset bytes SEEK_CUR: Set position to current location plus offset
+ * SEEK_END: Set position to end-of-stream plus offset.
+ * @throws RuntimeException on failure.
+ */
+ public function seek($offset, $whence = SEEK_SET): void
+ {
+ if (!$this->isSeekable()) {
+ throw new RuntimeException();
+ }
+ if (fseek($this->stream, $offset, $whence) !== 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Returns whether or not the stream is seekable.
+ *
+ * @return bool
+ */
+ public function isSeekable(): bool
+ {
+ return (bool)$this->getMetadata('seekable');
+ }
+
+ /**
+ * Get stream metadata as an associative array or retrieve a specific key.
+ *
+ * The keys returned are identical to the keys returned from PHP's
+ * stream_get_meta_data() function.
+ *
+ * @link http://php.net/manual/en/function.stream-get-meta-data.php
+ * @param string $key Specific metadata to retrieve.
+ * @return array|mixed|null Returns an associative array if no key is
+ * provided. Returns a specific key value if a key is provided and the
+ * value is found, or null if the key is not found.
+ */
+ public function getMetadata($key = null)
+ {
+ $metadata = stream_get_meta_data($this->stream);
+ return $key !== null ? @$metadata[$key] : $metadata;
+ }
+
+ /**
+ * Get the size of the stream if known.
+ *
+ * @return int|null Returns the size in bytes if known, or null if unknown.
+ */
+ public function getSize(): ?int
+ {
+ $stats = fstat($this->stream);
+ return $stats['size'];
+ }
+
+ /**
+ * Returns the current position of the file read/write pointer
+ *
+ * @return int Position of the file pointer
+ * @throws RuntimeException on error.
+ */
+ public function tell(): int
+ {
+ $position = ftell($this->stream);
+ if ($position === false) {
+ throw new RuntimeException();
+ }
+ return $position;
+ }
+
+ /**
+ * Returns true if the stream is at the end of the stream.
+ *
+ * @return bool
+ */
+ public function eof(): bool
+ {
+ return feof($this->stream);
+ }
+
+ /**
+ * Seek to the beginning of the stream.
+ *
+ * If the stream is not seekable, this method will raise an exception;
+ * otherwise, it will perform a seek(0).
+ *
+ * @see seek()
+ * @link http://www.php.net/manual/en/function.fseek.php
+ * @throws RuntimeException on failure.
+ */
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ /**
+ * Write data to the stream.
+ *
+ * @param string $string The string that is to be written.
+ * @return int Returns the number of bytes written to the stream.
+ * @throws RuntimeException on failure.
+ */
+ public function write($string): int
+ {
+ if (!$this->isWritable()) {
+ throw new RuntimeException();
+ }
+ if (fwrite($this->stream, $string) === false) {
+ throw new RuntimeException();
+ }
+ return mb_strlen($string);
+ }
+
+ /**
+ * Returns whether or not the stream is writable.
+ *
+ * @return bool
+ */
+ public function isWritable(): bool
+ {
+ $mode = $this->getMetadata('mode');
+ if (!is_string($mode)) {
+ throw new RuntimeException('Could not get stream mode from metadata!');
+ }
+ return preg_match('/[waxc+]/', $mode) === 1;
+ }
+
+ /**
+ * Read data from the stream.
+ *
+ * @param int $length Read up to $length bytes from the object and return
+ * them. Fewer than $length bytes may be returned if underlying stream
+ * 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.
+ */
+ public function read($length): string
+ {
+ if (!$this->isReadable()) {
+ throw new RuntimeException();
+ }
+ $result = fread($this->stream, $length);
+ if ($result === false) {
+ throw new RuntimeException();
+ }
+ return $result;
+ }
+
+ /**
+ * Returns whether or not the stream is readable.
+ *
+ * @return bool
+ */
+ public function isReadable(): bool
+ {
+ $mode = $this->getMetadata('mode');
+ if (!is_string($mode)) {
+ throw new RuntimeException('Could not get stream mode from metadata!');
+ }
+ return preg_match('/[r+]/', $mode) === 1;
+ }
+
+ /**
+ * Returns the remaining contents in a string
+ *
+ * @return string
+ * @throws RuntimeException if unable to read or an error occurs while
+ * reading.
+ */
+ public function getContents(): string
+ {
+ if (!$this->isReadable()) {
+ throw new RuntimeException();
+ }
+ $result = stream_get_contents($this->stream);
+ if ($result === false) {
+ throw new RuntimeException();
+ }
+ return $result;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/ZipStream.php b/vendor/maennchen/zipstream-php/src/ZipStream.php
new file mode 100644
index 0000000..eb5474a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/ZipStream.php
@@ -0,0 +1,608 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use Psr\Http\Message\StreamInterface;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Option\Archive as ArchiveOptions;
+use ZipStream\Option\File as FileOptions;
+use ZipStream\Option\Version;
+
+/**
+ * ZipStream
+ *
+ * Streamed, dynamically generated zip archives.
+ *
+ * Usage:
+ *
+ * Streaming zip archives is a simple, three-step process:
+ *
+ * 1. Create the zip stream:
+ *
+ * $zip = new ZipStream('example.zip');
+ *
+ * 2. Add one or more files to the archive:
+ *
+ * * add first file
+ * $data = file_get_contents('some_file.gif');
+ * $zip->addFile('some_file.gif', $data);
+ *
+ * * add second file
+ * $data = file_get_contents('some_file.gif');
+ * $zip->addFile('another_file.png', $data);
+ *
+ * 3. Finish the zip stream:
+ *
+ * $zip->finish();
+ *
+ * You can also add an archive comment, add comments to individual files,
+ * and adjust the timestamp of files. See the API documentation for each
+ * method below for additional information.
+ *
+ * Example:
+ *
+ * // create a new zip stream object
+ * $zip = new ZipStream('some_files.zip');
+ *
+ * // list of local files
+ * $files = array('foo.txt', 'bar.jpg');
+ *
+ * // read and add each file to the archive
+ * foreach ($files as $path)
+ * $zip->addFile($path, file_get_contents($path));
+ *
+ * // write archive footer to stream
+ * $zip->finish();
+ */
+class ZipStream
+{
+ /**
+ * This number corresponds to the ZIP version/OS used (2 bytes)
+ * From: https://www.iana.org/assignments/media-types/application/zip
+ * The upper byte (leftmost one) indicates the host system (OS) for the
+ * file. Software can use this information to determine
+ * the line record format for text files etc. The current
+ * mappings are:
+ *
+ * 0 - MS-DOS and OS/2 (F.A.T. file systems)
+ * 1 - Amiga 2 - VAX/VMS
+ * 3 - *nix 4 - VM/CMS
+ * 5 - Atari ST 6 - OS/2 H.P.F.S.
+ * 7 - Macintosh 8 - Z-System
+ * 9 - CP/M 10 thru 255 - unused
+ *
+ * The lower byte (rightmost one) indicates the version number of the
+ * software used to encode the file. The value/10
+ * indicates the major version number, and the value
+ * mod 10 is the minor version number.
+ * Here we are using 6 for the OS, indicating OS/2 H.P.F.S.
+ * to prevent file permissions issues upon extract (see #84)
+ * 0x603 is 00000110 00000011 in binary, so 6 and 3
+ */
+ public const ZIP_VERSION_MADE_BY = 0x603;
+
+ /**
+ * 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
+ */
+ 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
+ *
+ * @var ArchiveOptions
+ */
+ public $opt;
+
+ /**
+ * @var array
+ */
+ public $files = [];
+
+ /**
+ * @var Bigint
+ */
+ public $cdr_ofs;
+
+ /**
+ * @var Bigint
+ */
+ public $ofs;
+
+ /**
+ * @var bool
+ */
+ protected $need_headers;
+
+ /**
+ * @var null|String
+ */
+ protected $output_name;
+
+ /**
+ * Create a new ZipStream object.
+ *
+ * Parameters:
+ *
+ * @param String $name - Name of output file (optional).
+ * @param ArchiveOptions $opt - Archive Options
+ *
+ * Large File Support:
+ *
+ * By default, the method addFileFromPath() will send send files
+ * larger than 20 megabytes along raw rather than attempting to
+ * compress them. You can change both the maximum size and the
+ * compression behavior using the largeFile* options above, with the
+ * following caveats:
+ *
+ * * For "small" files (e.g. files smaller than largeFileSize), the
+ * memory use can be up to twice that of the actual file. In other
+ * words, adding a 10 megabyte file to the archive could potentially
+ * occupy 20 megabytes of memory.
+ *
+ * * Enabling compression on large files (e.g. files larger than
+ * large_file_size) is extremely slow, because ZipStream has to pass
+ * over the large file once to calculate header information, and then
+ * again to compress and send the actual data.
+ *
+ * Examples:
+ *
+ * // create a new zip file named 'foo.zip'
+ * $zip = new ZipStream('foo.zip');
+ *
+ * // create a new zip file named 'bar.zip' with a comment
+ * $opt->setComment = 'this is a comment for the zip file.';
+ * $zip = new ZipStream('bar.zip', $opt);
+ *
+ * Notes:
+ *
+ * In order to let this library send HTTP headers, a filename must be given
+ * _and_ the option `sendHttpHeaders` must be `true`. This behavior is to
+ * allow software to send its own headers (including the filename), and
+ * still use this library.
+ */
+ public function __construct(?string $name = null, ?ArchiveOptions $opt = null)
+ {
+ $this->opt = $opt ?: new ArchiveOptions();
+
+ $this->output_name = $name;
+ $this->need_headers = $name && $this->opt->isSendHttpHeaders();
+
+ $this->cdr_ofs = new Bigint();
+ $this->ofs = new Bigint();
+ }
+
+ /**
+ * addFile
+ *
+ * Add a file to the archive.
+ *
+ * @param String $name - path of file in archive (including directory).
+ * @param String $data - contents of file
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ * method - Storage method for file ("store" or "deflate")
+ *
+ * Examples:
+ *
+ * // add a file named 'foo.txt'
+ * $data = file_get_contents('foo.txt');
+ * $zip->addFile('foo.txt', $data);
+ *
+ * // add a file named 'bar.jpg' with a comment and a last-modified
+ * // time of two hours ago
+ * $data = file_get_contents('bar.jpg');
+ * $opt->setTime = time() - 2 * 3600;
+ * $opt->setComment = 'this is a comment about bar.jpg';
+ * $zip->addFile('bar.jpg', $data, $opt);
+ */
+ public function addFile(string $name, string $data, ?FileOptions $options = null): void
+ {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processData($data);
+ }
+
+ /**
+ * addFileFromPath
+ *
+ * Add a file at path to the archive.
+ *
+ * Note that large files may be compressed differently than smaller
+ * files; see the "Large File Support" section above for more
+ * information.
+ *
+ * @param String $name - name of file in archive (including directory path).
+ * @param String $path - path to file on disk (note: paths should be encoded using
+ * UNIX-style forward slashes -- e.g '/path/to/some/file').
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ * method - Storage method for file ("store" or "deflate")
+ *
+ * Examples:
+ *
+ * // add a file named 'foo.txt' from the local file '/tmp/foo.txt'
+ * $zip->addFileFromPath('foo.txt', '/tmp/foo.txt');
+ *
+ * // 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
+ * $path = '/usr/share/bigfile.rar';
+ * $opt->setTime = time() - 2 * 3600;
+ * $opt->setComment = 'this is a comment about bar.jpg';
+ * $zip->addFileFromPath('bigfile.rar', $path, $opt);
+ *
+ * @return void
+ * @throws \ZipStream\Exception\FileNotFoundException
+ * @throws \ZipStream\Exception\FileNotReadableException
+ */
+ public function addFileFromPath(string $name, string $path, ?FileOptions $options = null): void
+ {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processPath($path);
+ }
+
+ /**
+ * addFileFromStream
+ *
+ * Add an open stream to the archive.
+ *
+ * @param String $name - path of file in archive (including directory).
+ * @param resource $stream - contents of file as a stream resource
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ *
+ * Examples:
+ *
+ * // create a temporary file stream and write text to it
+ * $fp = tmpfile();
+ * fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
+ *
+ * // add a file named 'streamfile.txt' from the content of the stream
+ * $x->addFileFromStream('streamfile.txt', $fp);
+ *
+ * @return void
+ */
+ public function addFileFromStream(string $name, $stream, ?FileOptions $options = null): void
+ {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processStream(new DeflateStream($stream));
+ }
+
+ /**
+ * addFileFromPsr7Stream
+ *
+ * Add an open stream to the archive.
+ *
+ * @param String $name - path of file in archive (including directory).
+ * @param StreamInterface $stream - contents of file as a stream resource
+ * @param FileOptions $options
+ *
+ * File Options:
+ * time - Last-modified timestamp (seconds since the epoch) of
+ * this file. Defaults to the current time.
+ * comment - Comment related to this file.
+ *
+ * Examples:
+ *
+ * $stream = $response->getBody();
+ * // add a file named 'streamfile.txt' from the content of the stream
+ * $x->addFileFromPsr7Stream('streamfile.txt', $stream);
+ *
+ * @return void
+ */
+ public function addFileFromPsr7Stream(
+ string $name,
+ StreamInterface $stream,
+ ?FileOptions $options = null
+ ): void {
+ $options = $options ?: new FileOptions();
+ $options->defaultTo($this->opt);
+
+ $file = new File($this, $name, $options);
+ $file->processStream($stream);
+ }
+
+ /**
+ * finish
+ *
+ * Write zip footer to stream.
+ *
+ * Example:
+ *
+ * // add a list of files to the archive
+ * $files = array('foo.txt', 'bar.jpg');
+ * foreach ($files as $path)
+ * $zip->addFile($path, file_get_contents($path));
+ *
+ * // write footer to stream
+ * $zip->finish();
+ * @return void
+ *
+ * @throws OverflowException
+ */
+ public function finish(): void
+ {
+ // add trailing cdr file records
+ foreach ($this->files as $cdrFile) {
+ $this->send($cdrFile);
+ $this->cdr_ofs = $this->cdr_ofs->add(Bigint::init(strlen($cdrFile)));
+ }
+
+ // Add 64bit headers (if applicable)
+ if (count($this->files) >= 0xFFFF ||
+ $this->cdr_ofs->isOver32() ||
+ $this->ofs->isOver32()) {
+ if (!$this->opt->isEnableZip64()) {
+ throw new OverflowException();
+ }
+
+ $this->addCdr64Eof();
+ $this->addCdr64Locator();
+ }
+
+ // add trailing cdr eof record
+ $this->addCdrEof();
+
+ // The End
+ $this->clear();
+ }
+
+ /**
+ * Create a format string and argument list for pack(), then call
+ * pack() and return the result.
+ *
+ * @param array $fields
+ * @return string
+ */
+ public static function packFields(array $fields): string
+ {
+ $fmt = '';
+ $args = [];
+
+ // populate format string and argument list
+ foreach ($fields as [$format, $value]) {
+ if ($format === 'P') {
+ $fmt .= 'VV';
+ if ($value instanceof Bigint) {
+ $args[] = $value->getLow32();
+ $args[] = $value->getHigh32();
+ } else {
+ $args[] = $value;
+ $args[] = 0;
+ }
+ } else {
+ if ($value instanceof Bigint) {
+ $value = $value->getLow32();
+ }
+ $fmt .= $format;
+ $args[] = $value;
+ }
+ }
+
+ // prepend format string to argument list
+ array_unshift($args, $fmt);
+
+ // build output string from header and compressed data
+ return pack(...$args);
+ }
+
+ /**
+ * Send string, sending HTTP headers if necessary.
+ * Flush output after write if configure option is set.
+ *
+ * @param String $str
+ * @return void
+ */
+ public function send(string $str): void
+ {
+ if ($this->need_headers) {
+ $this->sendHttpHeaders();
+ }
+ $this->need_headers = false;
+
+ $outputStream = $this->opt->getOutputStream();
+
+ if ($outputStream instanceof StreamInterface) {
+ $outputStream->write($str);
+ } else {
+ fwrite($outputStream, $str);
+ }
+
+ if ($this->opt->isFlushOutput()) {
+ // flush output buffer if it is on and flushable
+ $status = ob_get_status();
+ if (isset($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
+ ob_flush();
+ }
+
+ // Flush system buffers after flushing userspace output buffer
+ flush();
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @return void
+ */
+ protected function sendHttpHeaders(): void
+ {
+ // grab content disposition
+ $disposition = $this->opt->getContentDisposition();
+
+ if ($this->output_name) {
+ // Various different browsers dislike various characters here. Strip them all for safety.
+ $safe_output = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->output_name));
+
+ // Check if we need to UTF-8 encode the filename
+ $urlencoded = rawurlencode($safe_output);
+ $disposition .= "; filename*=UTF-8''{$urlencoded}";
+ }
+
+ $headers = [
+ 'Content-Type' => $this->opt->getContentType(),
+ 'Content-Disposition' => $disposition,
+ 'Pragma' => 'public',
+ 'Cache-Control' => 'public, must-revalidate',
+ 'Content-Transfer-Encoding' => 'binary',
+ ];
+
+ $call = $this->opt->getHttpHeaderCallback();
+ foreach ($headers as $key => $val) {
+ $call("$key: $val");
+ }
+ }
+
+ /**
+ * Send ZIP64 CDR Locator (Central Directory Record Locator) record.
+ *
+ * @return void
+ */
+ protected function addCdr64Locator(): void
+ {
+ $cdr_offset = $this->ofs->add($this->cdr_ofs);
+
+ $fields = [
+ ['V', static::ZIP64_CDR_LOCATOR_SIGNATURE], // ZIP64 end of central file header signature
+ ['V', 0x00], // Disc number containing CDR64EOF
+ ['P', $cdr_offset], // CDR offset
+ ['V', 1], // Total number of disks
+ ];
+
+ $ret = static::packFields($fields);
+ $this->send($ret);
+ }
+
+ /**
+ * Send CDR EOF (Central Directory Record End-of-File) record.
+ *
+ * @return void
+ */
+ protected function addCdrEof(): void
+ {
+ $num_files = count($this->files);
+ $cdr_length = $this->cdr_ofs;
+ $cdr_offset = $this->ofs;
+
+ // grab comment (if specified)
+ $comment = $this->opt->getComment();
+
+ $fields = [
+ ['V', static::CDR_EOF_SIGNATURE], // end of central file header signature
+ ['v', 0x00], // disk number
+ ['v', 0x00], // no of disks
+ ['v', min($num_files, 0xFFFF)], // no of entries on disk
+ ['v', min($num_files, 0xFFFF)], // no of entries in cdr
+ ['V', $cdr_length->getLowFF()], // CDR size
+ ['V', $cdr_offset->getLowFF()], // CDR offset
+ ['v', strlen($comment)], // Zip Comment size
+ ];
+
+ $ret = static::packFields($fields) . $comment;
+ $this->send($ret);
+ }
+
+ /**
+ * Clear all internal variables. Note that the stream object is not
+ * usable after this.
+ *
+ * @return void
+ */
+ protected function clear(): void
+ {
+ $this->files = [];
+ $this->ofs = new Bigint();
+ $this->cdr_ofs = new Bigint();
+ $this->opt = new ArchiveOptions();
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/BigintTest.php b/vendor/maennchen/zipstream-php/test/BigintTest.php
new file mode 100644
index 0000000..4d26fcd
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/BigintTest.php
@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+
+namespace BigintTest;
+
+use OverflowException;
+use PHPUnit\Framework\TestCase;
+use ZipStream\Bigint;
+
+class BigintTest extends TestCase
+{
+ public function testConstruct(): void
+ {
+ $bigint = new Bigint(0x12345678);
+ $this->assertSame('0x0000000012345678', $bigint->getHex64());
+ $this->assertSame(0x12345678, $bigint->getLow32());
+ $this->assertSame(0, $bigint->getHigh32());
+ }
+
+ public function testConstructLarge(): void
+ {
+ $bigint = new Bigint(0x87654321);
+ $this->assertSame('0x0000000087654321', $bigint->getHex64());
+ $this->assertSame('87654321', bin2hex(pack('N', $bigint->getLow32())));
+ $this->assertSame(0, $bigint->getHigh32());
+ }
+
+ public function testAddSmallValue(): void
+ {
+ $bigint = new Bigint(1);
+ $bigint = $bigint->add(Bigint::init(2));
+ $this->assertSame(3, $bigint->getLow32());
+ $this->assertFalse($bigint->isOver32());
+ $this->assertTrue($bigint->isOver32(true));
+ $this->assertSame($bigint->getLowFF(), (float)$bigint->getLow32());
+ $this->assertSame($bigint->getLowFF(true), (float)0xFFFFFFFF);
+ }
+
+ public function testAddWithOverflowAtLowestByte(): void
+ {
+ $bigint = new Bigint(0xFF);
+ $bigint = $bigint->add(Bigint::init(0x01));
+ $this->assertSame(0x100, $bigint->getLow32());
+ }
+
+ public function testAddWithOverflowAtInteger32(): void
+ {
+ $bigint = new Bigint(0xFFFFFFFE);
+ $this->assertFalse($bigint->isOver32());
+ $bigint = $bigint->add(Bigint::init(0x01));
+ $this->assertTrue($bigint->isOver32());
+ $bigint = $bigint->add(Bigint::init(0x01));
+ $this->assertSame('0x0000000100000000', $bigint->getHex64());
+ $this->assertTrue($bigint->isOver32());
+ $this->assertSame((float)0xFFFFFFFF, $bigint->getLowFF());
+ }
+
+ public function testAddWithOverflowAtInteger64(): void
+ {
+ $bigint = Bigint::fromLowHigh(0xFFFFFFFF, 0xFFFFFFFF);
+ $this->assertSame('0xFFFFFFFFFFFFFFFF', $bigint->getHex64());
+ $this->expectException(OverflowException::class);
+ $bigint->add(Bigint::init(1));
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/ZipStreamTest.php b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
new file mode 100644
index 0000000..e9b3aee
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
@@ -0,0 +1,650 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStreamTest;
+
+use GuzzleHttp\Psr7\Response;
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
+use ReflectionClass;
+use ZipArchive;
+use ZipStream\File;
+use ZipStream\Option\Archive as ArchiveOptions;
+use ZipStream\Option\File as FileOptions;
+use ZipStream\Option\Method;
+use ZipStream\Stream;
+use ZipStream\ZipStream;
+
+/**
+ * Test Class for the Main ZipStream CLass
+ */
+class ZipStreamTest extends TestCase
+{
+ public const OSX_ARCHIVE_UTILITY =
+ '/System/Library/CoreServices/Applications/Archive Utility.app/Contents/MacOS/Archive Utility';
+
+ public function testFileNotFoundException(): void
+ {
+ $this->expectException(\ZipStream\Exception\FileNotFoundException::class);
+ // Get ZipStream Object
+ $zip = new ZipStream();
+
+ // Trigger error by adding a file which doesn't exist
+ $zip->addFileFromPath('foobar.php', '/foo/bar/foobar.php');
+ }
+
+ public function testFileNotReadableException(): void
+ {
+ // create new virtual filesystem
+ $root = vfsStream::setup('vfs');
+ // create a virtual file with no permissions
+ $file = vfsStream::newFile('foo.txt', 0)->at($root)->setContent('bar');
+ $zip = new ZipStream();
+ $this->expectException(\ZipStream\Exception\FileNotReadableException::class);
+ $zip->addFileFromPath('foo.txt', $file->url());
+ }
+
+ public function testDostime(): void
+ {
+ // Allows testing of protected method
+ $class = new ReflectionClass(File::class);
+ $method = $class->getMethod('dostime');
+ $method->setAccessible(true);
+
+ $this->assertSame($method->invoke(null, 1416246368), 1165069764);
+
+ // January 1 1980 - DOS Epoch.
+ $this->assertSame($method->invoke(null, 315532800), 2162688);
+
+ // January 1 1970 -> January 1 1980 due to minimum DOS Epoch. @todo Throw Exception?
+ $this->assertSame($method->invoke(null, 0), 2162688);
+ }
+
+ public function testAddFile(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testAddFileUtf8NameComment(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $name = 'árvíztűrő tükörfúrógép.txt';
+ $content = 'Sample String Data';
+ $comment =
+ 'Filename has every special characters ' .
+ 'from Hungarian language in lowercase. ' .
+ 'In uppercase: ÁÍŰŐÜÖÚÓÉ';
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setComment($comment);
+
+ $zip->addFile($name, $content, $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame([$name], $files);
+ $this->assertStringEqualsFile($tmpDir . '/' . $name, $content);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+ $this->assertSame($comment, $zipArch->getCommentName($name));
+ }
+
+ public function testAddFileUtf8NameNonUtfComment(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $name = 'á.txt';
+ $content = 'any';
+ $comment = mb_convert_encoding('á', 'ISO-8859-2', 'UTF-8');
+
+ // @see https://libzip.org/documentation/zip_file_get_comment.html
+ //
+ // mb_convert_encoding hasn't CP437.
+ // nearly CP850 (DOS-Latin-1)
+ $guessComment = mb_convert_encoding($comment, 'UTF-8', 'CP850');
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setComment($comment);
+
+ $zip->addFile($name, $content, $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+ $this->assertSame($guessComment, $zipArch->getCommentName($name));
+ $this->assertSame($comment, $zipArch->getCommentName($name, ZipArchive::FL_ENC_RAW));
+ }
+
+ public function testAddFileNonUtf8NameUtfComment(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $name = mb_convert_encoding('á.txt', 'ISO-8859-2', 'UTF-8');
+ $content = 'any';
+ $comment = 'á';
+
+ // @see https://libzip.org/documentation/zip_get_name.html
+ //
+ // mb_convert_encoding hasn't CP437.
+ // nearly CP850 (DOS-Latin-1)
+ $guessName = mb_convert_encoding($name, 'UTF-8', 'CP850');
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setComment($comment);
+
+ $zip->addFile($name, $content, $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertNotSame([$name], $files);
+ $this->assertSame([$guessName], $files);
+ $this->assertStringEqualsFile($tmpDir . '/' . $guessName, $content);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+ $this->assertSame($guessName, $zipArch->getNameIndex(0));
+ $this->assertSame($name, $zipArch->getNameIndex(0, ZipArchive::FL_ENC_RAW));
+ $this->assertSame($comment, $zipArch->getCommentName($guessName));
+ }
+
+ public function testAddFileWithStorageMethod(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $zip->addFile('sample.txt', 'Sample String Data', $fileOptions);
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+
+ $sample1 = $zipArch->statName('sample.txt');
+ $sample12 = $zipArch->statName('test/sample.txt');
+ $this->assertSame($sample1['comp_method'], Method::STORE);
+ $this->assertSame($sample12['comp_method'], Method::DEFLATE);
+
+ $zipArch->close();
+ }
+
+ public function testDecompressFileWithMacUnarchiver(): void
+ {
+ if (!file_exists(self::OSX_ARCHIVE_UTILITY)) {
+ $this->markTestSkipped('The Mac OSX Archive Utility is not available.');
+ }
+
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $folder = uniqid('', true);
+
+ $zip->addFile($folder . '/sample.txt', 'Sample Data');
+ $zip->finish();
+ fclose($stream);
+
+ exec(escapeshellarg(self::OSX_ARCHIVE_UTILITY) . ' ' . escapeshellarg($tmp), $output, $returnStatus);
+
+ $this->assertSame(0, $returnStatus);
+ $this->assertCount(0, $output);
+
+ $this->assertFileExists(dirname($tmp) . '/' . $folder . '/sample.txt');
+ $this->assertStringEqualsFile(dirname($tmp) . '/' . $folder . '/sample.txt', 'Sample Data');
+ }
+
+ public function testAddFileFromPath(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'Sample String Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('sample.txt', $tmpExample);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'More Simple Sample Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('test/sample.txt', $tmpExample);
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testAddFileFromPathWithStorageMethod(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'Sample String Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('sample.txt', $tmpExample, $fileOptions);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'More Simple Sample Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('test/sample.txt', $tmpExample);
+
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+
+ $sample1 = $zipArch->statName('sample.txt');
+ $this->assertSame(Method::STORE, $sample1['comp_method']);
+
+ $sample2 = $zipArch->statName('test/sample.txt');
+ $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
+
+ $zipArch->close();
+ }
+
+ public function testAddLargeFileFromPath(): void
+ {
+ $methods = [Method::DEFLATE(), Method::STORE()];
+ $falseTrue = [false, true];
+ foreach ($methods as $method) {
+ foreach ($falseTrue as $zeroHeader) {
+ foreach ($falseTrue as $zip64) {
+ if ($zeroHeader && $method->equals(Method::DEFLATE())) {
+ continue;
+ }
+ $this->addLargeFileFileFromPath($method, $zeroHeader, $zip64);
+ }
+ }
+ }
+ }
+
+ public function testAddFileFromStream(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ // In this test we can't use temporary stream to feed data
+ // because zlib.deflate filter gives empty string before PHP 7
+ // it works fine with file stream
+ $streamExample = fopen(__FILE__, 'rb');
+ $zip->addFileFromStream('sample.txt', $streamExample);
+// fclose($streamExample);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $streamExample2 = fopen('php://temp', 'wb+');
+ fwrite($streamExample2, 'More Simple Sample Data');
+ rewind($streamExample2); // move the pointer back to the beginning of file.
+ $zip->addFileFromStream('test/sample.txt', $streamExample2, $fileOptions);
+// fclose($streamExample2);
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile(__FILE__, file_get_contents($tmpDir . '/sample.txt'));
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testAddFileFromStreamWithStorageMethod(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $streamExample = fopen('php://temp', 'wb+');
+ fwrite($streamExample, 'Sample String Data');
+ rewind($streamExample); // move the pointer back to the beginning of file.
+ $zip->addFileFromStream('sample.txt', $streamExample, $fileOptions);
+// fclose($streamExample);
+
+ $streamExample2 = fopen('php://temp', 'bw+');
+ fwrite($streamExample2, 'More Simple Sample Data');
+ rewind($streamExample2); // move the pointer back to the beginning of file.
+ $zip->addFileFromStream('test/sample.txt', $streamExample2);
+// fclose($streamExample2);
+
+ $zip->finish();
+ fclose($stream);
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($tmp);
+
+ $sample1 = $zipArch->statName('sample.txt');
+ $this->assertSame(Method::STORE, $sample1['comp_method']);
+
+ $sample2 = $zipArch->statName('test/sample.txt');
+ $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
+
+ $zipArch->close();
+ }
+
+ public function testAddFileFromPsr7Stream(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $body = 'Sample String Data';
+ $response = new Response(200, [], $body);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testAddFileFromPsr7StreamWithOutputToPsr7Stream(): void
+ {
+ [$tmp, $resource] = $this->getTmpFileStream();
+ $psr7OutputStream = new Stream($resource);
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($psr7OutputStream);
+
+ $zip = new ZipStream(null, $options);
+
+ $body = 'Sample String Data';
+ $response = new Response(200, [], $body);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+
+ $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+ $zip->finish();
+ $psr7OutputStream->close();
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testAddFileFromPsr7StreamWithFileSizeSet(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+
+ $zip = new ZipStream(null, $options);
+
+ $body = 'Sample String Data';
+ $fileSize = strlen($body);
+ // Add fake padding
+ $fakePadding = "\0\0\0\0\0\0";
+ $response = new Response(200, [], $body . $fakePadding);
+
+ $fileOptions = new FileOptions();
+ $fileOptions->setMethod(Method::STORE());
+ $fileOptions->setSize($fileSize);
+ $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testCreateArchiveWithFlushOptionSet(): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+ $options->setFlushOutput(true);
+
+ $zip = new ZipStream(null, $options);
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
+
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+ $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
+ }
+
+ public function testCreateArchiveWithOutputBufferingOffAndFlushOptionSet(): void
+ {
+ // WORKAROUND (1/2): remove phpunit's output buffer in order to run test without any buffering
+ ob_end_flush();
+ $this->assertSame(0, ob_get_level());
+
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+ $options->setFlushOutput(true);
+
+ $zip = new ZipStream(null, $options);
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+ $this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
+
+ // WORKAROUND (2/2): add back output buffering so that PHPUnit doesn't complain that it is missing
+ ob_start();
+ }
+
+ /**
+ * @return array
+ */
+ protected function getTmpFileStream(): array
+ {
+ $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+ $stream = fopen($tmp, 'wb+');
+
+ return [$tmp, $stream];
+ }
+
+ /**
+ * @param string $tmp
+ * @return string
+ */
+ protected function validateAndExtractZip($tmp): string
+ {
+ $tmpDir = $this->getTmpDir();
+
+ $zipArch = new ZipArchive();
+ $res = $zipArch->open($tmp);
+
+ if ($res !== true) {
+ $this->fail("Failed to open {$tmp}. Code: $res");
+
+ return $tmpDir;
+ }
+
+ $this->assertSame(0, $zipArch->status);
+ $this->assertSame(0, $zipArch->statusSys);
+
+ $zipArch->extractTo($tmpDir);
+ $zipArch->close();
+
+ return $tmpDir;
+ }
+
+ protected function getTmpDir(): string
+ {
+ $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+ unlink($tmp);
+ mkdir($tmp) or $this->fail('Failed to make directory');
+
+ return $tmp;
+ }
+
+ /**
+ * @param string $path
+ * @return string[]
+ */
+ protected function getRecursiveFileList(string $path): array
+ {
+ $data = [];
+ $path = (string)realpath($path);
+ $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
+
+ $pathLen = strlen($path);
+ foreach ($files as $file) {
+ $filePath = $file->getRealPath();
+ if (!is_dir($filePath)) {
+ $data[] = substr($filePath, $pathLen + 1);
+ }
+ }
+
+ sort($data);
+
+ return $data;
+ }
+
+ protected function addLargeFileFileFromPath($method, $zeroHeader, $zip64): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $options = new ArchiveOptions();
+ $options->setOutputStream($stream);
+ $options->setLargeFileMethod($method);
+ $options->setLargeFileSize(5);
+ $options->setZeroHeader($zeroHeader);
+ $options->setEnableZip64($zip64);
+
+ $zip = new ZipStream(null, $options);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ for ($i = 0; $i <= 10000; $i++) {
+ fwrite($streamExample, sha1((string)$i));
+ if ($i % 100 === 0) {
+ fwrite($streamExample, "\n");
+ }
+ }
+ fclose($streamExample);
+ $shaExample = sha1_file($tmpExample);
+ $zip->addFileFromPath('sample.txt', $tmpExample);
+ unlink($tmpExample);
+
+ $zip->finish();
+ fclose($stream);
+
+ $tmpDir = $this->validateAndExtractZip($tmp);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt'], $files);
+
+ $this->assertSame(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$method}");
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/bootstrap.php b/vendor/maennchen/zipstream-php/test/bootstrap.php
new file mode 100644
index 0000000..13c7a0e
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/bootstrap.php
@@ -0,0 +1,7 @@
+<?php
+
+declare(strict_types=1);
+
+date_default_timezone_set('UTC');
+
+require __DIR__ . '/../vendor/autoload.php';
diff --git a/vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php b/vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php
new file mode 100644
index 0000000..05de4fe
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace BugHonorFileTimeTest;
+
+use DateTime;
+
+use function fopen;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Option\Archive;
+use ZipStream\Option\File;
+
+use ZipStream\ZipStream;
+
+/**
+ * Asserts that specified last-modified timestamps are not overwritten when a
+ * file is added
+ */
+class BugHonorFileTimeTest extends TestCase
+{
+ public function testHonorsFileTime(): void
+ {
+ $archiveOpt = new Archive();
+ $fileOpt = new File();
+ $expectedTime = new DateTime('2019-04-21T19:25:00-0800');
+
+ $archiveOpt->setOutputStream(fopen('php://memory', 'wb'));
+ $fileOpt->setTime(clone $expectedTime);
+
+ $zip = new ZipStream(null, $archiveOpt);
+
+ $zip->addFile('sample.txt', 'Sample', $fileOpt);
+
+ $zip->finish();
+
+ $this->assertEquals($expectedTime, $fileOpt->getTime());
+ }
+}