aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/maennchen/zipstream-php
diff options
context:
space:
mode:
authorMario <mario@mariovavti.com>2024-11-09 10:24:26 +0000
committerMario <mario@mariovavti.com>2024-11-09 10:24:26 +0000
commit0ed08274f16d65b427bd4a5bbd8bd5bd6b2a65c2 (patch)
tree25b05973f824b95fc5705cf8aa79b86a44cfde00 /vendor/maennchen/zipstream-php
parent2a152e0803309eb3646316bbe0d2a47353bad2b9 (diff)
parent0534fe68869aae231259ee48a38b4533f3f1ff99 (diff)
downloadvolse-hubzilla-0ed08274f16d65b427bd4a5bbd8bd5bd6b2a65c2.tar.gz
volse-hubzilla-0ed08274f16d65b427bd4a5bbd8bd5bd6b2a65c2.tar.bz2
volse-hubzilla-0ed08274f16d65b427bd4a5bbd8bd5bd6b2a65c2.zip
Merge branch 'clean-up-some-dependencies' into 'dev'
Clean up deps and upgrade EpubMeta See merge request hubzilla/core!2162
Diffstat (limited to 'vendor/maennchen/zipstream-php')
-rw-r--r--vendor/maennchen/zipstream-php/.editorconfig22
-rw-r--r--vendor/maennchen/zipstream-php/.gitattributes6
-rw-r--r--vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md132
-rw-r--r--vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md139
-rw-r--r--vendor/maennchen/zipstream-php/.github/FUNDING.yml1
-rw-r--r--vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml71
-rw-r--r--vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml11
-rw-r--r--vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md6
-rw-r--r--vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md13
-rw-r--r--vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md13
-rw-r--r--vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md9
-rw-r--r--vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md9
-rw-r--r--vendor/maennchen/zipstream-php/.github/SECURITY.md22
-rw-r--r--vendor/maennchen/zipstream-php/.github/dependabot.yml13
-rw-r--r--vendor/maennchen/zipstream-php/.github/scorecard.yml14
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml24
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml30
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml51
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/part_release.yml94
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/part_test.yml183
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/pr.yml50
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml78
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml29
-rw-r--r--vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml55
-rw-r--r--vendor/maennchen/zipstream-php/.gitignore12
-rw-r--r--vendor/maennchen/zipstream-php/.phive/phars.xml4
-rw-r--r--vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php70
-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.md154
-rw-r--r--vendor/maennchen/zipstream-php/composer.json88
-rw-r--r--vendor/maennchen/zipstream-php/guides/ContentLength.rst47
-rw-r--r--vendor/maennchen/zipstream-php/guides/FlySystem.rst34
-rw-r--r--vendor/maennchen/zipstream-php/guides/Nginx.rst16
-rw-r--r--vendor/maennchen/zipstream-php/guides/Options.rst66
-rw-r--r--vendor/maennchen/zipstream-php/guides/PSR7Streams.rst21
-rw-r--r--vendor/maennchen/zipstream-php/guides/StreamOutput.rst39
-rw-r--r--vendor/maennchen/zipstream-php/guides/Symfony.rst130
-rw-r--r--vendor/maennchen/zipstream-php/guides/Varnish.rst22
-rw-r--r--vendor/maennchen/zipstream-php/guides/index.rst126
-rw-r--r--vendor/maennchen/zipstream-php/phpdoc.dist.xml39
-rw-r--r--vendor/maennchen/zipstream-php/phpunit.xml.dist15
-rw-r--r--vendor/maennchen/zipstream-php/psalm.xml25
-rw-r--r--vendor/maennchen/zipstream-php/results.sarif1
-rw-r--r--vendor/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php52
-rw-r--r--vendor/maennchen/zipstream-php/src/CompressionMethod.php106
-rw-r--r--vendor/maennchen/zipstream-php/src/DataDescriptor.php26
-rw-r--r--vendor/maennchen/zipstream-php/src/EndOfCentralDirectory.php35
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception.php7
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php23
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php22
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php22
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php23
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/OverflowException.php21
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/ResourceActionException.php29
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php19
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php21
-rw-r--r--vendor/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php22
-rw-r--r--vendor/maennchen/zipstream-php/src/File.php420
-rw-r--r--vendor/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php89
-rw-r--r--vendor/maennchen/zipstream-php/src/LocalFileHeader.php40
-rw-r--r--vendor/maennchen/zipstream-php/src/OperationMode.php35
-rw-r--r--vendor/maennchen/zipstream-php/src/PackField.php56
-rw-r--r--vendor/maennchen/zipstream-php/src/Time.php39
-rw-r--r--vendor/maennchen/zipstream-php/src/Version.php12
-rw-r--r--vendor/maennchen/zipstream-php/src/Zip64/DataDescriptor.php28
-rw-r--r--vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php43
-rw-r--r--vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php29
-rw-r--r--vendor/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php45
-rw-r--r--vendor/maennchen/zipstream-php/src/ZipStream.php865
-rw-r--r--vendor/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php23
-rw-r--r--vendor/maennchen/zipstream-php/test/Assertions.php49
-rw-r--r--vendor/maennchen/zipstream-php/test/CentralDirectoryFileHeaderTest.php60
-rw-r--r--vendor/maennchen/zipstream-php/test/DataDescriptorTest.php26
-rw-r--r--vendor/maennchen/zipstream-php/test/EndOfCentralDirectoryTest.php35
-rw-r--r--vendor/maennchen/zipstream-php/test/EndlessCycleStream.php104
-rw-r--r--vendor/maennchen/zipstream-php/test/FaultInjectionResource.php141
-rw-r--r--vendor/maennchen/zipstream-php/test/LocalFileHeaderTest.php47
-rw-r--r--vendor/maennchen/zipstream-php/test/PackFieldTest.php42
-rw-r--r--vendor/maennchen/zipstream-php/test/ResourceStream.php159
-rw-r--r--vendor/maennchen/zipstream-php/test/Tempfile.php42
-rw-r--r--vendor/maennchen/zipstream-php/test/TimeTest.php44
-rw-r--r--vendor/maennchen/zipstream-php/test/Util.php127
-rw-r--r--vendor/maennchen/zipstream-php/test/Zip64/DataDescriptorTest.php28
-rw-r--r--vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.php28
-rw-r--r--vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryTest.php41
-rw-r--r--vendor/maennchen/zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.php42
-rw-r--r--vendor/maennchen/zipstream-php/test/ZipStreamTest.php1195
-rw-r--r--vendor/maennchen/zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.php22
-rw-r--r--vendor/maennchen/zipstream-php/test/bootstrap.php7
91 files changed, 6415 insertions, 0 deletions
diff --git a/vendor/maennchen/zipstream-php/.editorconfig b/vendor/maennchen/zipstream-php/.editorconfig
new file mode 100644
index 000000000..f7cd91427
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.editorconfig
@@ -0,0 +1,22 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+
+[*.{yml,md,xml}]
+indent_style = space
+indent_size = 2
+
+[*.{rst,php}]
+indent_style = space
+indent_size = 4
+
+[composer.json]
+indent_style = space
+indent_size = 2
+
+[composer.lock]
+indent_style = space
+indent_size = 4
diff --git a/vendor/maennchen/zipstream-php/.gitattributes b/vendor/maennchen/zipstream-php/.gitattributes
new file mode 100644
index 000000000..e058ebd0a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.gitattributes
@@ -0,0 +1,6 @@
+.gitignore text eol=lf
+.gitattributes text eol=lf
+*.md text eol=lf
+*.php text eol=lf
+*.yml text eol=lf
+*.xml text eol=lf
diff --git a/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md b/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..9d75b8763
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,132 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the overall
+ community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address,
+ without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+jonatan@maennchen.ch.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][mozilla coc].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][faq]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[mozilla coc]: https://github.com/mozilla/diversity
+[faq]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md b/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md
new file mode 100644
index 000000000..d8caee081
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/CONTRIBUTING.md
@@ -0,0 +1,139 @@
+# Contributing to ZipStream-PHP
+
+## Welcome!
+
+We look forward to your contributions! Here are some examples how you can
+contribute:
+
+- [Report a bug](https://github.com/maennchen/ZipStream-PHP/issues/new?labels=bug&template=BUG.md)
+- [Propose a new feature](https://github.com/maennchen/ZipStream-PHP/issues/new?labels=enhancement&template=FEATURE.md)
+- [Send a pull request](https://github.com/maennchen/ZipStream-PHP/pulls)
+
+## We have a Code of Conduct
+
+Please note that this project is released with a
+[Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this
+project you agree to abide by its terms.
+
+## Any contributions you make will be under the MIT License
+
+When you submit code changes, your submissions are understood to be under the
+same [MIT License](https://github.com/maennchen/ZipStream-PHP/blob/main/LICENSE)
+that covers the project. By contributing to this project, you agree that your
+contributions will be licensed under its MIT License.
+
+## Write bug reports with detail, background, and sample code
+
+In your bug report, please provide the following:
+
+- A quick summary and/or background
+- Steps to reproduce
+ - Be specific!
+ - Give sample code if you can.
+- What you expected would happen
+- What actually happens
+- Notes (possibly including why you think this might be happening, or stuff you
+- tried that didn't work)
+
+Please do not report a bug for a version of ZIPStream-PHP that is no longer
+supported (`< 3.0.0`). Please do not report a bug if you are using a version of
+PHP that is not supported by the version of ZipStream-PHP you are using.
+
+Please post code and output as text
+([using proper markup](https://guides.github.com/features/mastering-markdown/)).
+Do not post screenshots of code or output.
+
+Please include the output of `composer info | sort`.
+
+## Workflow for Pull Requests
+
+1. Fork the repository.
+2. Create your branch from `main` if you plan to implement new functionality or
+ change existing code significantly; create your branch from the oldest branch
+ that is affected by the bug if you plan to fix a bug.
+3. Implement your change and add tests for it.
+4. Ensure the test suite passes.
+5. Ensure the code complies with our coding guidelines (see below).
+6. Send that pull request!
+
+Please make sure you have
+[set up your user name and email address](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup)
+for use with Git. Strings such as `silly nick name <root@localhost>` look really
+stupid in the commit history of a project.
+
+We encourage you to
+[sign your Git commits with your GPG key](https://docs.github.com/en/github/authenticating-to-github/signing-commits).
+
+Pull requests for new features must be based on the `main` branch.
+
+We are trying to keep backwards compatibility breaks in ZipStream-PHP to a
+minimum. Please take this into account when proposing changes.
+
+Due to time constraints, we are not always able to respond as quickly as we
+would like. Please do not take delays personal and feel free to remind us if you
+feel that we forgot to respond.
+
+## Coding Guidelines
+
+This project comes with a configuration file (located at `/psalm.yml` in the
+repository) that you can use to perform static analysis (with a focus on type
+checking):
+
+```bash
+$ .composer run test:lint
+```
+
+This project comes with a configuration file (located at
+`/.php-cs-fixer.dist.php` in the repository) that you can use to (re)format your
+source code for compliance with this project's coding guidelines:
+
+```bash
+$ composer run format
+```
+
+Please understand that we will not accept a pull request when its changes
+violate this project's coding guidelines.
+
+## Using ZipStream-PHP from a Git checkout
+
+The following commands can be used to perform the initial checkout of
+ZipStream-PHP:
+
+```bash
+$ git clone git@github.com:maennchen/ZipStream-PHP.git
+
+$ cd ZipStream-PHP
+```
+
+Install ZipStream-PHP's dependencies using [Composer](https://getcomposer.org/):
+
+```bash
+$ composer install
+$ composer run install:tools # Install phpDocumentor using phive
+```
+
+## Running ZipStream-PHP's test suite
+
+After following the steps shown above, ZipStream-PHP's test suite is run like
+this:
+
+```bash
+$ composer run test:unit
+```
+
+There's some slow tests in the test suite that test the handling of big files in
+the archives. To skip them use the following command instead:
+
+```bash
+$ composer run test:unit:fast
+```
+
+## Generating ZipStream-PHP Documentation
+
+To generate the documentation for the library, run:
+
+```bash
+$ composer run docs:generate
+```
+
+The guide documentation pages can be found in the `/guides/` directory.
diff --git a/vendor/maennchen/zipstream-php/.github/FUNDING.yml b/vendor/maennchen/zipstream-php/.github/FUNDING.yml
new file mode 100644
index 000000000..5a4612769
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: maennchen
diff --git a/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml b/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml
new file mode 100644
index 000000000..0eb8cc772
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/BUG.yml
@@ -0,0 +1,71 @@
+name: 🐞 Bug Report
+description: Something is broken?
+labels: ["bug"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ - Create a discussion instead if you are looking for support:
+ https://github.com/maennchen/ZipStream-PHP/discussions
+ - type: input
+ id: version
+ attributes:
+ label: ZipStream-PHP version
+ placeholder: x.y.z
+ validations:
+ required: true
+ - type: input
+ id: php-version
+ attributes:
+ label: PHP version
+ placeholder: x.y.z
+ validations:
+ required: true
+ - type: checkboxes
+ id: constraints
+ attributes:
+ label: Constraints for Bug Report
+ options:
+ - label: |
+ I'm using a version of ZipStream that is currently supported:
+ https://github.com/maennchen/ZipStream-PHP#version-support
+ required: true
+ - label: |
+ I'm using a version of PHP that has active support:
+ https://www.php.net/supported-versions.php
+ required: true
+ - label: |
+ I'm using a version of PHP that is compatible with your used
+ ZipStream version.
+ required: true
+ - label: |
+ I'm using the latest release of the used ZipStream major version.
+ required: true
+ - type: textarea
+ id: summary
+ attributes:
+ label: Summary
+ description: Provide a summary describing the problem you are experiencing.
+ validations:
+ required: true
+ - type: textarea
+ id: current-behaviour
+ attributes:
+ label: Current behavior
+ description: What is the current (buggy) behavior?
+ validations:
+ required: true
+ - type: textarea
+ id: reproduction
+ attributes:
+ label: How to reproduce
+ description: Provide steps to reproduce the bug.
+ validations:
+ required: true
+ - type: textarea
+ id: expected-behaviour
+ attributes:
+ label: Expected behavior
+ description: What was the expected (correct) behavior?
+ validations:
+ required: true
diff --git a/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml b/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml
new file mode 100644
index 000000000..e5dec6371
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/ISSUE_TEMPLATE/FEATURE.yml
@@ -0,0 +1,11 @@
+name: 🎉 Feature Request
+description: You have a neat idea that should be implemented?
+labels: ["enhancement"]
+body:
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: Provide a summary of the feature you would like to see implemented.
+ validations:
+ required: true
diff --git a/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..6892c571b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,6 @@
+Please go the the `Preview` tab and select the appropriate sub-template:
+
+* [🐞 Failing Test](?expand=1&template=FAILING_TEST.md)
+* [🐞 Bug Fix](?expand=1&template=FIX.md)
+* [⚙ Improvement](?expand=1&template=IMPROVEMENT.md)
+* [🎉 New Feature](?expand=1&template=NEW_FEATURE.md)
diff --git a/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md
new file mode 100644
index 000000000..24603cb63
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.md
@@ -0,0 +1,13 @@
+<!---
+name: 🐞 Failing Test
+about: You found a bug and have a failing test?
+labels: bug, tests
+--->
+
+<!--
+- Please do not send a pull request for an issue in a version of ZipStream-PHP
+ that is no longer supported.
+ See: https://github.com/maennchen/ZipStream-PHP#version-support
+- Please target the oldest branch of ZipStream-PHP that is still supported and
+ where the test fails.
+-->
diff --git a/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md
new file mode 100644
index 000000000..77f65a080
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.md
@@ -0,0 +1,13 @@
+<!---
+name: 🐞 Bug Fix
+about: You have a fix for a bug?
+labels: bug
+--->
+
+<!--
+- Please do not send a pull request for an issue in a version of ZipStream-PHP
+ that is no longer supported.
+ See: https://github.com/maennchen/ZipStream-PHP#version-support
+- Please target the oldest branch of ZipStream-PHP that is still supported and
+ affected by this bug.
+-->
diff --git a/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md
new file mode 100644
index 000000000..3ac8e3100
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.md
@@ -0,0 +1,9 @@
+<!---
+name: ⚙ Improvement
+about: You have some improvement to make ZipStream-PHP better?
+labels: enhancement
+--->
+
+<!--
+- Please target the `main` branch of ZipStream-PHP.
+-->
diff --git a/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md
new file mode 100644
index 000000000..ca53939c8
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.md
@@ -0,0 +1,9 @@
+<!---
+name: 🎉 New Feature
+about: You have implemented some neat idea that you want to make part of ZipStream-PHP?
+labels: type/enhancement
+--->
+
+<!--
+- Please target the `main` branch of ZipStream-PHP.
+-->
diff --git a/vendor/maennchen/zipstream-php/.github/SECURITY.md b/vendor/maennchen/zipstream-php/.github/SECURITY.md
new file mode 100644
index 000000000..3046c3107
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/SECURITY.md
@@ -0,0 +1,22 @@
+# Security Policy
+
+[![OpenSSF Vulnerability Disclosure](https://img.shields.io/badge/OpenSSF-Vulnerability_Disclosure-green)](https://github.com/ossf/oss-vulnerability-guide/blob/main/finder-guide.md)
+[![GitHub Report](https://img.shields.io/badge/GitHub-Security_Advisories-blue)](https://github.com/maennchen/ZipStream-PHP/security/advisories/new)
+[![Email Report](https://img.shields.io/badge/Email-jonatan%40maennchen.ch-blue)](mailto:jonatan@maennchen.ch)
+
+This repository follows the
+[OpenSSF Vulnerability Disclosure guide](https://github.com/ossf/oss-vulnerability-guide/tree/main).
+You can learn more about it in the
+[Finders Guide](https://github.com/ossf/oss-vulnerability-guide/blob/main/finder-guide.md).
+
+Please report vulnerabilities via the
+[GitHub Security Vulnerability Reporting](https://github.com/maennchen/ZipStream-PHP/security/advisories/new)
+or via email to [`jonatan@maennchen.ch`](mailto:jonatan@maennchen.ch) if this does
+not work for you.
+
+Our vulnerability management team will respond within 3 working days of your
+report. If the issue is confirmed as a vulnerability, we will open a Security
+Advisory. This project follows a 90 day disclosure timeline.
+
+If you have questions about reporting security issues, email the vulnerability
+management team: [`jonatan@maennchen.ch`](mailto:jonatan@maennchen.ch)
diff --git a/vendor/maennchen/zipstream-php/.github/dependabot.yml b/vendor/maennchen/zipstream-php/.github/dependabot.yml
new file mode 100644
index 000000000..6056437b9
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/dependabot.yml
@@ -0,0 +1,13 @@
+version: 2
+updates:
+ - package-ecosystem: "composer"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ groups:
+ github-actions:
+ applies-to: version-updates \ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/.github/scorecard.yml b/vendor/maennchen/zipstream-php/.github/scorecard.yml
new file mode 100644
index 000000000..219fc0bfd
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/scorecard.yml
@@ -0,0 +1,14 @@
+annotations:
+ - checks:
+ - fuzzing
+ reasons:
+ - reason: not-applicable # PHP is memory safe
+ - checks:
+ - packaging
+ reasons:
+ - reason: not-supported # Using Composer
+ - checks:
+ - signed-releases
+ reasons:
+ - reason: not-applicable # Releases are distributed via Composer
+
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml b/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml
new file mode 100644
index 000000000..15ff2782c
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/branch_main.yml
@@ -0,0 +1,24 @@
+on:
+ push:
+ branches:
+ - "main"
+
+name: "Main Branch"
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ name: "Test"
+
+ permissions:
+ contents: read
+ security-events: write
+
+ uses: ./.github/workflows/part_test.yml
+
+ docs:
+ name: "Docs"
+
+ uses: ./.github/workflows/part_docs.yml
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml b/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml
new file mode 100644
index 000000000..77e466b81
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/part_dependabot.yml
@@ -0,0 +1,30 @@
+on:
+ workflow_call: {}
+
+name: "Dependabot"
+
+permissions:
+ contents: read
+
+jobs:
+ automerge_dependabot:
+ name: "Automerge PRs"
+
+ runs-on: ubuntu-latest
+
+ permissions:
+ pull-requests: write
+ contents: write
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - uses: fastify/github-action-merge-dependabot@3892334d1c649bb8119af3d22a3f3766bd5e593f # v3.10.2
+ with:
+ github-token: ${{ github.token }}
+ use-github-auto-merge: true
+ # Major Updates need to be merged manually
+ target: minor
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml b/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml
new file mode 100644
index 000000000..7af16f3be
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/part_docs.yml
@@ -0,0 +1,51 @@
+on:
+ workflow_call: {}
+
+name: "Documentation"
+
+permissions:
+ contents: read
+
+jobs:
+ generate:
+ name: "Generate"
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: Checkout Code
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - name: SetUp PHP
+ id: setup-php
+ uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2
+ with:
+ php-version: "8.3"
+ tools: phive
+ - name: Cache Tools
+ uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
+ id: cache
+ with:
+ path: ~/.phive
+ key: tools-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-${{ hashFiles('**/phars.xml') }}
+ restore-keys: |
+ tools-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-
+ tools-${{ steps.setup-php.outputs.php-version }}-
+ tools-
+ - name: Install Tools
+ run: composer run install:tools
+ - name: Generate Docs
+ run: composer run docs:generate
+ - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
+ with:
+ name: docs
+ path: docs
+ - name: Package for GitHub Pages
+ uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
+ with:
+ path: docs
+
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml b/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml
new file mode 100644
index 000000000..c0f3867df
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/part_release.yml
@@ -0,0 +1,94 @@
+on:
+ workflow_call:
+ inputs:
+ releaseName:
+ required: true
+ type: string
+ stable:
+ required: false
+ type: boolean
+ default: false
+
+name: "Release"
+
+permissions:
+ contents: read
+
+jobs:
+ create:
+ name: Create Release
+
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: Create prerelease
+ if: ${{ !inputs.stable }}
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ gh release create \
+ --repo ${{ github.repository }} \
+ --title ${{ inputs.releaseName }} \
+ --prerelease \
+ --generate-notes \
+ ${{ inputs.releaseName }}
+
+ - name: Create release
+ if: ${{ inputs.stable }}
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ gh release create \
+ --repo ${{ github.repository }} \
+ --title ${{ inputs.releaseName }} \
+ --generate-notes \
+ ${{ inputs.releaseName }}
+
+ upload_release:
+ name: "Upload"
+
+ needs: ["create"]
+
+ runs-on: ubuntu-latest
+
+ permissions:
+ id-token: write
+ contents: write
+ attestations: write
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+ with:
+ name: docs
+ path: docs
+ - run: |
+ tar -czvf docs.tar.gz docs
+ - name: "Attest Documentation"
+ id: attestation
+ uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
+ with:
+ subject-path: "docs.tar.gz"
+ - name: Copy Attestation
+ run: cp "$ATTESTATION" docs.tar.gz.sigstore
+ env:
+ ATTESTATION: "${{ steps.attestation.outputs.bundle-path }}"
+ - name: Upload
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ gh release upload --clobber "${{ github.ref_name }}" \
+ docs.tar.gz docs.tar.gz.sigstore
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml b/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml
new file mode 100644
index 000000000..ccf4d660e
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/part_test.yml
@@ -0,0 +1,183 @@
+on:
+ workflow_call:
+
+name: "Test"
+
+permissions:
+ contents: read
+
+jobs:
+ phpunit:
+ name: PHPUnit (PHP ${{ matrix.php }} on ${{ matrix.os }})
+
+ runs-on: ${{ matrix.os }}
+
+ continue-on-error: ${{ matrix.experimental }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php: ["8.1", "8.2", "8.3"]
+ os: [ubuntu-latest]
+ experimental: [false]
+ include:
+ - php: nightly
+ os: ubuntu-latest
+ experimental: true
+ - php: "8.3"
+ os: windows-latest
+ experimental: false
+ - php: "8.3"
+ os: macos-latest
+ experimental: false
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: Checkout Code
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - name: SetUp PHP
+ id: setup-php
+ uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2
+ with:
+ php-version: "${{ matrix.php }}"
+ tools: phpunit
+ coverage: xdebug
+ extensions: xdebug,zip
+ - name: Get composer cache directory
+ id: composer-cache-common
+ if: "${{ runner.os != 'Windows' }}"
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+ - name: Get composer cache directory
+ id: composer-cache-windows
+ if: "${{ runner.os == 'Windows' }}"
+ run: echo "dir=$(composer config cache-files-dir)" >> $env:GITHUB_OUTPUT
+ - name: Cache Deps
+ uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
+ id: cache
+ with:
+ path: ${{ steps.composer-cache-common.outputs.dir }}${{ steps.composer-cache-windows.outputs.dir }}
+ key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-
+ deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-
+ deps-${{ steps.setup-php.outputs.php-version }}-
+ deps-
+ - name: Install Deps
+ if: matrix.php != 'nightly'
+ run: composer install --prefer-dist
+ - name: Install Deps (ignore PHP requirement)
+ if: matrix.php == 'nightly'
+ run: composer install --prefer-dist --ignore-platform-req=php+
+ - name: Run PHPUnit
+ run: composer run test:unit
+ env:
+ XDEBUG_MODE: coverage
+ - name: Upload coverage results to Coveralls
+ env:
+ COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ COVERALLS_PARALLEL: true
+ COVERALLS_FLAG_NAME: ${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}
+ run: composer run coverage:report
+ continue-on-error: ${{ matrix.experimental }}
+
+ mark_coverage_done:
+ needs: ["phpunit"]
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: Coveralls Finished
+ uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 # v2.3.0
+ with:
+ github-token: ${{ secrets.github_token }}
+ parallel-finished: true
+
+ psalm:
+ name: Run Psalm
+
+ runs-on: "ubuntu-latest"
+
+ permissions:
+ security-events: write
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: Checkout Code
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - name: SetUp PHP
+ id: setup-php
+ uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2
+ with:
+ php-version: "8.3"
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+ - name: Cache Deps
+ uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
+ id: cache
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-
+ deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-
+ deps-${{ steps.setup-php.outputs.php-version }}-
+ deps-
+ - name: Install Deps
+ run: composer install --prefer-dist
+ - name: Run Psalm
+ run: composer run test:lint -- --report=results.sarif
+ - name: "Upload SARIF"
+ uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3
+ with:
+ sarif_file: results.sarif
+
+ php-cs:
+ name: Run PHP-CS
+
+ runs-on: "ubuntu-latest"
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: Checkout Code
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - name: SetUp PHP
+ id: setup-php
+ uses: shivammathur/setup-php@c541c155eee45413f5b09a52248675b1a2575231 # v2
+ with:
+ php-version: "8.3"
+ - name: Get composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+ - name: Cache Deps
+ uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
+ id: cache
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-
+ deps-${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-
+ deps-${{ steps.setup-php.outputs.php-version }}-
+ deps-
+ - name: Install Deps
+ run: composer install --prefer-dist
+ - name: Run PHP-CS
+ run: composer run test:formatted
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/pr.yml b/vendor/maennchen/zipstream-php/.github/workflows/pr.yml
new file mode 100644
index 000000000..05259d4f7
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/pr.yml
@@ -0,0 +1,50 @@
+on:
+ pull_request:
+ branches:
+ - "*"
+ workflow_dispatch: {}
+
+name: "Pull Request"
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ name: "Test"
+
+ permissions:
+ contents: read
+ security-events: write
+
+ uses: ./.github/workflows/part_test.yml
+
+ docs:
+ name: "Docs"
+
+ uses: ./.github/workflows/part_docs.yml
+
+ dependabot:
+ name: "Dependabot"
+
+ if: ${{ github.actor == 'dependabot[bot]'}}
+
+ permissions:
+ pull-requests: write
+ contents: write
+
+ uses: ./.github/workflows/part_dependabot.yml
+
+ dependency-review:
+ name: Dependency Review
+ runs-on: ubuntu-latest
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: 'Checkout Repository'
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ - name: 'Dependency Review'
+ uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml b/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml
new file mode 100644
index 000000000..7bb8dbb6c
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/scorecard.yml
@@ -0,0 +1,78 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecard supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '28 11 * * 3'
+ push:
+ branches: [ "main" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecard analysis
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+ # Uncomment the permissions below if installing in a private repository.
+ # contents: read
+ # actions: read
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: "Checkout code"
+ uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecard on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
+ # repo_token: ${{ secrets.SCORECARD_TOKEN }}
+
+ # Public repositories:
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories:
+ # - `publish_results` will always be set to `false`, regardless
+ # of the value entered here.
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard (optional).
+ # Commenting out will disable upload of results to your repo's Code Scanning dashboard
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12
+ with:
+ sarif_file: results.sarif
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml b/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml
new file mode 100644
index 000000000..b3399454a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/tag-beta.yml
@@ -0,0 +1,29 @@
+on:
+ push:
+ tags:
+ - "[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+"
+
+name: "Beta Tag"
+
+permissions:
+ contents: read
+
+jobs:
+ docs:
+ name: "Docs"
+
+ uses: ./.github/workflows/part_docs.yml
+
+ release:
+ name: "Release"
+
+ needs: ["docs"]
+
+ permissions:
+ id-token: write
+ contents: write
+ attestations: write
+
+ uses: ./.github/workflows/part_release.yml
+ with:
+ releaseName: "${{ github.ref_name }}"
diff --git a/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml b/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml
new file mode 100644
index 000000000..0e91cf0d0
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.github/workflows/tag-stable.yml
@@ -0,0 +1,55 @@
+on:
+ push:
+ tags:
+ - "[0-9]+.[0-9]+.[0-9]+"
+
+name: "Stable Tag"
+
+permissions:
+ contents: read
+
+jobs:
+ docs:
+ name: "Docs"
+
+ uses: ./.github/workflows/part_docs.yml
+
+ release:
+ name: "Release"
+
+ needs: ["docs"]
+
+ permissions:
+ id-token: write
+ contents: write
+ attestations: write
+
+ uses: ./.github/workflows/part_release.yml
+ with:
+ releaseName: "${{ github.ref_name }}"
+ stable: true
+
+ deploy_pages:
+ name: "Deploy to GitHub Pages"
+
+ needs: ["release", "docs"]
+
+ runs-on: ubuntu-latest
+
+ permissions:
+ pages: write
+ id-token: write
+
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
+ with:
+ egress-policy: audit
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
diff --git a/vendor/maennchen/zipstream-php/.gitignore b/vendor/maennchen/zipstream-php/.gitignore
new file mode 100644
index 000000000..e52a49877
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.gitignore
@@ -0,0 +1,12 @@
+/composer.lock
+/cov
+/coverage.clover.xml
+/docs
+.idea
+/.php-cs-fixer.cache
+/.phpdoc/cache
+/.phpunit.result.cache
+/phpunit.xml
+/.phpunit.cache
+/tools
+/vendor
diff --git a/vendor/maennchen/zipstream-php/.phive/phars.xml b/vendor/maennchen/zipstream-php/.phive/phars.xml
new file mode 100644
index 000000000..183927b12
--- /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.4.3" 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 000000000..38d6a7658
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
@@ -0,0 +1,70 @@
+<?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,
+ '@PHP82Migration' => 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_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);
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 000000000..b7507fb9c
--- /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 000000000..4a3dc9dfd
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.tool-versions
@@ -0,0 +1 @@
+php 8.3.1
diff --git a/vendor/maennchen/zipstream-php/LICENSE b/vendor/maennchen/zipstream-php/LICENSE
new file mode 100644
index 000000000..ebe7fe2f8
--- /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 000000000..858add09f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/README.md
@@ -0,0 +1,154 @@
+# 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)
+[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9524/badge)](https://www.bestpractices.dev/projects/9524)
+[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/maennchen/ZipStream-PHP/badge)](https://scorecard.dev/viewer/?uri=github.com/maennchen/ZipStream-PHP)
+
+## 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.github.io/ZipStream-PHP/).
+
+```php
+// Autoload the dependencies
+require 'vendor/autoload.php';
+
+// create a new zipstream object
+$zip = new ZipStream\ZipStream(
+ outputName: 'example.zip',
+
+ // enable output of HTTP headers
+ sendHttpHeaders: true,
+);
+
+// create a file named 'hello.txt'
+$zip->addFile(
+ fileName: 'hello.txt',
+ data: 'This is the contents of hello.txt',
+);
+
+// add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
+$zip->addFileFromPath(
+ fileName: 'some_image.jpg',
+ path: 'path/to/image.jpg',
+);
+
+// finish the zip stream
+$zip->finish();
+```
+
+## Upgrade to version 3.0.0
+
+### General
+
+- Minimum PHP Version: `8.1`
+- Only 64bit Architecture is supported.
+- The class `ZipStream\Option\Method` has been replaced with the enum
+ `ZipStream\CompressionMethod`.
+- Most clases have been flagged as `@internal` and should not be used from the
+ outside.
+ If you're using internal resources to extend this library, please open an
+ issue so that a clean interface can be added & published.
+ The externally available classes & enums are:
+ - `ZipStream\CompressionMethod`
+ - `ZipStream\Exception*`
+ - `ZipStream\ZipStream`
+
+### Archive Options
+
+- The class `ZipStream\Option\Archive` has been replaced in favor of named
+ arguments in the `ZipStream\ZipStream` constuctor.
+- The archive options `largeFileSize` & `largeFileMethod` has been removed. If
+ you want different `compressionMethods` based on the file size, you'll have to
+ implement this yourself.
+- The archive option `httpHeaderCallback` changed the type from `callable` to
+ `Closure`.
+- The archive option `zeroHeader` has been replaced with the option
+ `defaultEnableZeroHeader` and can be overridden for every file. Its default
+ value changed from `false` to `true`.
+- The archive option `statFiles` was removed since the library no longer checks
+ filesizes this way.
+- The archive option `deflateLevel` has been replaced with the option
+ `defaultDeflateLevel` and can be overridden for every file.
+- The first argument (`name`) of the `ZipStream\ZipStream` constuctor has been
+ replaced with the named argument `outputName`.
+- Headers are now also sent if the `outputName` is empty. If you do not want to
+ automatically send http headers, set `sendHttpHeaders` to `false`.
+
+### File Options
+
+- The class `ZipStream\Option\File` has been replaced in favor of named
+ arguments in the `ZipStream\ZipStream->addFile*` functions.
+- The file option `method` has been renamed to `compressionMethod`.
+- The file option `time` has been renamed to `lastModificationDateTime`.
+- The file option `size` has been renamed to `maxSize`.
+
+## Upgrade to version 2.0.0
+
+https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-200
+
+## Upgrade to version 1.0.0
+
+https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-100
+
+## Contributing
+
+ZipStream-PHP is a collaborative project. Please take a look at the
+[.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) file.
+
+## Version Support
+
+Versions are supported according to the table below.
+
+Please do not open any pull requests contradicting the current version support
+status.
+
+Careful: Always check the `README` on `main` for up-to-date information.
+
+| Version | New Features | Bugfixes | Security |
+|---------|--------------|----------|----------|
+| *3* | ✓ | ✓ | ✓ |
+| *2* | ✗ | ✗ | ✓ |
+| *1* | ✗ | ✗ | ✗ |
+| *0* | ✗ | ✗ | ✗ |
+
+This library aligns itself with the PHP core support. New features and bugfixes
+will only target PHP versions according to their current status.
+
+See: https://www.php.net/supported-versions.php
+
+## 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
diff --git a/vendor/maennchen/zipstream-php/composer.json b/vendor/maennchen/zipstream-php/composer.json
new file mode 100644
index 000000000..de5e62413
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/composer.json
@@ -0,0 +1,88 @@
+{
+ "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-64bit": "^8.1",
+ "ext-mbstring": "*",
+ "ext-zlib": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0",
+ "guzzlehttp/guzzle": "^7.5",
+ "ext-zip": "*",
+ "mikey179/vfsstream": "^1.6",
+ "php-coveralls/php-coveralls": "^2.5",
+ "friendsofphp/php-cs-fixer": "^3.16",
+ "vimeo/psalm": "^5.0"
+ },
+ "suggest": {
+ "psr/http-message": "^2.0",
+ "guzzlehttp/psr7": "^2.4"
+ },
+ "scripts": {
+ "format": "php-cs-fixer fix",
+ "test": [
+ "@test:unit",
+ "@test:formatted",
+ "@test:lint"
+ ],
+ "test:unit": "phpunit --coverage-clover=coverage.clover.xml --coverage-html cov",
+ "test:unit:slow": "@test:unit --group slow",
+ "test:unit:fast": "@test:unit --exclude-group slow",
+ "test:formatted": "@format --dry-run --stop-on-violation --using-cache=no",
+ "test:lint": "psalm --stats --show-info=true --find-unused-psalm-suppress",
+ "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json --insecure",
+ "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656 --trust-gpg-keys 0x8AC0BAA79732DD42",
+ "docs:generate": "tools/phpdocumentor --sourcecode"
+ },
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": { "ZipStream\\Test\\": "test/" }
+ },
+ "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 000000000..21fea34d7
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/ContentLength.rst
@@ -0,0 +1,47 @@
+Adding Content-Length header
+=============
+
+Adding a ``Content-Length`` header for ``ZipStream`` can be achieved by
+using the options ``SIMULATION_STRICT`` or ``SIMULATION_LAX`` in the
+``operationMode`` parameter.
+
+In the ``SIMULATION_STRICT`` mode, ``ZipStream`` will not allow to calculate the
+size based on reading the whole file. ``SIMULATION_LAX`` will read the whole
+file if neccessary.
+
+``SIMULATION_STRICT`` is therefore useful to make sure that the size can be
+calculated efficiently.
+
+.. code-block:: php
+ use ZipStream\OperationMode;
+ use ZipStream\ZipStream;
+
+ $zip = new ZipStream(
+ operationMode: OperationMode::SIMULATE_STRICT, // or SIMULATE_LAX
+ defaultEnableZeroHeader: false,
+ sendHttpHeaders: true,
+ outputStream: $stream,
+ );
+
+ // Normally add files
+ $zip->addFile('sample.txt', 'Sample String Data');
+
+ // Use addFileFromCallback and exactSize if you want to defer opening of
+ // the file resource
+ $zip->addFileFromCallback(
+ 'sample.txt',
+ exactSize: 18,
+ callback: function () {
+ return fopen('...');
+ }
+ );
+
+ // Read resulting file size
+ $size = $zip->finish();
+
+ // Tell it to the browser
+ header('Content-Length: '. $size);
+
+ // Execute the Simulation and stream the actual zip to the client
+ $zip->executeSimulation();
+
diff --git a/vendor/maennchen/zipstream-php/guides/FlySystem.rst b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
new file mode 100644
index 000000000..4e6c6fb82
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
@@ -0,0 +1,34 @@
+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+');
+
+ // Create Zip Archive
+ $zipStream = new ZipStream(
+ outputStream: $tempStream,
+ outputName: 'test.zip',
+ );
+ $zipStream->addFile('test.txt', 'text');
+ $zipStream->finish();
+
+ // Store File
+ // (see Flysystem documentation, and all its framework integration)
+ // Can be any adapter (AWS, Google, Ftp, etc.)
+ $adapter = new Local(__DIR__.'/path/to/folder');
+ $filesystem = new Filesystem($adapter);
+
+ $filesystem->writeStream('test.zip', $tempStream)
+
+ // Close Stream
+ fclose($tempStream);
diff --git a/vendor/maennchen/zipstream-php/guides/Nginx.rst b/vendor/maennchen/zipstream-php/guides/Nginx.rst
new file mode 100644
index 000000000..c53d3000e
--- /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 000000000..5e92e94d6
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Options.rst
@@ -0,0 +1,66 @@
+Available options
+===============
+
+Here is the full list of options available to you. You can also have a look at
+``src/ZipStream.php`` file.
+
+.. code-block:: php
+
+ use ZipStream\ZipStream;
+
+ require_once 'vendor/autoload.php';
+
+ $zip = new ZipStream(
+ // Define output stream
+ // (argument is eiter a resource or implementing
+ // `Psr\Http\Message\StreamInterface`)
+ //
+ // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies
+ // required when using `Psr\Http\Message\StreamInterface`.
+ outputStream: $filePointer,
+
+ // Set the deflate level (default is 6; use -1 to disable it)
+ defaultDeflateLevel: 6,
+
+ // Add a comment to the zip file
+ comment: 'This is a comment.',
+
+ // Send http headers (default is true)
+ sendHttpHeaders: 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.
+ contentDisposition: 'attachment',
+
+ // Output Name for HTTP Content-Disposition
+ // Defaults to no name
+ outputName: "example.zip",
+
+ // HTTP Content-Type.
+ // Defaults to 'application/x-zip'.
+ // Note that this does nothing if you are not sending HTTP headers.
+ contentType: 'application/x-zip',
+
+ // Set the function called for setting headers.
+ // Default is the `header()` of PHP
+ httpHeaderCallback: 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.
+ // Set to true if your input stream is remote
+ // (used with addFileFromStream()).
+ // Default is false.
+ defaultEnableZeroHeader: false,
+
+ // Enable zip64 extension, allowing very large archives
+ // (> 4Gb or file count > 64k)
+ // Default is true
+ enableZip64: true,
+
+ // Flush output buffer after every write
+ // Default is false
+ flushOutput: true,
+ );
diff --git a/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
new file mode 100644
index 000000000..22af71d4a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
@@ -0,0 +1,21 @@
+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(
+ fileName: 'streamfile.txt',
+ stream: $stream,
+ );
diff --git a/vendor/maennchen/zipstream-php/guides/StreamOutput.rst b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
new file mode 100644
index 000000000..9f3165b75
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
@@ -0,0 +1,39 @@
+Stream Output
+===============
+
+Stream to S3 Bucket
+---------------
+
+.. code-block:: php
+
+ use Aws\S3\S3Client;
+ use Aws\Credentials\CredentialProvider;
+ 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');
+
+ $zip = new ZipStream(
+ enableZip64: false,
+ outputStream: $zipFile,
+ );
+
+ $zip->addFile(
+ fileName: 'file1.txt',
+ data: 'File1 data',
+ );
+ $zip->addFile(
+ fileName: 'file2.txt',
+ data: 'File2 data',
+ );
+ $zip->finish();
+
+ fclose($zipFile);
diff --git a/vendor/maennchen/zipstream-php/guides/Symfony.rst b/vendor/maennchen/zipstream-php/guides/Symfony.rst
new file mode 100644
index 000000000..902552c92
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/Symfony.rst
@@ -0,0 +1,130 @@
+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.
+ // this is needed to prevent issues with truncated zip files
+ //initialise zipstream with output zip filename and options.
+ $zip = new ZipStream\ZipStream(
+ outputName: 'test.zip',
+ defaultEnableZeroHeader: true,
+ contentType: 'application/octet-stream',
+ );
+
+ //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.
+ // replace with your bucket name or get from parameters file.
+ $bucket = 'bucketname';
+ $s3path = "s3://" . $bucket . "/" . $key;
+
+ //addFileFromStream
+ if ($streamRead = fopen($s3path, 'r')) {
+ $zip->addFileFromStream(
+ fileName: $fileName,
+ stream: $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);
diff --git a/vendor/maennchen/zipstream-php/guides/Varnish.rst b/vendor/maennchen/zipstream-php/guides/Varnish.rst
new file mode 100644
index 000000000..952d28749
--- /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 000000000..48f465aea
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/guides/index.rst
@@ -0,0 +1,126 @@
+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
+
+If you want to use``addFileFromPsr7Stream```
+(``Psr\Http\Message\StreamInterface``) or use a stream instead of a
+``resource`` as ``outputStream``, the following dependencies must be installed
+as well:
+
+.. code-block:: sh
+ composer require psr/http-message guzzlehttp/psr7
+
+If ``composer install`` yields the following error, your installation is missing
+the `mbstring extension <https://www.php.net/manual/en/book.mbstring.php>`_,
+either `install it <https://www.php.net/manual/en/mbstring.installation.php>`_
+or run the follwoing command:
+
+.. code-block::
+ Your requirements could not be resolved to an installable set of packages.
+
+ Problem 1
+ - Root composer.json requires PHP extension ext-mbstring * but it is
+ missing from your system. Install or enable PHP's mbstrings extension.
+
+.. code-block:: sh
+ composer require symfony/polyfill-mbstring
+
+Usage Intro
+---------------
+
+Here's a simple example:
+
+.. code-block:: php
+
+ // Autoload the dependencies
+ require 'vendor/autoload.php';
+
+ // create a new zipstream object
+ $zip = new ZipStream\ZipStream(
+ outputName: 'example.zip',
+
+ // enable output of HTTP headers
+ sendHttpHeaders: true,
+ );
+
+ // create a file named 'hello.txt'
+ $zip->addFile(
+ fileName: 'hello.txt',
+ data: 'This is the contents of hello.txt',
+ );
+
+ // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
+ $zip->addFileFromPath(
+ fileName: 'some_image.jpg',
+ path: 'path/to/image.jpg',
+ );
+
+ // add a file named 'goodbye.txt' from an open stream resource
+ $filePointer = tmpfile();
+ fwrite($filePointer, 'The quick brown fox jumped over the lazy dog.');
+ rewind($filePointer);
+ $zip->addFileFromStream(
+ fileName: 'goodbye.txt',
+ stream: $filePointer,
+ );
+ fclose($filePointer);
+
+ // add a file named 'streamfile.txt' from the body of a `guzzle` response
+ // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies required.
+ $zip->addFileFromPsr7Stream(
+ fileName: 'streamfile.txt',
+ stream: $response->getBody(),
+ );
+
+ // 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/116>`_.
+
+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>`_.
diff --git a/vendor/maennchen/zipstream-php/phpdoc.dist.xml b/vendor/maennchen/zipstream-php/phpdoc.dist.xml
new file mode 100644
index 000000000..b98fe1cd2
--- /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 000000000..1b02a3af8
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/phpunit.xml.dist
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="test/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd" cacheDirectory=".phpunit.cache">
+ <coverage/>
+ <testsuites>
+ <testsuite name="Application">
+ <directory>test</directory>
+ </testsuite>
+ </testsuites>
+ <logging/>
+ <source>
+ <include>
+ <directory suffix=".php">src</directory>
+ </include>
+ </source>
+</phpunit>
diff --git a/vendor/maennchen/zipstream-php/psalm.xml b/vendor/maennchen/zipstream-php/psalm.xml
new file mode 100644
index 000000000..4da861836
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/psalm.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<psalm
+ errorLevel="1"
+ 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"
+ findUnusedBaselineEntry="true"
+ findUnusedCode="true"
+ phpVersion="8.1.0"
+>
+ <!-- TODO: Update phpVersion when raising the minimum supported version -->
+ <projectFiles>
+ <directory name="src" />
+ <ignoreFiles>
+ <directory name="vendor" />
+ </ignoreFiles>
+ </projectFiles>
+ <issueHandlers>
+ <!-- Turn off dead code warnings for externally called functions -->
+ <PossiblyUnusedProperty errorLevel="suppress" />
+ <PossiblyUnusedMethod errorLevel="suppress" />
+ <PossiblyUnusedReturnValue errorLevel="suppress" />
+ </issueHandlers>
+</psalm>
diff --git a/vendor/maennchen/zipstream-php/results.sarif b/vendor/maennchen/zipstream-php/results.sarif
new file mode 100644
index 000000000..c99a3f47f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/results.sarif
@@ -0,0 +1 @@
+{"version":"2.1.0","$schema":"https:\/\/json.schemastore.org\/sarif-2.1.0.json","runs":[{"tool":{"driver":{"name":"Psalm","informationUri":"https:\/\/psalm.dev","version":"5.26.1@d747f6500b38ac4f7dfc5edbcae6e4b637d7add0"}},"results":[]}]}
diff --git a/vendor/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php b/vendor/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php
new file mode 100644
index 000000000..ffcfc6e97
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use DateTimeInterface;
+
+/**
+ * @internal
+ */
+abstract class CentralDirectoryFileHeader
+{
+ private const SIGNATURE = 0x02014b50;
+
+ public static function generate(
+ int $versionMadeBy,
+ int $versionNeededToExtract,
+ int $generalPurposeBitFlag,
+ CompressionMethod $compressionMethod,
+ DateTimeInterface $lastModificationDateTime,
+ int $crc32,
+ int $compressedSize,
+ int $uncompressedSize,
+ string $fileName,
+ string $extraField,
+ string $fileComment,
+ int $diskNumberStart,
+ int $internalFileAttributes,
+ int $externalFileAttributes,
+ int $relativeOffsetOfLocalHeader,
+ ): string {
+ return PackField::pack(
+ new PackField(format: 'V', value: self::SIGNATURE),
+ new PackField(format: 'v', value: $versionMadeBy),
+ new PackField(format: 'v', value: $versionNeededToExtract),
+ new PackField(format: 'v', value: $generalPurposeBitFlag),
+ new PackField(format: 'v', value: $compressionMethod->value),
+ new PackField(format: 'V', value: Time::dateTimeToDosTime($lastModificationDateTime)),
+ new PackField(format: 'V', value: $crc32),
+ new PackField(format: 'V', value: $compressedSize),
+ new PackField(format: 'V', value: $uncompressedSize),
+ new PackField(format: 'v', value: strlen($fileName)),
+ new PackField(format: 'v', value: strlen($extraField)),
+ new PackField(format: 'v', value: strlen($fileComment)),
+ new PackField(format: 'v', value: $diskNumberStart),
+ new PackField(format: 'v', value: $internalFileAttributes),
+ new PackField(format: 'V', value: $externalFileAttributes),
+ new PackField(format: 'V', value: $relativeOffsetOfLocalHeader),
+ ) . $fileName . $extraField . $fileComment;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/CompressionMethod.php b/vendor/maennchen/zipstream-php/src/CompressionMethod.php
new file mode 100644
index 000000000..51e436370
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/CompressionMethod.php
@@ -0,0 +1,106 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+enum CompressionMethod: int
+{
+ /**
+ * The file is stored (no compression)
+ */
+ case STORE = 0x00;
+
+ // 0x01: legacy algorithm - The file is Shrunk
+ // 0x02: legacy algorithm - The file is Reduced with compression factor 1
+ // 0x03: legacy algorithm - The file is Reduced with compression factor 2
+ // 0x04: legacy algorithm - The file is Reduced with compression factor 3
+ // 0x05: legacy algorithm - The file is Reduced with compression factor 4
+ // 0x06: legacy algorithm - The file is Imploded
+ // 0x07: Reserved for Tokenizing compression algorithm
+
+ /**
+ * The file is Deflated
+ */
+ case DEFLATE = 0x08;
+
+ // /**
+ // * Enhanced Deflating using Deflate64(tm)
+ // */
+ // case DEFLATE_64 = 0x09;
+
+ // /**
+ // * PKWARE Data Compression Library Imploding (old IBM TERSE)
+ // */
+ // case PKWARE = 0x0a;
+
+ // // 0x0b: Reserved by PKWARE
+
+ // /**
+ // * File is compressed using BZIP2 algorithm
+ // */
+ // case BZIP2 = 0x0c;
+
+ // // 0x0d: Reserved by PKWARE
+
+ // /**
+ // * LZMA
+ // */
+ // case LZMA = 0x0e;
+
+ // // 0x0f: Reserved by PKWARE
+
+ // /**
+ // * IBM z/OS CMPSC Compression
+ // */
+ // case IBM_ZOS_CMPSC = 0x10;
+
+ // // 0x11: Reserved by PKWARE
+
+ // /**
+ // * File is compressed using IBM TERSE
+ // */
+ // case IBM_TERSE = 0x12;
+
+ // /**
+ // * IBM LZ77 z Architecture
+ // */
+ // case IBM_LZ77 = 0x13;
+
+ // // 0x14: deprecated (use method 93 for zstd)
+
+ // /**
+ // * Zstandard (zstd) Compression
+ // */
+ // case ZSTD = 0x5d;
+
+ // /**
+ // * MP3 Compression
+ // */
+ // case MP3 = 0x5e;
+
+ // /**
+ // * XZ Compression
+ // */
+ // case XZ = 0x5f;
+
+ // /**
+ // * JPEG variant
+ // */
+ // case JPEG = 0x60;
+
+ // /**
+ // * WavPack compressed data
+ // */
+ // case WAV_PACK = 0x61;
+
+ // /**
+ // * PPMd version I, Rev 1
+ // */
+ // case PPMD_1_1 = 0x62;
+
+ // /**
+ // * AE-x encryption marker
+ // */
+ // case AE_X_ENCRYPTION = 0x63;
+}
diff --git a/vendor/maennchen/zipstream-php/src/DataDescriptor.php b/vendor/maennchen/zipstream-php/src/DataDescriptor.php
new file mode 100644
index 000000000..04146190d
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/DataDescriptor.php
@@ -0,0 +1,26 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * @internal
+ */
+abstract class DataDescriptor
+{
+ private const SIGNATURE = 0x08074b50;
+
+ public static function generate(
+ int $crc32UncompressedData,
+ int $compressedSize,
+ int $uncompressedSize,
+ ): string {
+ return PackField::pack(
+ new PackField(format: 'V', value: self::SIGNATURE),
+ new PackField(format: 'V', value: $crc32UncompressedData),
+ new PackField(format: 'V', value: $compressedSize),
+ new PackField(format: 'V', value: $uncompressedSize),
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/EndOfCentralDirectory.php b/vendor/maennchen/zipstream-php/src/EndOfCentralDirectory.php
new file mode 100644
index 000000000..4320addc3
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/EndOfCentralDirectory.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * @internal
+ */
+abstract class EndOfCentralDirectory
+{
+ private const SIGNATURE = 0x06054b50;
+
+ public static function generate(
+ int $numberOfThisDisk,
+ int $numberOfTheDiskWithCentralDirectoryStart,
+ int $numberOfCentralDirectoryEntriesOnThisDisk,
+ int $numberOfCentralDirectoryEntries,
+ int $sizeOfCentralDirectory,
+ int $centralDirectoryStartOffsetOnDisk,
+ string $zipFileComment,
+ ): string {
+ /** @psalm-suppress MixedArgument */
+ return PackField::pack(
+ new PackField(format: 'V', value: static::SIGNATURE),
+ new PackField(format: 'v', value: $numberOfThisDisk),
+ new PackField(format: 'v', value: $numberOfTheDiskWithCentralDirectoryStart),
+ new PackField(format: 'v', value: $numberOfCentralDirectoryEntriesOnThisDisk),
+ new PackField(format: 'v', value: $numberOfCentralDirectoryEntries),
+ new PackField(format: 'V', value: $sizeOfCentralDirectory),
+ new PackField(format: 'V', value: $centralDirectoryStartOffsetOnDisk),
+ new PackField(format: 'v', value: strlen($zipFileComment)),
+ ) . $zipFileComment;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception.php b/vendor/maennchen/zipstream-php/src/Exception.php
new file mode 100644
index 000000000..2e81e307b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception.php
@@ -0,0 +1,7 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+abstract class Exception extends \Exception {}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php b/vendor/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php
new file mode 100644
index 000000000..b8d050808
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use DateTimeInterface;
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a file wasn't found
+ */
+class DosTimeOverflowException extends Exception
+{
+ /**
+ * @internal
+ */
+ public function __construct(
+ public readonly DateTimeInterface $dateTime
+ ) {
+ parent::__construct('The date ' . $dateTime->format(DateTimeInterface::ATOM) . " can't be represented as DOS time / date.");
+ }
+}
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 000000000..350a7bfe5
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php
@@ -0,0 +1,22 @@
+<?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
+{
+ /**
+ * @internal
+ */
+ public function __construct(
+ public readonly 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 000000000..93d0c6c64
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php
@@ -0,0 +1,22 @@
+<?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
+{
+ /**
+ * @internal
+ */
+ public function __construct(
+ public readonly string $path
+ ) {
+ parent::__construct("The file with the path $path isn't readable.");
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php b/vendor/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php
new file mode 100644
index 000000000..11f0b67b3
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a file is not as large as it was specified.
+ */
+class FileSizeIncorrectException extends Exception
+{
+ /**
+ * @internal
+ */
+ public function __construct(
+ public readonly int $expectedSize,
+ public readonly int $actualSize
+ ) {
+ parent::__construct("File is {$actualSize} instead of {$expectedSize} bytes large. Adjust `exactSize` parameter.");
+ }
+}
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 000000000..09bdafb28
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php
@@ -0,0 +1,21 @@
+<?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
+{
+ /**
+ * @internal
+ */
+ 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/ResourceActionException.php b/vendor/maennchen/zipstream-php/src/Exception/ResourceActionException.php
new file mode 100644
index 000000000..cbd9b0bb9
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/ResourceActionException.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a resource like `fread` returns false
+ */
+class ResourceActionException extends Exception
+{
+ /**
+ * @var ?resource
+ */
+ public $resource;
+
+ /**
+ * @param resource $resource
+ */
+ public function __construct(
+ public readonly string $function,
+ $resource = null,
+ ) {
+ $this->resource = $resource;
+ parent::__construct('Function ' . $function . 'failed on resource.');
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php b/vendor/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php
new file mode 100644
index 000000000..717c1aafe
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a strict simulation is executed and the file
+ * information can't be determined without reading the entire file.
+ */
+class SimulationFileUnknownException extends Exception
+{
+ public function __construct()
+ {
+ parent::__construct('The details of the strict simulation file could not be determined without reading the entire file.');
+ }
+}
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 000000000..c1446735a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a stream can't be read.
+ */
+class StreamNotReadableException extends Exception
+{
+ /**
+ * @internal
+ */
+ public function __construct()
+ {
+ parent::__construct('The stream could not be read.');
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php b/vendor/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php
new file mode 100644
index 000000000..606f11f14
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a non seekable stream is
+ * provided and zero headers are disabled.
+ */
+class StreamNotSeekableException extends Exception
+{
+ /**
+ * @internal
+ */
+ public function __construct()
+ {
+ parent::__construct('enableZeroHeader must be enable to add non seekable streams');
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/File.php b/vendor/maennchen/zipstream-php/src/File.php
new file mode 100644
index 000000000..0462196e2
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/File.php
@@ -0,0 +1,420 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use Closure;
+use DateTimeInterface;
+use DeflateContext;
+use RuntimeException;
+use ZipStream\Exception\FileSizeIncorrectException;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Exception\ResourceActionException;
+use ZipStream\Exception\SimulationFileUnknownException;
+use ZipStream\Exception\StreamNotReadableException;
+use ZipStream\Exception\StreamNotSeekableException;
+
+/**
+ * @internal
+ */
+class File
+{
+ private const CHUNKED_READ_BLOCK_SIZE = 0x1000000;
+
+ private Version $version;
+
+ private int $compressedSize = 0;
+
+ private int $uncompressedSize = 0;
+
+ private int $crc = 0;
+
+ private int $generalPurposeBitFlag = 0;
+
+ private readonly string $fileName;
+
+ /**
+ * @var resource|null
+ */
+ private $stream;
+
+ /**
+ * @param Closure $dataCallback
+ * @psalm-param Closure(): resource $dataCallback
+ */
+ public function __construct(
+ string $fileName,
+ private readonly Closure $dataCallback,
+ private readonly OperationMode $operationMode,
+ private readonly int $startOffset,
+ private readonly CompressionMethod $compressionMethod,
+ private readonly string $comment,
+ private readonly DateTimeInterface $lastModificationDateTime,
+ private readonly int $deflateLevel,
+ private readonly ?int $maxSize,
+ private readonly ?int $exactSize,
+ private readonly bool $enableZip64,
+ private readonly bool $enableZeroHeader,
+ private readonly Closure $send,
+ private readonly Closure $recordSentBytes,
+ ) {
+ $this->fileName = self::filterFilename($fileName);
+ $this->checkEncoding();
+
+ if ($this->enableZeroHeader) {
+ $this->generalPurposeBitFlag |= GeneralPurposeBitFlag::ZERO_HEADER;
+ }
+
+ $this->version = $this->compressionMethod === CompressionMethod::DEFLATE ? Version::DEFLATE : Version::STORE;
+ }
+
+ public function cloneSimulationExecution(): self
+ {
+ return new self(
+ $this->fileName,
+ $this->dataCallback,
+ OperationMode::NORMAL,
+ $this->startOffset,
+ $this->compressionMethod,
+ $this->comment,
+ $this->lastModificationDateTime,
+ $this->deflateLevel,
+ $this->maxSize,
+ $this->exactSize,
+ $this->enableZip64,
+ $this->enableZeroHeader,
+ $this->send,
+ $this->recordSentBytes,
+ );
+ }
+
+ public function process(): string
+ {
+ $forecastSize = $this->forecastSize();
+
+ if ($this->enableZeroHeader) {
+ // No calculation required
+ } elseif ($this->isSimulation() && $forecastSize !== null) {
+ $this->uncompressedSize = $forecastSize;
+ $this->compressedSize = $forecastSize;
+ } else {
+ $this->readStream(send: false);
+ if (rewind($this->unpackStream()) === false) {
+ throw new ResourceActionException('rewind', $this->unpackStream());
+ }
+ }
+
+ $this->addFileHeader();
+
+ $detectedSize = $forecastSize ?? ($this->compressedSize > 0 ? $this->compressedSize : null);
+
+ if (
+ $this->isSimulation() &&
+ $detectedSize !== null
+ ) {
+ ($this->recordSentBytes)($detectedSize);
+ } else {
+ $this->readStream(send: true);
+ }
+
+ $this->addFileFooter();
+ return $this->getCdrFile();
+ }
+
+ /**
+ * @return resource
+ */
+ private function unpackStream()
+ {
+ if ($this->stream) {
+ return $this->stream;
+ }
+
+ if ($this->operationMode === OperationMode::SIMULATE_STRICT) {
+ throw new SimulationFileUnknownException();
+ }
+
+ $this->stream = ($this->dataCallback)();
+
+ if (!$this->enableZeroHeader && !stream_get_meta_data($this->stream)['seekable']) {
+ throw new StreamNotSeekableException();
+ }
+ if (!(
+ str_contains(stream_get_meta_data($this->stream)['mode'], 'r')
+ || str_contains(stream_get_meta_data($this->stream)['mode'], 'w+')
+ || str_contains(stream_get_meta_data($this->stream)['mode'], 'a+')
+ || str_contains(stream_get_meta_data($this->stream)['mode'], 'x+')
+ || str_contains(stream_get_meta_data($this->stream)['mode'], 'c+')
+ )) {
+ throw new StreamNotReadableException();
+ }
+
+ return $this->stream;
+ }
+
+ private function forecastSize(): ?int
+ {
+ if ($this->compressionMethod !== CompressionMethod::STORE) {
+ return null;
+ }
+ if ($this->exactSize !== null) {
+ return $this->exactSize;
+ }
+ $fstat = fstat($this->unpackStream());
+ if (!$fstat || !array_key_exists('size', $fstat) || $fstat['size'] < 1) {
+ return null;
+ }
+
+ if ($this->maxSize !== null && $this->maxSize < $fstat['size']) {
+ return $this->maxSize;
+ }
+
+ return $fstat['size'];
+ }
+
+ /**
+ * Create and send zip header for this file.
+ */
+ private function addFileHeader(): void
+ {
+ $forceEnableZip64 = $this->enableZeroHeader && $this->enableZip64;
+
+ $footer = $this->buildZip64ExtraBlock($forceEnableZip64);
+
+ $zip64Enabled = $footer !== '';
+
+ if ($zip64Enabled) {
+ $this->version = Version::ZIP64;
+ }
+
+ if ($this->generalPurposeBitFlag & GeneralPurposeBitFlag::EFS) {
+ // Put the tricky entry to
+ // force Linux unzip to lookup EFS flag.
+ $footer .= Zs\ExtendedInformationExtraField::generate();
+ }
+
+ $data = LocalFileHeader::generate(
+ versionNeededToExtract: $this->version->value,
+ generalPurposeBitFlag: $this->generalPurposeBitFlag,
+ compressionMethod: $this->compressionMethod,
+ lastModificationDateTime: $this->lastModificationDateTime,
+ crc32UncompressedData: $this->crc,
+ compressedSize: $zip64Enabled
+ ? 0xFFFFFFFF
+ : $this->compressedSize,
+ uncompressedSize: $zip64Enabled
+ ? 0xFFFFFFFF
+ : $this->uncompressedSize,
+ fileName: $this->fileName,
+ extraField: $footer,
+ );
+
+
+ ($this->send)($data);
+ }
+
+ /**
+ * Strip characters that are not legal in Windows filenames
+ * to prevent compatibility issues
+ */
+ private static function filterFilename(
+ /**
+ * Unprocessed filename
+ */
+ string $fileName
+ ): string {
+ // strip leading slashes from file name
+ // (fixes bug in windows archive viewer)
+ $fileName = ltrim($fileName, '/');
+
+ return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $fileName);
+ }
+
+ private function checkEncoding(): void
+ {
+ // 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($this->fileName, 'UTF-8') &&
+ mb_check_encoding($this->comment, 'UTF-8')) {
+ $this->generalPurposeBitFlag |= GeneralPurposeBitFlag::EFS;
+ }
+ }
+
+ private function buildZip64ExtraBlock(bool $force = false): string
+ {
+ $outputZip64ExtraBlock = false;
+
+ $originalSize = null;
+ if ($force || $this->uncompressedSize > 0xFFFFFFFF) {
+ $outputZip64ExtraBlock = true;
+ $originalSize = $this->uncompressedSize;
+ }
+
+ $compressedSize = null;
+ if ($force || $this->compressedSize > 0xFFFFFFFF) {
+ $outputZip64ExtraBlock = true;
+ $compressedSize = $this->compressedSize;
+ }
+
+ // 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
+ $relativeHeaderOffset = null;
+ if ($this->startOffset > 0xFFFFFFFF) {
+ $outputZip64ExtraBlock = true;
+ $relativeHeaderOffset = $this->startOffset;
+ }
+
+ if (!$outputZip64ExtraBlock) {
+ return '';
+ }
+
+ if (!$this->enableZip64) {
+ throw new OverflowException();
+ }
+
+ return Zip64\ExtendedInformationExtraField::generate(
+ originalSize: $originalSize,
+ compressedSize: $compressedSize,
+ relativeHeaderOffset: $relativeHeaderOffset,
+ diskStartNumber: null,
+ );
+ }
+
+ private function addFileFooter(): void
+ {
+ if (($this->compressedSize > 0xFFFFFFFF || $this->uncompressedSize > 0xFFFFFFFF) && $this->version !== Version::ZIP64) {
+ throw new OverflowException();
+ }
+
+ if (!$this->enableZeroHeader) {
+ return;
+ }
+
+ if ($this->version === Version::ZIP64) {
+ $footer = Zip64\DataDescriptor::generate(
+ crc32UncompressedData: $this->crc,
+ compressedSize: $this->compressedSize,
+ uncompressedSize: $this->uncompressedSize,
+ );
+ } else {
+ $footer = DataDescriptor::generate(
+ crc32UncompressedData: $this->crc,
+ compressedSize: $this->compressedSize,
+ uncompressedSize: $this->uncompressedSize,
+ );
+ }
+
+ ($this->send)($footer);
+ }
+
+ private function readStream(bool $send): void
+ {
+ $this->compressedSize = 0;
+ $this->uncompressedSize = 0;
+ $hash = hash_init('crc32b');
+
+ $deflate = $this->compressionInit();
+
+ while (
+ !feof($this->unpackStream()) &&
+ ($this->maxSize === null || $this->uncompressedSize < $this->maxSize) &&
+ ($this->exactSize === null || $this->uncompressedSize < $this->exactSize)
+ ) {
+ $readLength = min(
+ ($this->maxSize ?? PHP_INT_MAX) - $this->uncompressedSize,
+ ($this->exactSize ?? PHP_INT_MAX) - $this->uncompressedSize,
+ self::CHUNKED_READ_BLOCK_SIZE
+ );
+
+ $data = fread($this->unpackStream(), $readLength);
+
+ hash_update($hash, $data);
+
+ $this->uncompressedSize += strlen($data);
+
+ if ($deflate) {
+ $data = deflate_add(
+ $deflate,
+ $data,
+ feof($this->unpackStream()) ? ZLIB_FINISH : ZLIB_NO_FLUSH
+ );
+ }
+
+ $this->compressedSize += strlen($data);
+
+ if ($send) {
+ ($this->send)($data);
+ }
+ }
+
+ if ($this->exactSize !== null && $this->uncompressedSize !== $this->exactSize) {
+ throw new FileSizeIncorrectException(expectedSize: $this->exactSize, actualSize: $this->uncompressedSize);
+ }
+
+ $this->crc = hexdec(hash_final($hash));
+ }
+
+ private function compressionInit(): ?DeflateContext
+ {
+ switch ($this->compressionMethod) {
+ case CompressionMethod::STORE:
+ // Noting to do
+ return null;
+ case CompressionMethod::DEFLATE:
+ $deflateContext = deflate_init(
+ ZLIB_ENCODING_RAW,
+ ['level' => $this->deflateLevel]
+ );
+
+ if (!$deflateContext) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException("Can't initialize deflate context.");
+ // @codeCoverageIgnoreEnd
+ }
+
+ // False positive, resource is no longer returned from this function
+ return $deflateContext;
+ default:
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('Unsupported Compression Method ' . print_r($this->compressionMethod, true));
+ // @codeCoverageIgnoreEnd
+ }
+ }
+
+ private function getCdrFile(): string
+ {
+ $footer = $this->buildZip64ExtraBlock();
+
+ return CentralDirectoryFileHeader::generate(
+ versionMadeBy: ZipStream::ZIP_VERSION_MADE_BY,
+ versionNeededToExtract: $this->version->value,
+ generalPurposeBitFlag: $this->generalPurposeBitFlag,
+ compressionMethod: $this->compressionMethod,
+ lastModificationDateTime: $this->lastModificationDateTime,
+ crc32: $this->crc,
+ compressedSize: $this->compressedSize > 0xFFFFFFFF
+ ? 0xFFFFFFFF
+ : $this->compressedSize,
+ uncompressedSize: $this->uncompressedSize > 0xFFFFFFFF
+ ? 0xFFFFFFFF
+ : $this->uncompressedSize,
+ fileName: $this->fileName,
+ extraField: $footer,
+ fileComment: $this->comment,
+ diskNumberStart: 0,
+ internalFileAttributes: 0,
+ externalFileAttributes: 32,
+ relativeOffsetOfLocalHeader: $this->startOffset > 0xFFFFFFFF
+ ? 0xFFFFFFFF
+ : $this->startOffset,
+ );
+ }
+
+ private function isSimulation(): bool
+ {
+ return $this->operationMode === OperationMode::SIMULATE_LAX || $this->operationMode === OperationMode::SIMULATE_STRICT;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php b/vendor/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php
new file mode 100644
index 000000000..23a66d889
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * @internal
+ */
+abstract class GeneralPurposeBitFlag
+{
+ /**
+ * If set, indicates that the file is encrypted.
+ */
+ public const ENCRYPTED = 1 << 0;
+
+ /**
+ * (For Methods 8 and 9 - Deflating)
+ * Normal (-en) compression option was used.
+ */
+ public const DEFLATE_COMPRESSION_NORMAL = 0 << 1;
+
+ /**
+ * (For Methods 8 and 9 - Deflating)
+ * Maximum (-exx/-ex) compression option was used.
+ */
+ public const DEFLATE_COMPRESSION_MAXIMUM = 1 << 1;
+
+ /**
+ * (For Methods 8 and 9 - Deflating)
+ * Fast (-ef) compression option was used.
+ */
+ public const DEFLATE_COMPRESSION_FAST = 10 << 1;
+
+ /**
+ * (For Methods 8 and 9 - Deflating)
+ * Super Fast (-es) compression option was used.
+ */
+ public const DEFLATE_COMPRESSION_SUPERFAST = 11 << 1;
+
+ /**
+ * If the compression method used was type 14,
+ * LZMA, then this bit, if set, indicates
+ * an end-of-stream (EOS) marker is used to
+ * mark the end of the compressed data stream.
+ * If clear, then an EOS marker is not present
+ * and the compressed data size must be known
+ * to extract.
+ */
+ public const LZMA_EOS = 1 << 1;
+
+ /**
+ * If this bit is set, the fields crc-32, compressed
+ * size and uncompressed size are set to zero in the
+ * local header. The correct values are put in the
+ * data descriptor immediately following the compressed
+ * data.
+ */
+ public const ZERO_HEADER = 1 << 3;
+
+ /**
+ * If this bit is set, this indicates that the file is
+ * compressed patched data.
+ */
+ public const COMPRESSED_PATCHED_DATA = 1 << 5;
+
+ /**
+ * Strong encryption. If this bit is set, you MUST
+ * set the version needed to extract value to at least
+ * 50 and you MUST also set bit 0. If AES encryption
+ * is used, the version needed to extract value MUST
+ * be at least 51.
+ */
+ public const STRONG_ENCRYPTION = 1 << 6;
+
+ /**
+ * Language encoding flag (EFS). If this bit is set,
+ * the filename and comment fields for this file
+ * MUST be encoded using UTF-8.
+ */
+ public const EFS = 1 << 11;
+
+ /**
+ * Set when encrypting the Central Directory to indicate
+ * selected data values in the Local Header are masked to
+ * hide their actual values.
+ */
+ public const ENCRYPT_CENTRAL_DIRECTORY = 1 << 13;
+}
diff --git a/vendor/maennchen/zipstream-php/src/LocalFileHeader.php b/vendor/maennchen/zipstream-php/src/LocalFileHeader.php
new file mode 100644
index 000000000..e08b65610
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/LocalFileHeader.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use DateTimeInterface;
+
+/**
+ * @internal
+ */
+abstract class LocalFileHeader
+{
+ private const SIGNATURE = 0x04034b50;
+
+ public static function generate(
+ int $versionNeededToExtract,
+ int $generalPurposeBitFlag,
+ CompressionMethod $compressionMethod,
+ DateTimeInterface $lastModificationDateTime,
+ int $crc32UncompressedData,
+ int $compressedSize,
+ int $uncompressedSize,
+ string $fileName,
+ string $extraField,
+ ): string {
+ return PackField::pack(
+ new PackField(format: 'V', value: self::SIGNATURE),
+ new PackField(format: 'v', value: $versionNeededToExtract),
+ new PackField(format: 'v', value: $generalPurposeBitFlag),
+ new PackField(format: 'v', value: $compressionMethod->value),
+ new PackField(format: 'V', value: Time::dateTimeToDosTime($lastModificationDateTime)),
+ new PackField(format: 'V', value: $crc32UncompressedData),
+ new PackField(format: 'V', value: $compressedSize),
+ new PackField(format: 'V', value: $uncompressedSize),
+ new PackField(format: 'v', value: strlen($fileName)),
+ new PackField(format: 'v', value: strlen($extraField)),
+ ) . $fileName . $extraField;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/OperationMode.php b/vendor/maennchen/zipstream-php/src/OperationMode.php
new file mode 100644
index 000000000..dd650f070
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/OperationMode.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * ZipStream execution operation modes
+ */
+enum OperationMode
+{
+ /**
+ * Stream file into output stream
+ */
+ case NORMAL;
+
+ /**
+ * Simulate the zip to figure out the resulting file size
+ *
+ * This only supports entries where the file size is known beforehand and
+ * deflation is disabled.
+ */
+ case SIMULATE_STRICT;
+
+ /**
+ * Simulate the zip to figure out the resulting file size
+ *
+ * If the file size is not known beforehand or deflation is enabled, the
+ * entry streams will be read and rewound.
+ *
+ * If the entry does not support rewinding either, you will not be able to
+ * use the same stream in a later operation mode like `NORMAL`.
+ */
+ case SIMULATE_LAX;
+}
diff --git a/vendor/maennchen/zipstream-php/src/PackField.php b/vendor/maennchen/zipstream-php/src/PackField.php
new file mode 100644
index 000000000..892b4009a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/PackField.php
@@ -0,0 +1,56 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use RuntimeException;
+
+/**
+ * @internal
+ * TODO: Make class readonly when requiring PHP 8.2 exclusively
+ */
+class PackField
+{
+ public const MAX_V = 0xFFFFFFFF;
+
+ public const MAX_v = 0xFFFF;
+
+ public function __construct(
+ public readonly string $format,
+ public readonly int|string $value
+ ) {}
+
+ /**
+ * Create a format string and argument list for pack(), then call
+ * pack() and return the result.
+ */
+ public static function pack(self ...$fields): string
+ {
+ $fmt = array_reduce($fields, function (string $acc, self $field) {
+ return $acc . $field->format;
+ }, '');
+
+ $args = array_map(function (self $field) {
+ switch ($field->format) {
+ case 'V':
+ if ($field->value > self::MAX_V) {
+ throw new RuntimeException(print_r($field->value, true) . ' is larger than 32 bits');
+ }
+ break;
+ case 'v':
+ if ($field->value > self::MAX_v) {
+ throw new RuntimeException(print_r($field->value, true) . ' is larger than 16 bits');
+ }
+ break;
+ case 'P': break;
+ default:
+ break;
+ }
+
+ return $field->value;
+ }, $fields);
+
+ return pack($fmt, ...$args);
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Time.php b/vendor/maennchen/zipstream-php/src/Time.php
new file mode 100644
index 000000000..1b4121ca9
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Time.php
@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use DateInterval;
+use DateTimeImmutable;
+use DateTimeInterface;
+use ZipStream\Exception\DosTimeOverflowException;
+
+/**
+ * @internal
+ */
+abstract class Time
+{
+ private const DOS_MINIMUM_DATE = '1980-01-01 00:00:00Z';
+
+ public static function dateTimeToDosTime(DateTimeInterface $dateTime): int
+ {
+ $dosMinimumDate = new DateTimeImmutable(self::DOS_MINIMUM_DATE);
+
+ if ($dateTime->getTimestamp() < $dosMinimumDate->getTimestamp()) {
+ throw new DosTimeOverflowException(dateTime: $dateTime);
+ }
+
+ $dateTime = DateTimeImmutable::createFromInterface($dateTime)->sub(new DateInterval('P1980Y'));
+
+ [$year, $month, $day, $hour, $minute, $second] = explode(' ', $dateTime->format('Y n j G i s'));
+
+ return
+ ((int) $year << 25) |
+ ((int) $month << 21) |
+ ((int) $day << 16) |
+ ((int) $hour << 11) |
+ ((int) $minute << 5) |
+ ((int) $second >> 1);
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Version.php b/vendor/maennchen/zipstream-php/src/Version.php
new file mode 100644
index 000000000..c014f8a10
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Version.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+enum Version: int
+{
+ case STORE = 0x000A; // 1.00
+ case DEFLATE = 0x0014; // 2.00
+ case ZIP64 = 0x002D; // 4.50
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/DataDescriptor.php b/vendor/maennchen/zipstream-php/src/Zip64/DataDescriptor.php
new file mode 100644
index 000000000..041c5579d
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/DataDescriptor.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class DataDescriptor
+{
+ private const SIGNATURE = 0x08074b50;
+
+ public static function generate(
+ int $crc32UncompressedData,
+ int $compressedSize,
+ int $uncompressedSize,
+ ): string {
+ return PackField::pack(
+ new PackField(format: 'V', value: self::SIGNATURE),
+ new PackField(format: 'V', value: $crc32UncompressedData),
+ new PackField(format: 'P', value: $compressedSize),
+ new PackField(format: 'P', value: $uncompressedSize),
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php
new file mode 100644
index 000000000..08588e49c
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class EndOfCentralDirectory
+{
+ private const SIGNATURE = 0x06064b50;
+
+ public static function generate(
+ int $versionMadeBy,
+ int $versionNeededToExtract,
+ int $numberOfThisDisk,
+ int $numberOfTheDiskWithCentralDirectoryStart,
+ int $numberOfCentralDirectoryEntriesOnThisDisk,
+ int $numberOfCentralDirectoryEntries,
+ int $sizeOfCentralDirectory,
+ int $centralDirectoryStartOffsetOnDisk,
+ string $extensibleDataSector,
+ ): string {
+ $recordSize = 44 + strlen($extensibleDataSector); // (length of block - 12) = 44;
+
+ /** @psalm-suppress MixedArgument */
+ return PackField::pack(
+ new PackField(format: 'V', value: static::SIGNATURE),
+ new PackField(format: 'P', value: $recordSize),
+ new PackField(format: 'v', value: $versionMadeBy),
+ new PackField(format: 'v', value: $versionNeededToExtract),
+ new PackField(format: 'V', value: $numberOfThisDisk),
+ new PackField(format: 'V', value: $numberOfTheDiskWithCentralDirectoryStart),
+ new PackField(format: 'P', value: $numberOfCentralDirectoryEntriesOnThisDisk),
+ new PackField(format: 'P', value: $numberOfCentralDirectoryEntries),
+ new PackField(format: 'P', value: $sizeOfCentralDirectory),
+ new PackField(format: 'P', value: $centralDirectoryStartOffsetOnDisk),
+ ) . $extensibleDataSector;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php
new file mode 100644
index 000000000..ef431c347
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class EndOfCentralDirectoryLocator
+{
+ private const SIGNATURE = 0x07064b50;
+
+ public static function generate(
+ int $numberOfTheDiskWithZip64CentralDirectoryStart,
+ int $zip64centralDirectoryStartOffsetOnDisk,
+ int $totalNumberOfDisks,
+ ): string {
+ /** @psalm-suppress MixedArgument */
+ return PackField::pack(
+ new PackField(format: 'V', value: static::SIGNATURE),
+ new PackField(format: 'V', value: $numberOfTheDiskWithZip64CentralDirectoryStart),
+ new PackField(format: 'P', value: $zip64centralDirectoryStartOffsetOnDisk),
+ new PackField(format: 'V', value: $totalNumberOfDisks),
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php b/vendor/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php
new file mode 100644
index 000000000..aaac51c83
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class ExtendedInformationExtraField
+{
+ private const TAG = 0x0001;
+
+ public static function generate(
+ ?int $originalSize = null,
+ ?int $compressedSize = null,
+ ?int $relativeHeaderOffset = null,
+ ?int $diskStartNumber = null,
+ ): string {
+ return PackField::pack(
+ new PackField(format: 'v', value: self::TAG),
+ new PackField(
+ format: 'v',
+ value: ($originalSize === null ? 0 : 8) +
+ ($compressedSize === null ? 0 : 8) +
+ ($relativeHeaderOffset === null ? 0 : 8) +
+ ($diskStartNumber === null ? 0 : 4)
+ ),
+ ...($originalSize === null ? [] : [
+ new PackField(format: 'P', value: $originalSize),
+ ]),
+ ...($compressedSize === null ? [] : [
+ new PackField(format: 'P', value: $compressedSize),
+ ]),
+ ...($relativeHeaderOffset === null ? [] : [
+ new PackField(format: 'P', value: $relativeHeaderOffset),
+ ]),
+ ...($diskStartNumber === null ? [] : [
+ new PackField(format: 'V', value: $diskStartNumber),
+ ]),
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/ZipStream.php b/vendor/maennchen/zipstream-php/src/ZipStream.php
new file mode 100644
index 000000000..3f4f481a8
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/ZipStream.php
@@ -0,0 +1,865 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use Closure;
+use DateTimeImmutable;
+use DateTimeInterface;
+use GuzzleHttp\Psr7\StreamWrapper;
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+use ZipStream\Exception\FileNotFoundException;
+use ZipStream\Exception\FileNotReadableException;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Exception\ResourceActionException;
+
+/**
+ * Streamed, dynamically generated zip archives.
+ *
+ * ## Usage
+ *
+ * Streaming zip archives is a simple, three-step process:
+ *
+ * 1. Create the zip stream:
+ *
+ * ```php
+ * $zip = new ZipStream(outputName: 'example.zip');
+ * ```
+ *
+ * 2. Add one or more files to the archive:
+ *
+ * ```php
+ * // add first file
+ * $zip->addFile(fileName: 'world.txt', data: 'Hello World');
+ *
+ * // add second file
+ * $zip->addFile(fileName: 'moon.txt', data: 'Hello Moon');
+ * ```
+ *
+ * 3. Finish the zip stream:
+ *
+ * ```php
+ * $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
+ *
+ * ```php
+ * // create a new zip stream object
+ * $zip = new ZipStream(outputName: '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->addFileFromPath(fileName: $path, $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
+ *
+ * @internal
+ */
+ public const ZIP_VERSION_MADE_BY = 0x603;
+
+ private bool $ready = true;
+
+ private int $offset = 0;
+
+ /**
+ * @var string[]
+ */
+ private array $centralDirectoryRecords = [];
+
+ /**
+ * @var resource
+ */
+ private $outputStream;
+
+ private readonly Closure $httpHeaderCallback;
+
+ /**
+ * @var File[]
+ */
+ private array $recordedSimulation = [];
+
+ /**
+ * Create a new ZipStream object.
+ *
+ * ##### Examples
+ *
+ * ```php
+ * // create a new zip file named 'foo.zip'
+ * $zip = new ZipStream(outputName: 'foo.zip');
+ *
+ * // create a new zip file named 'bar.zip' with a comment
+ * $zip = new ZipStream(
+ * outputName: 'bar.zip',
+ * comment: 'this is a comment for the zip file.',
+ * );
+ * ```
+ *
+ * @param OperationMode $operationMode
+ * The mode can be used to switch between `NORMAL` and `SIMULATION_*` modes.
+ * For details see the `OperationMode` documentation.
+ *
+ * Default to `NORMAL`.
+ *
+ * @param string $comment
+ * Archive Level Comment
+ *
+ * @param StreamInterface|resource|null $outputStream
+ * Override the output of the archive to a different target.
+ *
+ * By default the archive is sent to `STDOUT`.
+ *
+ * @param CompressionMethod $defaultCompressionMethod
+ * How to handle file compression. Legal values are
+ * `CompressionMethod::DEFLATE` (the default), or
+ * `CompressionMethod::STORE`. `STORE` sends the file raw and is
+ * significantly faster, while `DEFLATE` compresses the file and
+ * is much, much slower.
+ *
+ * @param int $defaultDeflateLevel
+ * Default deflation level. Only relevant if `compressionMethod`
+ * is `DEFLATE`.
+ *
+ * See details of [`deflate_init`](https://www.php.net/manual/en/function.deflate-init.php#refsect1-function.deflate-init-parameters)
+ *
+ * @param bool $enableZip64
+ * Enable Zip64 extension, supporting very large
+ * archives (any size > 4 GB or file count > 64k)
+ *
+ * @param bool $defaultEnableZeroHeader
+ * Enable streaming files with single read.
+ *
+ * When the zero header is set, the file is streamed into the output
+ * and the size & checksum are added at the end of the file. This is the
+ * fastest method and uses the least memory. Unfortunately not all
+ * ZIP clients fully support this and can lead to clients reporting
+ * the generated ZIP files as corrupted in combination with other
+ * circumstances. (Zip64 enabled, using UTF8 in comments / names etc.)
+ *
+ * When the zero header is not set, the length & checksum need to be
+ * defined before the file is actually added. To prevent loading all
+ * the data into memory, the data has to be read twice. If the data
+ * which is added is not seekable, this call will fail.
+ *
+ * @param bool $sendHttpHeaders
+ * Boolean indicating whether or not to send
+ * the HTTP headers for this file.
+ *
+ * @param ?Closure $httpHeaderCallback
+ * The method called to send HTTP headers
+ *
+ * @param string|null $outputName
+ * The name of the created archive.
+ *
+ * Only relevant if `$sendHttpHeaders = true`.
+ *
+ * @param string $contentDisposition
+ * HTTP Content-Disposition
+ *
+ * Only relevant if `sendHttpHeaders = true`.
+ *
+ * @param string $contentType
+ * HTTP Content Type
+ *
+ * Only relevant if `sendHttpHeaders = true`.
+ *
+ * @param bool $flushOutput
+ * Enable flush after every write to output stream.
+ *
+ * @return self
+ */
+ public function __construct(
+ private OperationMode $operationMode = OperationMode::NORMAL,
+ private readonly string $comment = '',
+ $outputStream = null,
+ private readonly CompressionMethod $defaultCompressionMethod = CompressionMethod::DEFLATE,
+ private readonly int $defaultDeflateLevel = 6,
+ private readonly bool $enableZip64 = true,
+ private readonly bool $defaultEnableZeroHeader = true,
+ private bool $sendHttpHeaders = true,
+ ?Closure $httpHeaderCallback = null,
+ private readonly ?string $outputName = null,
+ private readonly string $contentDisposition = 'attachment',
+ private readonly string $contentType = 'application/x-zip',
+ private bool $flushOutput = false,
+ ) {
+ $this->outputStream = self::normalizeStream($outputStream);
+ $this->httpHeaderCallback = $httpHeaderCallback ?? header(...);
+ }
+
+ /**
+ * Add a file to the archive.
+ *
+ * ##### File Options
+ *
+ * See {@see addFileFromPsr7Stream()}
+ *
+ * ##### Examples
+ *
+ * ```php
+ * // add a file named 'world.txt'
+ * $zip->addFile(fileName: 'world.txt', data: 'Hello World!');
+ *
+ * // add a file named 'bar.jpg' with a comment and a last-modified
+ * // time of two hours ago
+ * $zip->addFile(
+ * fileName: 'bar.jpg',
+ * data: $data,
+ * comment: 'this is a comment about bar.jpg',
+ * lastModificationDateTime: new DateTime('2 hours ago'),
+ * );
+ * ```
+ *
+ * @param string $data
+ *
+ * contents of file
+ */
+ public function addFile(
+ string $fileName,
+ string $data,
+ string $comment = '',
+ ?CompressionMethod $compressionMethod = null,
+ ?int $deflateLevel = null,
+ ?DateTimeInterface $lastModificationDateTime = null,
+ ?int $maxSize = null,
+ ?int $exactSize = null,
+ ?bool $enableZeroHeader = null,
+ ): void {
+ $this->addFileFromCallback(
+ fileName: $fileName,
+ callback: fn() => $data,
+ comment: $comment,
+ compressionMethod: $compressionMethod,
+ deflateLevel: $deflateLevel,
+ lastModificationDateTime: $lastModificationDateTime,
+ maxSize: $maxSize,
+ exactSize: $exactSize,
+ enableZeroHeader: $enableZeroHeader,
+ );
+ }
+
+ /**
+ * Add a file at path to the archive.
+ *
+ * ##### File Options
+ *
+ * See {@see addFileFromPsr7Stream()}
+ *
+ * ###### Examples
+ *
+ * ```php
+ * // add a file named 'foo.txt' from the local file '/tmp/foo.txt'
+ * $zip->addFileFromPath(
+ * fileName: 'foo.txt',
+ * path: '/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
+ * $zip->addFileFromPath(
+ * fileName: 'bigfile.rar',
+ * path: '/usr/share/bigfile.rar',
+ * comment: 'this is a comment about bigfile.rar',
+ * lastModificationDateTime: new DateTime('2 hours ago'),
+ * );
+ * ```
+ *
+ * @throws \ZipStream\Exception\FileNotFoundException
+ * @throws \ZipStream\Exception\FileNotReadableException
+ */
+ public function addFileFromPath(
+ /**
+ * name of file in archive (including directory path).
+ */
+ string $fileName,
+
+ /**
+ * path to file on disk (note: paths should be encoded using
+ * UNIX-style forward slashes -- e.g '/path/to/some/file').
+ */
+ string $path,
+ string $comment = '',
+ ?CompressionMethod $compressionMethod = null,
+ ?int $deflateLevel = null,
+ ?DateTimeInterface $lastModificationDateTime = null,
+ ?int $maxSize = null,
+ ?int $exactSize = null,
+ ?bool $enableZeroHeader = null,
+ ): void {
+ if (!is_readable($path)) {
+ if (!file_exists($path)) {
+ throw new FileNotFoundException($path);
+ }
+ throw new FileNotReadableException($path);
+ }
+
+ $fileTime = filemtime($path);
+ if ($fileTime !== false) {
+ $lastModificationDateTime ??= (new DateTimeImmutable())->setTimestamp($fileTime);
+ }
+
+ $this->addFileFromCallback(
+ fileName: $fileName,
+ callback: function () use ($path) {
+
+ $stream = fopen($path, 'rb');
+
+ if (!$stream) {
+ // @codeCoverageIgnoreStart
+ throw new ResourceActionException('fopen');
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $stream;
+ },
+ comment: $comment,
+ compressionMethod: $compressionMethod,
+ deflateLevel: $deflateLevel,
+ lastModificationDateTime: $lastModificationDateTime,
+ maxSize: $maxSize,
+ exactSize: $exactSize,
+ enableZeroHeader: $enableZeroHeader,
+ );
+ }
+
+ /**
+ * Add an open stream (resource) to the archive.
+ *
+ * ##### File Options
+ *
+ * See {@see addFileFromPsr7Stream()}
+ *
+ * ##### Examples
+ *
+ * ```php
+ * // create a temporary file stream and write text to it
+ * $filePointer = tmpfile();
+ * fwrite($filePointer, 'The quick brown fox jumped over the lazy dog.');
+ *
+ * // add a file named 'streamfile.txt' from the content of the stream
+ * $archive->addFileFromStream(
+ * fileName: 'streamfile.txt',
+ * stream: $filePointer,
+ * );
+ * ```
+ *
+ * @param resource $stream contents of file as a stream resource
+ */
+ public function addFileFromStream(
+ string $fileName,
+ $stream,
+ string $comment = '',
+ ?CompressionMethod $compressionMethod = null,
+ ?int $deflateLevel = null,
+ ?DateTimeInterface $lastModificationDateTime = null,
+ ?int $maxSize = null,
+ ?int $exactSize = null,
+ ?bool $enableZeroHeader = null,
+ ): void {
+ $this->addFileFromCallback(
+ fileName: $fileName,
+ callback: fn() => $stream,
+ comment: $comment,
+ compressionMethod: $compressionMethod,
+ deflateLevel: $deflateLevel,
+ lastModificationDateTime: $lastModificationDateTime,
+ maxSize: $maxSize,
+ exactSize: $exactSize,
+ enableZeroHeader: $enableZeroHeader,
+ );
+ }
+
+ /**
+ * Add an open stream to the archive.
+ *
+ * ##### Examples
+ *
+ * ```php
+ * $stream = $response->getBody();
+ * // add a file named 'streamfile.txt' from the content of the stream
+ * $archive->addFileFromPsr7Stream(
+ * fileName: 'streamfile.txt',
+ * stream: $stream,
+ * );
+ * ```
+ *
+ * @param string $fileName
+ * path of file in archive (including directory)
+ *
+ * @param StreamInterface $stream
+ * contents of file as a stream resource
+ *
+ * @param string $comment
+ * ZIP comment for this file
+ *
+ * @param ?CompressionMethod $compressionMethod
+ * Override `defaultCompressionMethod`
+ *
+ * See {@see __construct()}
+ *
+ * @param ?int $deflateLevel
+ * Override `defaultDeflateLevel`
+ *
+ * See {@see __construct()}
+ *
+ * @param ?DateTimeInterface $lastModificationDateTime
+ * Set last modification time of file.
+ *
+ * Default: `now`
+ *
+ * @param ?int $maxSize
+ * Only read `maxSize` bytes from file.
+ *
+ * The file is considered done when either reaching `EOF`
+ * or the `maxSize`.
+ *
+ * @param ?int $exactSize
+ * Read exactly `exactSize` bytes from file.
+ * If `EOF` is reached before reading `exactSize` bytes, an error will be
+ * thrown. The parameter allows for faster size calculations if the `stream`
+ * does not support `fstat` size or is slow and otherwise known beforehand.
+ *
+ * @param ?bool $enableZeroHeader
+ * Override `defaultEnableZeroHeader`
+ *
+ * See {@see __construct()}
+ */
+ public function addFileFromPsr7Stream(
+ string $fileName,
+ StreamInterface $stream,
+ string $comment = '',
+ ?CompressionMethod $compressionMethod = null,
+ ?int $deflateLevel = null,
+ ?DateTimeInterface $lastModificationDateTime = null,
+ ?int $maxSize = null,
+ ?int $exactSize = null,
+ ?bool $enableZeroHeader = null,
+ ): void {
+ $this->addFileFromCallback(
+ fileName: $fileName,
+ callback: fn() => $stream,
+ comment: $comment,
+ compressionMethod: $compressionMethod,
+ deflateLevel: $deflateLevel,
+ lastModificationDateTime: $lastModificationDateTime,
+ maxSize: $maxSize,
+ exactSize: $exactSize,
+ enableZeroHeader: $enableZeroHeader,
+ );
+ }
+
+ /**
+ * Add a file based on a callback.
+ *
+ * This is useful when you want to simulate a lot of files without keeping
+ * all of the file handles open at the same time.
+ *
+ * ##### Examples
+ *
+ * ```php
+ * foreach($files as $name => $size) {
+ * $archive->addFileFromCallback(
+ * fileName: 'streamfile.txt',
+ * exactSize: $size,
+ * callback: function() use($name): Psr\Http\Message\StreamInterface {
+ * $response = download($name);
+ * return $response->getBody();
+ * }
+ * );
+ * }
+ * ```
+ *
+ * @param string $fileName
+ * path of file in archive (including directory)
+ *
+ * @param Closure $callback
+ * @psalm-param Closure(): (resource|StreamInterface|string) $callback
+ * A callback to get the file contents in the shape of a PHP stream,
+ * a Psr StreamInterface implementation, or a string.
+ *
+ * @param string $comment
+ * ZIP comment for this file
+ *
+ * @param ?CompressionMethod $compressionMethod
+ * Override `defaultCompressionMethod`
+ *
+ * See {@see __construct()}
+ *
+ * @param ?int $deflateLevel
+ * Override `defaultDeflateLevel`
+ *
+ * See {@see __construct()}
+ *
+ * @param ?DateTimeInterface $lastModificationDateTime
+ * Set last modification time of file.
+ *
+ * Default: `now`
+ *
+ * @param ?int $maxSize
+ * Only read `maxSize` bytes from file.
+ *
+ * The file is considered done when either reaching `EOF`
+ * or the `maxSize`.
+ *
+ * @param ?int $exactSize
+ * Read exactly `exactSize` bytes from file.
+ * If `EOF` is reached before reading `exactSize` bytes, an error will be
+ * thrown. The parameter allows for faster size calculations if the `stream`
+ * does not support `fstat` size or is slow and otherwise known beforehand.
+ *
+ * @param ?bool $enableZeroHeader
+ * Override `defaultEnableZeroHeader`
+ *
+ * See {@see __construct()}
+ */
+ public function addFileFromCallback(
+ string $fileName,
+ Closure $callback,
+ string $comment = '',
+ ?CompressionMethod $compressionMethod = null,
+ ?int $deflateLevel = null,
+ ?DateTimeInterface $lastModificationDateTime = null,
+ ?int $maxSize = null,
+ ?int $exactSize = null,
+ ?bool $enableZeroHeader = null,
+ ): void {
+ $file = new File(
+ dataCallback: function () use ($callback, $maxSize) {
+ $data = $callback();
+
+ if (is_resource($data)) {
+ return $data;
+ }
+
+ if ($data instanceof StreamInterface) {
+ return StreamWrapper::getResource($data);
+ }
+
+
+ $stream = fopen('php://memory', 'rw+');
+ if ($stream === false) {
+ // @codeCoverageIgnoreStart
+ throw new ResourceActionException('fopen');
+ // @codeCoverageIgnoreEnd
+ }
+ if ($maxSize !== null && fwrite($stream, $data, $maxSize) === false) {
+ // @codeCoverageIgnoreStart
+ throw new ResourceActionException('fwrite', $stream);
+ // @codeCoverageIgnoreEnd
+ } elseif (fwrite($stream, $data) === false) {
+ // @codeCoverageIgnoreStart
+ throw new ResourceActionException('fwrite', $stream);
+ // @codeCoverageIgnoreEnd
+ }
+ if (rewind($stream) === false) {
+ // @codeCoverageIgnoreStart
+ throw new ResourceActionException('rewind', $stream);
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $stream;
+
+ },
+ send: $this->send(...),
+ recordSentBytes: $this->recordSentBytes(...),
+ operationMode: $this->operationMode,
+ fileName: $fileName,
+ startOffset: $this->offset,
+ compressionMethod: $compressionMethod ?? $this->defaultCompressionMethod,
+ comment: $comment,
+ deflateLevel: $deflateLevel ?? $this->defaultDeflateLevel,
+ lastModificationDateTime: $lastModificationDateTime ?? new DateTimeImmutable(),
+ maxSize: $maxSize,
+ exactSize: $exactSize,
+ enableZip64: $this->enableZip64,
+ enableZeroHeader: $enableZeroHeader ?? $this->defaultEnableZeroHeader,
+ );
+
+ if ($this->operationMode !== OperationMode::NORMAL) {
+ $this->recordedSimulation[] = $file;
+ }
+
+ $this->centralDirectoryRecords[] = $file->process();
+ }
+
+ /**
+ * Add a directory to the archive.
+ *
+ * ##### File Options
+ *
+ * See {@see addFileFromPsr7Stream()}
+ *
+ * ##### Examples
+ *
+ * ```php
+ * // add a directory named 'world/'
+ * $zip->addDirectory(fileName: 'world/');
+ * ```
+ */
+ public function addDirectory(
+ string $fileName,
+ string $comment = '',
+ ?DateTimeInterface $lastModificationDateTime = null,
+ ): void {
+ if (!str_ends_with($fileName, '/')) {
+ $fileName .= '/';
+ }
+
+ $this->addFile(
+ fileName: $fileName,
+ data: '',
+ comment: $comment,
+ compressionMethod: CompressionMethod::STORE,
+ deflateLevel: null,
+ lastModificationDateTime: $lastModificationDateTime,
+ maxSize: 0,
+ exactSize: 0,
+ enableZeroHeader: false,
+ );
+ }
+
+ /**
+ * Executes a previously calculated simulation.
+ *
+ * ##### Example
+ *
+ * ```php
+ * $zip = new ZipStream(
+ * outputName: 'foo.zip',
+ * operationMode: OperationMode::SIMULATE_STRICT,
+ * );
+ *
+ * $zip->addFile('test.txt', 'Hello World');
+ *
+ * $size = $zip->finish();
+ *
+ * header('Content-Length: '. $size);
+ *
+ * $zip->executeSimulation();
+ * ```
+ */
+ public function executeSimulation(): void
+ {
+ if ($this->operationMode !== OperationMode::NORMAL) {
+ throw new RuntimeException('Zip simulation is not finished.');
+ }
+
+ foreach ($this->recordedSimulation as $file) {
+ $this->centralDirectoryRecords[] = $file->cloneSimulationExecution()->process();
+ }
+
+ $this->finish();
+ }
+
+ /**
+ * Write zip footer to stream.
+ *
+ * The clase is left in an unusable state after `finish`.
+ *
+ * ##### Example
+ *
+ * ```php
+ * // write footer to stream
+ * $zip->finish();
+ * ```
+ */
+ public function finish(): int
+ {
+ $centralDirectoryStartOffsetOnDisk = $this->offset;
+ $sizeOfCentralDirectory = 0;
+
+ // add trailing cdr file records
+ foreach ($this->centralDirectoryRecords as $centralDirectoryRecord) {
+ $this->send($centralDirectoryRecord);
+ $sizeOfCentralDirectory += strlen($centralDirectoryRecord);
+ }
+
+ // Add 64bit headers (if applicable)
+ if (count($this->centralDirectoryRecords) >= 0xFFFF ||
+ $centralDirectoryStartOffsetOnDisk > 0xFFFFFFFF ||
+ $sizeOfCentralDirectory > 0xFFFFFFFF) {
+ if (!$this->enableZip64) {
+ throw new OverflowException();
+ }
+
+ $this->send(Zip64\EndOfCentralDirectory::generate(
+ versionMadeBy: self::ZIP_VERSION_MADE_BY,
+ versionNeededToExtract: Version::ZIP64->value,
+ numberOfThisDisk: 0,
+ numberOfTheDiskWithCentralDirectoryStart: 0,
+ numberOfCentralDirectoryEntriesOnThisDisk: count($this->centralDirectoryRecords),
+ numberOfCentralDirectoryEntries: count($this->centralDirectoryRecords),
+ sizeOfCentralDirectory: $sizeOfCentralDirectory,
+ centralDirectoryStartOffsetOnDisk: $centralDirectoryStartOffsetOnDisk,
+ extensibleDataSector: '',
+ ));
+
+ $this->send(Zip64\EndOfCentralDirectoryLocator::generate(
+ numberOfTheDiskWithZip64CentralDirectoryStart: 0x00,
+ zip64centralDirectoryStartOffsetOnDisk: $centralDirectoryStartOffsetOnDisk + $sizeOfCentralDirectory,
+ totalNumberOfDisks: 1,
+ ));
+ }
+
+ // add trailing cdr eof record
+ $numberOfCentralDirectoryEntries = min(count($this->centralDirectoryRecords), 0xFFFF);
+ $this->send(EndOfCentralDirectory::generate(
+ numberOfThisDisk: 0x00,
+ numberOfTheDiskWithCentralDirectoryStart: 0x00,
+ numberOfCentralDirectoryEntriesOnThisDisk: $numberOfCentralDirectoryEntries,
+ numberOfCentralDirectoryEntries: $numberOfCentralDirectoryEntries,
+ sizeOfCentralDirectory: min($sizeOfCentralDirectory, 0xFFFFFFFF),
+ centralDirectoryStartOffsetOnDisk: min($centralDirectoryStartOffsetOnDisk, 0xFFFFFFFF),
+ zipFileComment: $this->comment,
+ ));
+
+ $size = $this->offset;
+
+ // The End
+ $this->clear();
+
+ return $size;
+ }
+
+ /**
+ * @param StreamInterface|resource|null $outputStream
+ * @return resource
+ */
+ private static function normalizeStream($outputStream)
+ {
+ if ($outputStream instanceof StreamInterface) {
+ return StreamWrapper::getResource($outputStream);
+ }
+ if (is_resource($outputStream)) {
+ return $outputStream;
+ }
+ return fopen('php://output', 'wb');
+ }
+
+ /**
+ * Record sent bytes
+ */
+ private function recordSentBytes(int $sentBytes): void
+ {
+ $this->offset += $sentBytes;
+ }
+
+ /**
+ * Send string, sending HTTP headers if necessary.
+ * Flush output after write if configure option is set.
+ */
+ private function send(string $data): void
+ {
+ if (!$this->ready) {
+ throw new RuntimeException('Archive is already finished');
+ }
+
+ if ($this->operationMode === OperationMode::NORMAL && $this->sendHttpHeaders) {
+ $this->sendHttpHeaders();
+ $this->sendHttpHeaders = false;
+ }
+
+ $this->recordSentBytes(strlen($data));
+
+ if ($this->operationMode === OperationMode::NORMAL) {
+ if (fwrite($this->outputStream, $data) === false) {
+ throw new ResourceActionException('fwrite', $this->outputStream);
+ }
+
+ if ($this->flushOutput) {
+ // flush output buffer if it is on and flushable
+ $status = ob_get_status();
+ if (isset($status['flags']) && is_int($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
+ ob_flush();
+ }
+
+ // Flush system buffers after flushing userspace output buffer
+ flush();
+ }
+ }
+ }
+
+ /**
+ * Send HTTP headers for this stream.
+ */
+ private function sendHttpHeaders(): void
+ {
+ // grab content disposition
+ $disposition = $this->contentDisposition;
+
+ if ($this->outputName !== null) {
+ // Various different browsers dislike various characters here. Strip them all for safety.
+ $safeOutput = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->outputName));
+
+ // Check if we need to UTF-8 encode the filename
+ $urlencoded = rawurlencode($safeOutput);
+ $disposition .= "; filename*=UTF-8''{$urlencoded}";
+ }
+
+ $headers = [
+ 'Content-Type' => $this->contentType,
+ 'Content-Disposition' => $disposition,
+ 'Pragma' => 'public',
+ 'Cache-Control' => 'public, must-revalidate',
+ 'Content-Transfer-Encoding' => 'binary',
+ ];
+
+ foreach ($headers as $key => $val) {
+ ($this->httpHeaderCallback)("$key: $val");
+ }
+ }
+
+ /**
+ * Clear all internal variables. Note that the stream object is not
+ * usable after this.
+ */
+ private function clear(): void
+ {
+ $this->centralDirectoryRecords = [];
+ $this->offset = 0;
+
+ if ($this->operationMode === OperationMode::NORMAL) {
+ $this->ready = false;
+ $this->recordedSimulation = [];
+ } else {
+ $this->operationMode = OperationMode::NORMAL;
+ }
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php b/vendor/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php
new file mode 100644
index 000000000..bf621bc09
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zs;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class ExtendedInformationExtraField
+{
+ private const TAG = 0x5653;
+
+ public static function generate(): string
+ {
+ return PackField::pack(
+ new PackField(format: 'v', value: self::TAG),
+ new PackField(format: 'v', value: 0x0000),
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Assertions.php b/vendor/maennchen/zipstream-php/test/Assertions.php
new file mode 100644
index 000000000..8d7670eff
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Assertions.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+trait Assertions
+{
+ protected function assertFileContains(string $filePath, string $needle): void
+ {
+ $last = '';
+
+ $handle = fopen($filePath, 'r');
+ while (!feof($handle)) {
+ $line = fgets($handle, 1024);
+
+ if (str_contains($last . $line, $needle)) {
+ fclose($handle);
+ return;
+ }
+
+ $last = $line;
+ }
+
+ fclose($handle);
+
+ $this->fail("File {$filePath} must contain {$needle}");
+ }
+
+ protected function assertFileDoesNotContain(string $filePath, string $needle): void
+ {
+ $last = '';
+
+ $handle = fopen($filePath, 'r');
+ while (!feof($handle)) {
+ $line = fgets($handle, 1024);
+
+ if (str_contains($last . $line, $needle)) {
+ fclose($handle);
+
+ $this->fail("File {$filePath} must not contain {$needle}");
+ }
+
+ $last = $line;
+ }
+
+ fclose($handle);
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/CentralDirectoryFileHeaderTest.php b/vendor/maennchen/zipstream-php/test/CentralDirectoryFileHeaderTest.php
new file mode 100644
index 000000000..5457b4f44
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/CentralDirectoryFileHeaderTest.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use DateTimeImmutable;
+use PHPUnit\Framework\TestCase;
+use ZipStream\CentralDirectoryFileHeader;
+use ZipStream\CompressionMethod;
+
+class CentralDirectoryFileHeaderTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $dateTime = new DateTimeImmutable('2022-01-01 01:01:01Z');
+
+ $header = CentralDirectoryFileHeader::generate(
+ versionMadeBy: 0x603,
+ versionNeededToExtract: 0x002D,
+ generalPurposeBitFlag: 0x2222,
+ compressionMethod: CompressionMethod::DEFLATE,
+ lastModificationDateTime: $dateTime,
+ crc32: 0x11111111,
+ compressedSize: 0x77777777,
+ uncompressedSize: 0x99999999,
+ fileName: 'test.png',
+ extraField: 'some content',
+ fileComment: 'some comment',
+ diskNumberStart: 0,
+ internalFileAttributes: 0,
+ externalFileAttributes: 32,
+ relativeOffsetOfLocalHeader: 0x1234,
+ );
+
+ $this->assertSame(
+ bin2hex($header),
+ '504b0102' . // 4 bytes; central file header signature
+ '0306' . // 2 bytes; version made by
+ '2d00' . // 2 bytes; version needed to extract
+ '2222' . // 2 bytes; general purpose bit flag
+ '0800' . // 2 bytes; compression method
+ '2008' . // 2 bytes; last mod file time
+ '2154' . // 2 bytes; last mod file date
+ '11111111' . // 4 bytes; crc-32
+ '77777777' . // 4 bytes; compressed size
+ '99999999' . // 4 bytes; uncompressed size
+ '0800' . // 2 bytes; file name length (n)
+ '0c00' . // 2 bytes; extra field length (m)
+ '0c00' . // 2 bytes; file comment length (o)
+ '0000' . // 2 bytes; disk number start
+ '0000' . // 2 bytes; internal file attributes
+ '20000000' . // 4 bytes; external file attributes
+ '34120000' . // 4 bytes; relative offset of local header
+ '746573742e706e67' . // n bytes; file name
+ '736f6d6520636f6e74656e74' . // m bytes; extra field
+ '736f6d6520636f6d6d656e74' // o bytes; file comment
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/DataDescriptorTest.php b/vendor/maennchen/zipstream-php/test/DataDescriptorTest.php
new file mode 100644
index 000000000..cc886c74b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/DataDescriptorTest.php
@@ -0,0 +1,26 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\DataDescriptor;
+
+class DataDescriptorTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $this->assertSame(
+ bin2hex(DataDescriptor::generate(
+ crc32UncompressedData: 0x11111111,
+ compressedSize: 0x77777777,
+ uncompressedSize: 0x99999999,
+ )),
+ '504b0708' . // 4 bytes; Optional data descriptor signature = 0x08074b50
+ '11111111' . // 4 bytes; CRC-32 of uncompressed data
+ '77777777' . // 4 bytes; Compressed size
+ '99999999' // 4 bytes; Uncompressed size
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/EndOfCentralDirectoryTest.php b/vendor/maennchen/zipstream-php/test/EndOfCentralDirectoryTest.php
new file mode 100644
index 000000000..be0a90743
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/EndOfCentralDirectoryTest.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\EndOfCentralDirectory;
+
+class EndOfCentralDirectoryTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $this->assertSame(
+ bin2hex(EndOfCentralDirectory::generate(
+ numberOfThisDisk: 0x00,
+ numberOfTheDiskWithCentralDirectoryStart: 0x00,
+ numberOfCentralDirectoryEntriesOnThisDisk: 0x10,
+ numberOfCentralDirectoryEntries: 0x10,
+ sizeOfCentralDirectory: 0x22,
+ centralDirectoryStartOffsetOnDisk: 0x33,
+ zipFileComment: 'foo',
+ )),
+ '504b0506' . // 4 bytes; end of central dir signature 0x06054b50
+ '0000' . // 2 bytes; number of this disk
+ '0000' . // 2 bytes; number of the disk with the start of the central directory
+ '1000' . // 2 bytes; total number of entries in the central directory on this disk
+ '1000' . // 2 bytes; total number of entries in the central directory
+ '22000000' . // 4 bytes; size of the central directory
+ '33000000' . // 4 bytes; offset of start of central directory with respect to the starting disk number
+ '0300' . // 2 bytes; .ZIP file comment length
+ bin2hex('foo')
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/EndlessCycleStream.php b/vendor/maennchen/zipstream-php/test/EndlessCycleStream.php
new file mode 100644
index 000000000..d9e7df1fb
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/EndlessCycleStream.php
@@ -0,0 +1,104 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+
+class EndlessCycleStream implements StreamInterface
+{
+ private int $offset = 0;
+
+ public function __construct(private readonly string $toRepeat = '0') {}
+
+ public function __toString(): string
+ {
+ throw new RuntimeException('Infinite Stream!');
+ }
+
+ public function close(): void
+ {
+ $this->detach();
+ }
+
+ /**
+ * @return null
+ */
+ public function detach()
+ {
+ return;
+ }
+
+ public function getSize(): ?int
+ {
+ return null;
+ }
+
+ public function tell(): int
+ {
+ return $this->offset;
+ }
+
+ public function eof(): bool
+ {
+ return false;
+ }
+
+ public function isSeekable(): bool
+ {
+ return true;
+ }
+
+ public function seek(int $offset, int $whence = SEEK_SET): void
+ {
+ switch ($whence) {
+ case SEEK_SET:
+ $this->offset = $offset;
+ break;
+ case SEEK_CUR:
+ $this->offset += $offset;
+ break;
+ case SEEK_END:
+ throw new RuntimeException('Infinite Stream!');
+ break;
+ }
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ public function isWritable(): bool
+ {
+ return false;
+ }
+
+ public function write(string $string): int
+ {
+ throw new RuntimeException('Not writeable');
+ }
+
+ public function isReadable(): bool
+ {
+ return true;
+ }
+
+ public function read(int $length): string
+ {
+ $this->offset += $length;
+ return substr(str_repeat($this->toRepeat, (int) ceil($length / strlen($this->toRepeat))), 0, $length);
+ }
+
+ public function getContents(): string
+ {
+ throw new RuntimeException('Infinite Stream!');
+ }
+
+ public function getMetadata(?string $key = null): array|null
+ {
+ return $key !== null ? null : [];
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/FaultInjectionResource.php b/vendor/maennchen/zipstream-php/test/FaultInjectionResource.php
new file mode 100644
index 000000000..3d4440e8a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/FaultInjectionResource.php
@@ -0,0 +1,141 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+class FaultInjectionResource
+{
+ public const NAME = 'zipstream-php-test-broken-resource';
+
+ /** @var resource */
+ public $context;
+
+ private array $injectFaults;
+
+ private string $mode;
+
+ /**
+ * @return resource
+ */
+ public static function getResource(array $injectFaults)
+ {
+ self::register();
+
+ return fopen(self::NAME . '://foobar', 'rw+', false, self::createStreamContext($injectFaults));
+ }
+
+ public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool
+ {
+ $options = stream_context_get_options($this->context);
+
+ if (!isset($options[self::NAME]['injectFaults'])) {
+ return false;
+ }
+
+ $this->mode = $mode;
+ $this->injectFaults = $options[self::NAME]['injectFaults'];
+
+ if ($this->shouldFail(__FUNCTION__)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function stream_write(string $data)
+ {
+ if ($this->shouldFail(__FUNCTION__)) {
+ return false;
+ }
+ return true;
+ }
+
+ public function stream_eof()
+ {
+ return true;
+ }
+
+ public function stream_seek(int $offset, int $whence): bool
+ {
+ if ($this->shouldFail(__FUNCTION__)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function stream_tell(): int
+ {
+ if ($this->shouldFail(__FUNCTION__)) {
+ return false;
+ }
+
+ return 0;
+ }
+
+ public static function register(): void
+ {
+ if (!in_array(self::NAME, stream_get_wrappers(), true)) {
+ stream_wrapper_register(self::NAME, __CLASS__);
+ }
+ }
+
+ public function stream_stat(): array
+ {
+ static $modeMap = [
+ 'r' => 33060,
+ 'rb' => 33060,
+ 'r+' => 33206,
+ 'w' => 33188,
+ 'wb' => 33188,
+ ];
+
+ return [
+ 'dev' => 0,
+ 'ino' => 0,
+ 'mode' => $modeMap[$this->mode],
+ 'nlink' => 0,
+ 'uid' => 0,
+ 'gid' => 0,
+ 'rdev' => 0,
+ 'size' => 0,
+ 'atime' => 0,
+ 'mtime' => 0,
+ 'ctime' => 0,
+ 'blksize' => 0,
+ 'blocks' => 0,
+ ];
+ }
+
+ public function url_stat(string $path, int $flags): array
+ {
+ return [
+ 'dev' => 0,
+ 'ino' => 0,
+ 'mode' => 0,
+ 'nlink' => 0,
+ 'uid' => 0,
+ 'gid' => 0,
+ 'rdev' => 0,
+ 'size' => 0,
+ 'atime' => 0,
+ 'mtime' => 0,
+ 'ctime' => 0,
+ 'blksize' => 0,
+ 'blocks' => 0,
+ ];
+ }
+
+ private static function createStreamContext(array $injectFaults)
+ {
+ return stream_context_create([
+ self::NAME => ['injectFaults' => $injectFaults],
+ ]);
+ }
+
+ private function shouldFail(string $function): bool
+ {
+ return in_array($function, $this->injectFaults, true);
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/LocalFileHeaderTest.php b/vendor/maennchen/zipstream-php/test/LocalFileHeaderTest.php
new file mode 100644
index 000000000..196dd0fe3
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/LocalFileHeaderTest.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use DateTimeImmutable;
+use PHPUnit\Framework\TestCase;
+use ZipStream\CompressionMethod;
+use ZipStream\LocalFileHeader;
+
+class LocalFileHeaderTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $dateTime = new DateTimeImmutable('2022-01-01 01:01:01Z');
+
+ $header = LocalFileHeader::generate(
+ versionNeededToExtract: 0x002D,
+ generalPurposeBitFlag: 0x2222,
+ compressionMethod: CompressionMethod::DEFLATE,
+ lastModificationDateTime: $dateTime,
+ crc32UncompressedData: 0x11111111,
+ compressedSize: 0x77777777,
+ uncompressedSize: 0x99999999,
+ fileName: 'test.png',
+ extraField: 'some content'
+ );
+
+ $this->assertSame(
+ bin2hex((string) $header),
+ '504b0304' . // 4 bytes; Local file header signature
+ '2d00' . // 2 bytes; Version needed to extract (minimum)
+ '2222' . // 2 bytes; General purpose bit flag
+ '0800' . // 2 bytes; Compression method; e.g. none = 0, DEFLATE = 8
+ '2008' . // 2 bytes; File last modification time
+ '2154' . // 2 bytes; File last modification date
+ '11111111' . // 4 bytes; CRC-32 of uncompressed data
+ '77777777' . // 4 bytes; Compressed size (or 0xffffffff for ZIP64)
+ '99999999' . // 4 bytes; Uncompressed size (or 0xffffffff for ZIP64)
+ '0800' . // 2 bytes; File name length (n)
+ '0c00' . // 2 bytes; Extra field length (m)
+ '746573742e706e67' . // n bytes; File name
+ '736f6d6520636f6e74656e74' // m bytes; Extra field
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/PackFieldTest.php b/vendor/maennchen/zipstream-php/test/PackFieldTest.php
new file mode 100644
index 000000000..ecd66bac7
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/PackFieldTest.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use PHPUnit\Framework\TestCase;
+use RuntimeException;
+use ZipStream\PackField;
+
+class PackFieldTest extends TestCase
+{
+ public function testPacksFields(): void
+ {
+ $this->assertSame(
+ bin2hex(PackField::pack(new PackField(format: 'v', value: 0x1122))),
+ '2211',
+ );
+ }
+
+ public function testOverflow2(): void
+ {
+ $this->expectException(RuntimeException::class);
+
+ PackField::pack(new PackField(format: 'v', value: 0xFFFFF));
+ }
+
+ public function testOverflow4(): void
+ {
+ $this->expectException(RuntimeException::class);
+
+ PackField::pack(new PackField(format: 'V', value: 0xFFFFFFFFF));
+ }
+
+ public function testUnknownOperator(): void
+ {
+ $this->assertSame(
+ bin2hex(PackField::pack(new PackField(format: 'a', value: 0x1122))),
+ '34',
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/ResourceStream.php b/vendor/maennchen/zipstream-php/test/ResourceStream.php
new file mode 100644
index 000000000..752a1a357
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/ResourceStream.php
@@ -0,0 +1,159 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+
+/**
+ * @internal
+ */
+class ResourceStream implements StreamInterface
+{
+ public function __construct(
+ /**
+ * @var resource
+ */
+ private $stream
+ ) {}
+
+ public function __toString(): string
+ {
+ if ($this->isSeekable()) {
+ $this->seek(0);
+ }
+ return (string) stream_get_contents($this->stream);
+ }
+
+ public function close(): void
+ {
+ $stream = $this->detach();
+ if ($stream) {
+ fclose($stream);
+ }
+ }
+
+ public function detach()
+ {
+ $result = $this->stream;
+ // According to the interface, the stream is left in an unusable state;
+ /** @psalm-suppress PossiblyNullPropertyAssignmentValue */
+ $this->stream = null;
+ return $result;
+ }
+
+ public function seek(int $offset, int $whence = SEEK_SET): void
+ {
+ if (!$this->isSeekable()) {
+ throw new RuntimeException();
+ }
+ if (fseek($this->stream, $offset, $whence) !== 0) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException();
+ // @codeCoverageIgnoreEnd
+ }
+ }
+
+ public function isSeekable(): bool
+ {
+ return (bool) $this->getMetadata('seekable');
+ }
+
+ public function getMetadata(?string $key = null)
+ {
+ $metadata = stream_get_meta_data($this->stream);
+ return $key !== null ? @$metadata[$key] : $metadata;
+ }
+
+ public function getSize(): ?int
+ {
+ $stats = fstat($this->stream);
+ return $stats['size'];
+ }
+
+ public function tell(): int
+ {
+ $position = ftell($this->stream);
+ if ($position === false) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException();
+ // @codeCoverageIgnoreEnd
+ }
+ return $position;
+ }
+
+ public function eof(): bool
+ {
+ return feof($this->stream);
+ }
+
+ public function rewind(): void
+ {
+ $this->seek(0);
+ }
+
+ public function write(string $string): int
+ {
+ if (!$this->isWritable()) {
+ throw new RuntimeException();
+ }
+ if (fwrite($this->stream, $string) === false) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException();
+ // @codeCoverageIgnoreEnd
+ }
+ return strlen($string);
+ }
+
+ public function isWritable(): bool
+ {
+ $mode = $this->getMetadata('mode');
+ if (!is_string($mode)) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('Could not get stream mode from metadata!');
+ // @codeCoverageIgnoreEnd
+ }
+ return preg_match('/[waxc+]/', $mode) === 1;
+ }
+
+ public function read(int $length): string
+ {
+ if (!$this->isReadable()) {
+ throw new RuntimeException();
+ }
+ $result = fread($this->stream, $length);
+ if ($result === false) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException();
+ // @codeCoverageIgnoreEnd
+ }
+ return $result;
+ }
+
+ public function isReadable(): bool
+ {
+ $mode = $this->getMetadata('mode');
+ if (!is_string($mode)) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException('Could not get stream mode from metadata!');
+ // @codeCoverageIgnoreEnd
+ }
+ return preg_match('/[r+]/', $mode) === 1;
+ }
+
+ public function getContents(): string
+ {
+ if (!$this->isReadable()) {
+ throw new RuntimeException();
+ }
+ $result = stream_get_contents($this->stream);
+ if ($result === false) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException();
+ // @codeCoverageIgnoreEnd
+ }
+ return $result;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Tempfile.php b/vendor/maennchen/zipstream-php/test/Tempfile.php
new file mode 100644
index 000000000..7ef9c61f9
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Tempfile.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+trait Tempfile
+{
+ protected string|null $tempfile;
+
+ /**
+ * @var resource
+ */
+ protected $tempfileStream;
+
+ protected function setUp(): void
+ {
+ [$tempfile, $tempfileStream] = $this->getTmpFileStream();
+
+ $this->tempfile = $tempfile;
+ $this->tempfileStream = $tempfileStream;
+ }
+
+ protected function tearDown(): void
+ {
+ unlink($this->tempfile);
+ if (is_resource($this->tempfileStream)) {
+ fclose($this->tempfileStream);
+ }
+
+ $this->tempfile = null;
+ $this->tempfileStream = null;
+ }
+
+ protected function getTmpFileStream(): array
+ {
+ $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+ $stream = fopen($tmp, 'wb+');
+
+ return [$tmp, $stream];
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/TimeTest.php b/vendor/maennchen/zipstream-php/test/TimeTest.php
new file mode 100644
index 000000000..61cfe0388
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/TimeTest.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use DateTimeImmutable;
+use PHPUnit\Framework\TestCase;
+use ZipStream\Exception\DosTimeOverflowException;
+use ZipStream\Time;
+
+class TimeTest extends TestCase
+{
+ public function testNormalDateToDosTime(): void
+ {
+ $this->assertSame(
+ Time::dateTimeToDosTime(new DateTimeImmutable('2014-11-17T17:46:08Z')),
+ 1165069764
+ );
+
+ // January 1 1980 - DOS Epoch.
+ $this->assertSame(
+ Time::dateTimeToDosTime(new DateTimeImmutable('1980-01-01T00:00:00+00:00')),
+ 2162688
+ );
+
+ // Local timezone different than UTC.
+ $prevLocalTimezone = date_default_timezone_get();
+ date_default_timezone_set('Europe/Berlin');
+ $this->assertSame(
+ Time::dateTimeToDosTime(new DateTimeImmutable('1980-01-01T00:00:00+00:00')),
+ 2162688
+ );
+ date_default_timezone_set($prevLocalTimezone);
+ }
+
+ public function testTooEarlyDateToDosTime(): void
+ {
+ $this->expectException(DosTimeOverflowException::class);
+
+ // January 1 1980 is the minimum DOS Epoch.
+ Time::dateTimeToDosTime(new DateTimeImmutable('1970-01-01T00:00:00+00:00'));
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Util.php b/vendor/maennchen/zipstream-php/test/Util.php
new file mode 100644
index 000000000..86592b429
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Util.php
@@ -0,0 +1,127 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use function fgets;
+use function pclose;
+use function popen;
+use function preg_match;
+
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
+
+use function strtolower;
+
+use ZipArchive;
+
+trait Util
+{
+ protected function cmdExists(string $command): bool
+ {
+ if (strtolower(\substr(PHP_OS, 0, 3)) === 'win') {
+ $fp = popen("where $command", 'r');
+ $result = fgets($fp, 255);
+ $exists = !preg_match('#Could not find files#', $result);
+ pclose($fp);
+ } else { // non-Windows
+ $fp = popen("which $command", 'r');
+ $result = fgets($fp, 255);
+ $exists = !empty($result);
+ pclose($fp);
+ }
+
+ return $exists;
+ }
+
+ protected function dumpZipContents(string $path): string
+ {
+ if (!$this->cmdExists('hexdump')) {
+ return '';
+ }
+
+ $output = [];
+
+ if (!exec("hexdump -C \"$path\" | head -n 50", $output)) {
+ return '';
+ }
+
+ return "\nHexdump:\n" . implode("\n", $output);
+ }
+
+ protected function validateAndExtractZip(string $zipPath): string
+ {
+ $tmpDir = $this->getTmpDir();
+
+ $zipArchive = new ZipArchive();
+ $result = $zipArchive->open($zipPath);
+
+ if ($result !== true) {
+ $codeName = $this->zipArchiveOpenErrorCodeName($result);
+ $debugInformation = $this->dumpZipContents($zipPath);
+
+ $this->fail("Failed to open {$zipPath}. Code: $result ($codeName)$debugInformation");
+
+ return $tmpDir;
+ }
+
+ $this->assertSame(0, $zipArchive->status);
+ $this->assertSame(0, $zipArchive->statusSys);
+
+ $zipArchive->extractTo($tmpDir);
+ $zipArchive->close();
+
+ return $tmpDir;
+ }
+
+ protected function zipArchiveOpenErrorCodeName(int $code): string
+ {
+ switch ($code) {
+ case ZipArchive::ER_EXISTS: return 'ER_EXISTS';
+ case ZipArchive::ER_INCONS: return 'ER_INCONS';
+ case ZipArchive::ER_INVAL: return 'ER_INVAL';
+ case ZipArchive::ER_MEMORY: return 'ER_MEMORY';
+ case ZipArchive::ER_NOENT: return 'ER_NOENT';
+ case ZipArchive::ER_NOZIP: return 'ER_NOZIP';
+ case ZipArchive::ER_OPEN: return 'ER_OPEN';
+ case ZipArchive::ER_READ: return 'ER_READ';
+ case ZipArchive::ER_SEEK: return 'ER_SEEK';
+ default: return 'unknown';
+ }
+ }
+
+ protected function getTmpDir(): string
+ {
+ $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+ unlink($tmp);
+ mkdir($tmp) or $this->fail('Failed to make directory');
+
+ return $tmp;
+ }
+
+ /**
+ * @return string[]
+ */
+ protected function getRecursiveFileList(string $path, bool $includeDirectories = false): 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) && !$includeDirectories) {
+ continue;
+ }
+
+ $data[] = substr($filePath, $pathLen + 1);
+ }
+
+ sort($data);
+
+ return $data;
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/DataDescriptorTest.php b/vendor/maennchen/zipstream-php/test/Zip64/DataDescriptorTest.php
new file mode 100644
index 000000000..49fb2ccb2
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/DataDescriptorTest.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\DataDescriptor;
+
+class DataDescriptorTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $descriptor = DataDescriptor::generate(
+ crc32UncompressedData: 0x11111111,
+ compressedSize: (0x77777777 << 32) + 0x66666666,
+ uncompressedSize: (0x99999999 << 32) + 0x88888888,
+ );
+
+ $this->assertSame(
+ bin2hex($descriptor),
+ '504b0708' . // 4 bytes; Optional data descriptor signature = 0x08074b50
+ '11111111' . // 4 bytes; CRC-32 of uncompressed data
+ '6666666677777777' . // 8 bytes; Compressed size
+ '8888888899999999' // 8 bytes; Uncompressed size
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.php b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.php
new file mode 100644
index 000000000..271a29862
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\EndOfCentralDirectoryLocator;
+
+class EndOfCentralDirectoryLocatorTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $descriptor = EndOfCentralDirectoryLocator::generate(
+ numberOfTheDiskWithZip64CentralDirectoryStart: 0x11111111,
+ zip64centralDirectoryStartOffsetOnDisk: (0x22222222 << 32) + 0x33333333,
+ totalNumberOfDisks: 0x44444444,
+ );
+
+ $this->assertSame(
+ bin2hex($descriptor),
+ '504b0607' . // 4 bytes; zip64 end of central dir locator signature - 0x07064b50
+ '11111111' . // 4 bytes; number of the disk with the start of the zip64 end of central directory
+ '3333333322222222' . // 28 bytes; relative offset of the zip64 end of central directory record
+ '44444444' // 4 bytes;total number of disks
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryTest.php b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryTest.php
new file mode 100644
index 000000000..b86fb1781
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryTest.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\EndOfCentralDirectory;
+
+class EndOfCentralDirectoryTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $descriptor = EndOfCentralDirectory::generate(
+ versionMadeBy: 0x3333,
+ versionNeededToExtract: 0x4444,
+ numberOfThisDisk: 0x55555555,
+ numberOfTheDiskWithCentralDirectoryStart: 0x66666666,
+ numberOfCentralDirectoryEntriesOnThisDisk: (0x77777777 << 32) + 0x88888888,
+ numberOfCentralDirectoryEntries: (0x99999999 << 32) + 0xAAAAAAAA,
+ sizeOfCentralDirectory: (0xBBBBBBBB << 32) + 0xCCCCCCCC,
+ centralDirectoryStartOffsetOnDisk: (0xDDDDDDDD << 32) + 0xEEEEEEEE,
+ extensibleDataSector: 'foo',
+ );
+
+ $this->assertSame(
+ bin2hex($descriptor),
+ '504b0606' . // 4 bytes;zip64 end of central dir signature - 0x06064b50
+ '2f00000000000000' . // 8 bytes; size of zip64 end of central directory record
+ '3333' . // 2 bytes; version made by
+ '4444' . // 2 bytes; version needed to extract
+ '55555555' . // 4 bytes; number of this disk
+ '66666666' . // 4 bytes; number of the disk with the start of the central directory
+ '8888888877777777' . // 8 bytes; total number of entries in the central directory on this disk
+ 'aaaaaaaa99999999' . // 8 bytes; total number of entries in the central directory
+ 'ccccccccbbbbbbbb' . // 8 bytes; size of the central directory
+ 'eeeeeeeedddddddd' . // 8 bytes; offset of start of central directory with respect to the starting disk number
+ bin2hex('foo')
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.php b/vendor/maennchen/zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.php
new file mode 100644
index 000000000..904783d86
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\ExtendedInformationExtraField;
+
+class ExtendedInformationExtraFieldTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $extraField = ExtendedInformationExtraField::generate(
+ originalSize: (0x77777777 << 32) + 0x66666666,
+ compressedSize: (0x99999999 << 32) + 0x88888888,
+ relativeHeaderOffset: (0x22222222 << 32) + 0x11111111,
+ diskStartNumber: 0x33333333,
+ );
+
+ $this->assertSame(
+ bin2hex($extraField),
+ '0100' . // 2 bytes; Tag for this "extra" block type
+ '1c00' . // 2 bytes; Size of this "extra" block
+ '6666666677777777' . // 8 bytes; Original uncompressed file size
+ '8888888899999999' . // 8 bytes; Size of compressed data
+ '1111111122222222' . // 8 bytes; Offset of local header record
+ '33333333' // 4 bytes; Number of the disk on which this file starts
+ );
+ }
+
+ public function testSerializesEmptyCorrectly(): void
+ {
+ $extraField = ExtendedInformationExtraField::generate();
+
+ $this->assertSame(
+ bin2hex($extraField),
+ '0100' . // 2 bytes; Tag for this "extra" block type
+ '0000' // 2 bytes; Size of this "extra" block
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/ZipStreamTest.php b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
new file mode 100644
index 000000000..9b10ba65d
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
@@ -0,0 +1,1195 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use DateTimeImmutable;
+use GuzzleHttp\Psr7\Response;
+use GuzzleHttp\Psr7\StreamWrapper;
+use org\bovigo\vfs\vfsStream;
+use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+use ZipArchive;
+use ZipStream\CompressionMethod;
+use ZipStream\Exception\FileNotFoundException;
+use ZipStream\Exception\FileNotReadableException;
+use ZipStream\Exception\FileSizeIncorrectException;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Exception\ResourceActionException;
+use ZipStream\Exception\SimulationFileUnknownException;
+use ZipStream\Exception\StreamNotReadableException;
+use ZipStream\Exception\StreamNotSeekableException;
+use ZipStream\OperationMode;
+use ZipStream\PackField;
+use ZipStream\ZipStream;
+
+class ZipStreamTest extends TestCase
+{
+ use Util;
+ use Assertions;
+ use Tempfile;
+
+ public function testAddFile(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $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
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $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: ÁÍŰŐÜÖÚÓÉ';
+
+ $zip->addFile(fileName: $name, data: $content, comment: $comment);
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame([$name], $files);
+ $this->assertStringEqualsFile($tmpDir . '/' . $name, $content);
+
+ $zipArchive = new ZipArchive();
+ $zipArchive->open($this->tempfile);
+ $this->assertSame($comment, $zipArchive->getCommentName($name));
+ }
+
+ public function testAddFileUtf8NameNonUtfComment(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $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');
+
+ $zip->addFile(fileName: $name, data: $content, comment: $comment);
+
+ $zip->finish();
+
+ $zipArch = new ZipArchive();
+ $zipArch->open($this->tempfile);
+ $this->assertSame($guessComment, $zipArch->getCommentName($name));
+ $this->assertSame($comment, $zipArch->getCommentName($name, ZipArchive::FL_ENC_RAW));
+ }
+
+ public function testAddFileWithStorageMethod(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addFile(fileName: 'sample.txt', data: 'Sample String Data', compressionMethod: CompressionMethod::STORE);
+ $zip->addFile(fileName: 'test/sample.txt', data: 'More Simple Sample Data');
+ $zip->finish();
+
+ $zipArchive = new ZipArchive();
+ $zipArchive->open($this->tempfile);
+
+ $sample1 = $zipArchive->statName('sample.txt');
+ $sample12 = $zipArchive->statName('test/sample.txt');
+ $this->assertSame($sample1['comp_method'], CompressionMethod::STORE->value);
+ $this->assertSame($sample12['comp_method'], CompressionMethod::DEFLATE->value);
+
+ $zipArchive->close();
+ }
+
+ public function testAddFileFromPath(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'Sample String Data');
+ fclose($streamExample);
+ $zip->addFileFromPath(fileName: 'sample.txt', path: $tmpExample);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'More Simple Sample Data');
+ fclose($streamExample);
+ $zip->addFileFromPath(fileName: 'test/sample.txt', path: $tmpExample);
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $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');
+
+ unlink($tmpExample);
+ }
+
+ public function testAddFileFromPathFileNotFoundException(): void
+ {
+ $this->expectException(FileNotFoundException::class);
+
+ // Get ZipStream Object
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ // Trigger error by adding a file which doesn't exist
+ $zip->addFileFromPath(fileName: 'foobar.php', path: '/foo/bar/foobar.php');
+ }
+
+ public function testAddFileFromPathFileNotReadableException(): void
+ {
+ $this->expectException(FileNotReadableException::class);
+
+ // 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');
+
+ // Get ZipStream Object
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addFileFromPath('foo.txt', $file->url());
+ }
+
+ public function testAddFileFromPathWithStorageMethod(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'Sample String Data');
+ fclose($streamExample);
+ $zip->addFileFromPath(fileName: 'sample.txt', path: $tmpExample, compressionMethod: CompressionMethod::STORE);
+
+ [$tmpExample, $streamExample] = $this->getTmpFileStream();
+ fwrite($streamExample, 'More Simple Sample Data');
+ fclose($streamExample);
+ $zip->addFileFromPath('test/sample.txt', $tmpExample);
+
+ $zip->finish();
+
+ $zipArchive = new ZipArchive();
+ $zipArchive->open($this->tempfile);
+
+ $sample1 = $zipArchive->statName('sample.txt');
+ $this->assertSame(CompressionMethod::STORE->value, $sample1['comp_method']);
+
+ $sample2 = $zipArchive->statName('test/sample.txt');
+ $this->assertSame(CompressionMethod::DEFLATE->value, $sample2['comp_method']);
+
+ $zipArchive->close();
+ }
+
+ public function testAddLargeFileFromPath(): void
+ {
+ foreach ([CompressionMethod::DEFLATE, CompressionMethod::STORE] as $compressionMethod) {
+ foreach ([false, true] as $zeroHeader) {
+ foreach ([false, true] as $zip64) {
+ if ($zeroHeader && $compressionMethod === CompressionMethod::DEFLATE) {
+ continue;
+ }
+ $this->addLargeFileFileFromPath(
+ compressionMethod: $compressionMethod,
+ zeroHeader: $zeroHeader,
+ zip64: $zip64
+ );
+ }
+ }
+ }
+ }
+
+ public function testAddFileFromStream(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ // 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);
+
+ $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();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $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 testAddFileFromStreamUnreadableInput(): void
+ {
+ $this->expectException(StreamNotReadableException::class);
+
+ [$tmpInput] = $this->getTmpFileStream();
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $streamUnreadable = fopen($tmpInput, 'w');
+
+ $zip->addFileFromStream('sample.json', $streamUnreadable);
+ }
+
+ public function testAddFileFromStreamBrokenOutputWrite(): void
+ {
+ $this->expectException(ResourceActionException::class);
+
+ $outputStream = FaultInjectionResource::getResource(['stream_write']);
+
+ $zip = new ZipStream(
+ outputStream: $outputStream,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addFile('sample.txt', 'foobar');
+ }
+
+ public function testAddFileFromStreamBrokenInputRewind(): void
+ {
+ $this->expectException(ResourceActionException::class);
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ defaultEnableZeroHeader: false,
+ );
+
+ $fileStream = FaultInjectionResource::getResource(['stream_seek']);
+
+ $zip->addFileFromStream('sample.txt', $fileStream, maxSize: 0);
+ }
+
+ public function testAddFileFromStreamUnseekableInputWithoutZeroHeader(): void
+ {
+ $this->expectException(StreamNotSeekableException::class);
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ defaultEnableZeroHeader: false,
+ );
+
+ if (file_exists('/dev/null')) {
+ $streamUnseekable = fopen('/dev/null', 'w+');
+ } elseif (file_exists('NUL')) {
+ $streamUnseekable = fopen('NUL', 'w+');
+ } else {
+ $this->markTestSkipped('Needs file /dev/null');
+ }
+
+ $zip->addFileFromStream('sample.txt', $streamUnseekable, maxSize: 2);
+ }
+
+ public function testAddFileFromStreamUnseekableInputWithZeroHeader(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ defaultEnableZeroHeader: true,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ );
+
+ $streamUnseekable = StreamWrapper::getResource(new class ('test') extends EndlessCycleStream {
+ public function isSeekable(): bool
+ {
+ return false;
+ }
+
+ public function seek(int $offset, int $whence = SEEK_SET): void
+ {
+ throw new RuntimeException('Not seekable');
+ }
+ });
+
+ $zip->addFileFromStream('sample.txt', $streamUnseekable, maxSize: 7);
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.txt'], $files);
+
+ $this->assertSame(filesize($tmpDir . '/sample.txt'), 7);
+ }
+
+ public function testAddFileFromStreamWithStorageMethod(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $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, compressionMethod: CompressionMethod::STORE);
+ 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, compressionMethod: CompressionMethod::DEFLATE);
+ fclose($streamExample2);
+
+ $zip->finish();
+
+ $zipArchive = new ZipArchive();
+ $zipArchive->open($this->tempfile);
+
+ $sample1 = $zipArchive->statName('sample.txt');
+ $this->assertSame(CompressionMethod::STORE->value, $sample1['comp_method']);
+
+ $sample2 = $zipArchive->statName('test/sample.txt');
+ $this->assertSame(CompressionMethod::DEFLATE->value, $sample2['comp_method']);
+
+ $zipArchive->close();
+ }
+
+ public function testAddFileFromPsr7Stream(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $body = 'Sample String Data';
+ $response = new Response(200, [], $body);
+
+ $zip->addFileFromPsr7Stream('sample.json', $response->getBody());
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ /**
+ * @group slow
+ */
+ public function testAddLargeFileFromPsr7Stream(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: true,
+ );
+
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample.json',
+ stream: new EndlessCycleStream('0'),
+ maxSize: 0x100000000,
+ compressionMethod: CompressionMethod::STORE,
+ lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+ );
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.json'], $files);
+ $this->assertFileIsReadable($tmpDir . '/sample.json');
+ $this->assertStringStartsWith('000000', file_get_contents(filename: $tmpDir . '/sample.json', length: 20));
+ }
+
+ public function testContinueFinishedZip(): void
+ {
+ $this->expectException(RuntimeException::class);
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+ $zip->finish();
+
+ $zip->addFile('sample.txt', '1234');
+ }
+
+ /**
+ * @group slow
+ */
+ public function testManyFilesWithoutZip64(): void
+ {
+ $this->expectException(OverflowException::class);
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: false,
+ );
+
+ for ($i = 0; $i <= 0xFFFF; $i++) {
+ $zip->addFile('sample' . $i, '');
+ }
+
+ $zip->finish();
+ }
+
+ /**
+ * @group slow
+ */
+ public function testManyFilesWithZip64(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: true,
+ );
+
+ for ($i = 0; $i <= 0xFFFF; $i++) {
+ $zip->addFile('sample' . $i, '');
+ }
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertSame(count($files), 0x10000);
+ }
+
+ /**
+ * @group slow
+ */
+ public function testLongZipWithout64(): void
+ {
+ $this->expectException(OverflowException::class);
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: false,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ );
+
+ for ($i = 0; $i < 4; $i++) {
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample' . $i,
+ stream: new EndlessCycleStream('0'),
+ maxSize: 0xFFFFFFFF,
+ compressionMethod: CompressionMethod::STORE,
+ lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+ );
+ }
+ }
+
+ /**
+ * @group slow
+ */
+ public function testLongZipWith64(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: true,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ );
+
+ for ($i = 0; $i < 4; $i++) {
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample' . $i,
+ stream: new EndlessCycleStream('0'),
+ maxSize: 0x5FFFFFFF,
+ compressionMethod: CompressionMethod::STORE,
+ lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+ );
+ }
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample0', 'sample1', 'sample2', 'sample3'], $files);
+ }
+
+ /**
+ * @group slow
+ */
+ public function testAddLargeFileWithoutZip64WithZeroHeader(): void
+ {
+ $this->expectException(OverflowException::class);
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: false,
+ defaultEnableZeroHeader: true,
+ );
+
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample.json',
+ stream: new EndlessCycleStream('0'),
+ maxSize: 0x100000000,
+ compressionMethod: CompressionMethod::STORE,
+ lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+ );
+ }
+
+ /**
+ * @group slow
+ */
+ public function testAddsZip64HeaderWhenNeeded(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: true,
+ defaultEnableZeroHeader: false,
+ );
+
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample.json',
+ stream: new EndlessCycleStream('0'),
+ maxSize: 0x100000000,
+ compressionMethod: CompressionMethod::STORE,
+ lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+ );
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertSame(['sample.json'], $files);
+ $this->assertFileContains($this->tempfile, PackField::pack(
+ new PackField(format: 'V', value: 0x06064b50)
+ ));
+ }
+
+ /**
+ * @group slow
+ */
+ public function testDoesNotAddZip64HeaderWhenNotNeeded(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: true,
+ defaultEnableZeroHeader: false,
+ );
+
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample.json',
+ stream: new EndlessCycleStream('0'),
+ maxSize: 0x10,
+ compressionMethod: CompressionMethod::STORE,
+ lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+ );
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertSame(['sample.json'], $files);
+ $this->assertFileDoesNotContain($this->tempfile, PackField::pack(
+ new PackField(format: 'V', value: 0x06064b50)
+ ));
+ }
+
+ /**
+ * @group slow
+ */
+ public function testAddLargeFileWithoutZip64WithoutZeroHeader(): void
+ {
+ $this->expectException(OverflowException::class);
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ enableZip64: false,
+ defaultEnableZeroHeader: false,
+ );
+
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample.json',
+ stream: new EndlessCycleStream('0'),
+ maxSize: 0x100000000,
+ compressionMethod: CompressionMethod::STORE,
+ lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+ );
+ }
+
+ public function testAddFileFromPsr7StreamWithOutputToPsr7Stream(): void
+ {
+ $psr7OutputStream = new ResourceStream($this->tempfileStream);
+
+ $zip = new ZipStream(
+ outputStream: $psr7OutputStream,
+ sendHttpHeaders: false,
+ );
+
+ $body = 'Sample String Data';
+ $response = new Response(200, [], $body);
+
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample.json',
+ stream: $response->getBody(),
+ compressionMethod: CompressionMethod::STORE,
+ );
+ $zip->finish();
+ $psr7OutputStream->close();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+ $files = $this->getRecursiveFileList($tmpDir);
+
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testAddFileFromPsr7StreamWithFileSizeSet(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $body = 'Sample String Data';
+ $fileSize = strlen($body);
+ // Add fake padding
+ $fakePadding = "\0\0\0\0\0\0";
+ $response = new Response(200, [], $body . $fakePadding);
+
+ $zip->addFileFromPsr7Stream(
+ fileName: 'sample.json',
+ stream: $response->getBody(),
+ compressionMethod: CompressionMethod::STORE,
+ maxSize: $fileSize
+ );
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['sample.json'], $files);
+ $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
+ }
+
+ public function testCreateArchiveHeaders(): void
+ {
+ $headers = [];
+
+ $httpHeaderCallback = function (string $header) use (&$headers) {
+ $headers[] = $header;
+ };
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: true,
+ outputName: 'example.zip',
+ httpHeaderCallback: $httpHeaderCallback,
+ );
+
+ $zip->addFile(
+ fileName: 'sample.json',
+ data: 'foo',
+ );
+ $zip->finish();
+
+ $this->assertContains('Content-Type: application/x-zip', $headers);
+ $this->assertContains("Content-Disposition: attachment; filename*=UTF-8''example.zip", $headers);
+ $this->assertContains('Pragma: public', $headers);
+ $this->assertContains('Cache-Control: public, must-revalidate', $headers);
+ $this->assertContains('Content-Transfer-Encoding: binary', $headers);
+ }
+
+ public function testCreateArchiveWithFlushOptionSet(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ flushOutput: true,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $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());
+
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ flushOutput: true,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+ $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();
+ }
+
+ public function testAddEmptyDirectory(): void
+ {
+ $zip = new ZipStream(
+ outputStream: $this->tempfileStream,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addDirectory('foo');
+
+ $zip->finish();
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir, includeDirectories: true);
+
+ $this->assertContains('foo', $files);
+
+ $this->assertFileExists($tmpDir . DIRECTORY_SEPARATOR . 'foo');
+ $this->assertDirectoryExists($tmpDir . DIRECTORY_SEPARATOR . 'foo');
+ }
+
+ public function testAddFileSimulate(): void
+ {
+ $create = function (OperationMode $operationMode): int {
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: $operationMode,
+ defaultEnableZeroHeader: true,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ return $zip->finish();
+ };
+
+
+ $sizeExpected = $create(OperationMode::NORMAL);
+ $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+ $this->assertEquals($sizeExpected, $sizeActual);
+ }
+
+ public function testAddFileSimulateWithMaxSize(): void
+ {
+ $create = function (OperationMode $operationMode): int {
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: $operationMode,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ defaultEnableZeroHeader: true,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data', maxSize: 0);
+
+ return $zip->finish();
+ };
+
+
+ $sizeExpected = $create(OperationMode::NORMAL);
+ $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+ $this->assertEquals($sizeExpected, $sizeActual);
+ }
+
+ public function testAddFileSimulateWithFstat(): void
+ {
+ $create = function (OperationMode $operationMode): int {
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: $operationMode,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ defaultEnableZeroHeader: true,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+ return $zip->finish();
+ };
+
+
+ $sizeExpected = $create(OperationMode::NORMAL);
+ $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+ $this->assertEquals($sizeExpected, $sizeActual);
+ }
+
+ public function testAddFileSimulateWithExactSizeZero(): void
+ {
+ $create = function (OperationMode $operationMode): int {
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: $operationMode,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ defaultEnableZeroHeader: true,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data', exactSize: 18);
+
+ return $zip->finish();
+ };
+
+
+ $sizeExpected = $create(OperationMode::NORMAL);
+ $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+ $this->assertEquals($sizeExpected, $sizeActual);
+ }
+
+ public function testAddFileSimulateWithExactSizeInitial(): void
+ {
+ $create = function (OperationMode $operationMode): int {
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: $operationMode,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ defaultEnableZeroHeader: false,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data', exactSize: 18);
+
+ return $zip->finish();
+ };
+
+ $sizeExpected = $create(OperationMode::NORMAL);
+ $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+ $this->assertEquals($sizeExpected, $sizeActual);
+ }
+
+ public function testAddFileSimulateWithZeroSizeInFstat(): void
+ {
+ $create = function (OperationMode $operationMode): int {
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: $operationMode,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ defaultEnableZeroHeader: false,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->addFileFromPsr7Stream('sample.txt', new class implements StreamInterface {
+ public $pos = 0;
+
+ public function __toString(): string
+ {
+ return 'test';
+ }
+
+ public function close(): void {}
+
+ public function detach() {}
+
+ public function getSize(): ?int
+ {
+ return null;
+ }
+
+ public function tell(): int
+ {
+ return $this->pos;
+ }
+
+ public function eof(): bool
+ {
+ return $this->pos >= 4;
+ }
+
+ public function isSeekable(): bool
+ {
+ return true;
+ }
+
+ public function seek(int $offset, int $whence = SEEK_SET): void
+ {
+ $this->pos = $offset;
+ }
+
+ public function rewind(): void
+ {
+ $this->pos = 0;
+ }
+
+ public function isWritable(): bool
+ {
+ return false;
+ }
+
+ public function write(string $string): int
+ {
+ return 0;
+ }
+
+ public function isReadable(): bool
+ {
+ return true;
+ }
+
+ public function read(int $length): string
+ {
+ $data = substr('test', $this->pos, $length);
+ $this->pos += strlen($data);
+ return $data;
+ }
+
+ public function getContents(): string
+ {
+ return $this->read(4);
+ }
+
+ public function getMetadata(?string $key = null)
+ {
+ return $key !== null ? null : [];
+ }
+ });
+
+ return $zip->finish();
+ };
+
+ $sizeExpected = $create(OperationMode::NORMAL);
+ $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+
+ $this->assertEquals($sizeExpected, $sizeActual);
+ }
+
+ public function testAddFileSimulateWithWrongExactSize(): void
+ {
+ $this->expectException(FileSizeIncorrectException::class);
+
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: OperationMode::SIMULATE_LAX,
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data', exactSize: 1000);
+ }
+
+ public function testAddFileSimulateStrictZero(): void
+ {
+ $this->expectException(SimulationFileUnknownException::class);
+
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: OperationMode::SIMULATE_STRICT,
+ defaultEnableZeroHeader: true
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ }
+
+ public function testAddFileSimulateStrictInitial(): void
+ {
+ $this->expectException(SimulationFileUnknownException::class);
+
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: OperationMode::SIMULATE_STRICT,
+ defaultEnableZeroHeader: false
+ );
+
+ $zip->addFile('sample.txt', 'Sample String Data');
+ }
+
+ public function testAddFileCallbackStrict(): void
+ {
+ $this->expectException(SimulationFileUnknownException::class);
+
+ $zip = new ZipStream(
+ sendHttpHeaders: false,
+ operationMode: OperationMode::SIMULATE_STRICT,
+ defaultEnableZeroHeader: false
+ );
+
+ $zip->addFileFromCallback('sample.txt', callback: function () {
+ return '';
+ });
+ }
+
+ public function testAddFileCallbackLax(): void
+ {
+ $zip = new ZipStream(
+ operationMode: OperationMode::SIMULATE_LAX,
+ defaultEnableZeroHeader: false,
+ sendHttpHeaders: false,
+ );
+
+ $zip->addFileFromCallback('sample.txt', callback: function () {
+ return 'Sample String Data';
+ });
+
+ $size = $zip->finish();
+
+ $this->assertEquals($size, 142);
+ }
+
+ public function testExecuteSimulation(): void
+ {
+ $zip = new ZipStream(
+ operationMode: OperationMode::SIMULATE_STRICT,
+ defaultCompressionMethod: CompressionMethod::STORE,
+ defaultEnableZeroHeader: false,
+ sendHttpHeaders: false,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->addFileFromCallback(
+ 'sample.txt',
+ exactSize: 18,
+ callback: function () {
+ return 'Sample String Data';
+ }
+ );
+
+ $zip->addFileFromCallback(
+ '.gitkeep',
+ exactSize: 0,
+ callback: function () {
+ return '';
+ }
+ );
+
+ $size = $zip->finish();
+
+ $this->assertEquals(filesize($this->tempfile), 0);
+
+ $zip->executeSimulation();
+
+ clearstatcache();
+
+ $this->assertEquals(filesize($this->tempfile), $size);
+
+ $tmpDir = $this->validateAndExtractZip($this->tempfile);
+
+ $files = $this->getRecursiveFileList($tmpDir);
+ $this->assertSame(['.gitkeep', 'sample.txt'], $files);
+ }
+
+ public function testExecuteSimulationBeforeFinish(): void
+ {
+ $this->expectException(RuntimeException::class);
+
+ $zip = new ZipStream(
+ operationMode: OperationMode::SIMULATE_LAX,
+ defaultEnableZeroHeader: false,
+ sendHttpHeaders: false,
+ outputStream: $this->tempfileStream,
+ );
+
+ $zip->executeSimulation();
+ }
+
+ private function addLargeFileFileFromPath(CompressionMethod $compressionMethod, $zeroHeader, $zip64): void
+ {
+ [$tmp, $stream] = $this->getTmpFileStream();
+
+ $zip = new ZipStream(
+ outputStream: $stream,
+ sendHttpHeaders: false,
+ defaultEnableZeroHeader: $zeroHeader,
+ enableZip64: $zip64,
+ );
+
+ [$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: {$compressionMethod->value}");
+
+ unlink($tmp);
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.php b/vendor/maennchen/zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.php
new file mode 100644
index 000000000..2b8dbed4a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zs;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zs\ExtendedInformationExtraField;
+
+class ExtendedInformationExtraFieldTest extends TestCase
+{
+ public function testSerializesCorrectly(): void
+ {
+ $extraField = ExtendedInformationExtraField::generate();
+
+ $this->assertSame(
+ bin2hex((string) $extraField),
+ '5356' . // 2 bytes; Tag for this "extra" block type
+ '0000' // 2 bytes; TODO: Document
+ );
+ }
+}
diff --git a/vendor/maennchen/zipstream-php/test/bootstrap.php b/vendor/maennchen/zipstream-php/test/bootstrap.php
new file mode 100644
index 000000000..13c7a0e6c
--- /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';