1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-08-15 19:55:39 +02:00

68 Commits

Author SHA1 Message Date
Ne-Lexa
330c724ce6 fix psalm 2022-06-19 11:27:45 +03:00
Ne-Lexa
4130739a21 Merge tag '4.0.2' into develop
Tagging version 4.0.2 4.0.2
2022-06-17 14:17:46 +03:00
Ne-Lexa
88a1b6549b Merge branch 'release/4.0.2' 2022-06-17 14:17:46 +03:00
Ne-Lexa
9e1d4a1d7a Merge pull request #82 from mbardelmeijer/patch-1
Remove ftruncate for saveAsStream
2021-12-12 13:42:46 +03:00
Ne-Lexa
66168de897 Merge tag '4.0.1' into develop
4.0.1 version
2021-12-12 12:50:55 +03:00
Ne-Lexa
a18f80db50 Merge branch 'hotfix/4.0.1' 2021-12-12 12:50:45 +03:00
Ne-Lexa
bfff5f475a cs fix 2021-12-12 12:50:30 +03:00
Ne-Lexa
83dd71e602 update dependencies 2021-12-12 12:46:29 +03:00
Michel Bardelmeijer
0251dbf5c7 Remove ftruncate for saveAsStream 2021-09-06 20:28:28 +02:00
Ne-Lexa
f216617486 change php version for coverage 2021-02-25 22:46:16 +03:00
Ne-Lexa
58f821040f update build 2021-02-25 21:42:14 +03:00
Ne-Lexa
f0c59a458d update build 2021-02-25 21:33:48 +03:00
Ne-Lexa
d0228d388a disabled JIT for tests on php 8 2021-02-25 21:28:27 +03:00
Ne-Lexa
1a762284da disabled JIT for tests on php 8 2021-02-25 21:26:47 +03:00
Ne-Lexa
0be0d32fff fix build 2021-02-25 21:04:19 +03:00
Ne-Lexa
f0e26007c1 Merge tag '4.0.0' into develop
Tagging version 4.0.0 4.0.0
2021-02-24 15:51:19 +03:00
Ne-Lexa
668afcf81a Merge branch 'release/4.0.0' 2021-02-24 15:51:19 +03:00
Ne-Lexa
a11c6367b4 added method outputAsSymfonyResponse, rename method outputAsResponse to outputAsPsr7Response 2021-02-24 15:37:11 +03:00
Ne-Lexa
e8418d57b1 Merge branch 'feature/php8' into develop 2021-02-24 12:11:34 +03:00
Ne-Lexa
4a436569da update README 2021-02-24 12:06:08 +03:00
Ne-Lexa
ac899a02e0 update readme 2021-02-24 09:53:24 +03:00
Ne-Lexa
28d33ab687 build: run all tests (incl. very slow) 2021-02-23 20:07:37 +03:00
Ne-Lexa
8f43a3a75f fix static analysis config 2021-02-23 19:57:13 +03:00
Ne-Lexa
00dd097b2d added static analysis 2021-02-23 19:45:39 +03:00
Ne-Lexa
a85288d34b added build on macos 2021-02-23 15:55:08 +03:00
Ne-Lexa
620065ac5b disabled jit on php 8.0 test 2021-02-23 15:37:03 +03:00
Ne-Lexa
34074a8024 update docs and build 2021-02-23 13:34:41 +03:00
Ne-Lexa
f799eca6de update github actions build 2021-02-22 20:24:13 +03:00
Ne-Lexa
ae04b357fc update github actions build 2021-02-22 20:00:59 +03:00
Ne-Lexa
f67934f95a update readme 2021-02-22 19:59:54 +03:00
Ne-Lexa
a65fe4579b php8 support 2021-02-22 13:12:01 +03:00
Ne-Lexa
44c2041f62 Merge branch 'php8' of https://github.com/odan/php-zip into feature/php8 2021-02-18 09:47:51 +03:00
Daniel Opitz
bcc66edad3 Add PHP 7.2 to build matrix 2021-01-09 18:49:36 +01:00
Daniel Opitz
85b3ea84b6 Fix cs 2021-01-09 18:25:45 +01:00
Daniel Opitz
4ecb0c4ee6 Add PHP 8 support 2021-01-09 18:16:56 +01:00
wapplay
501b52f6fc Merge branch 'release/3.3.3' 2020-07-12 00:01:42 +03:00
wapplay
7283fc3402 Merge tag '3.3.3' into develop
Tagging version 3.3.3 3.3.3
2020-07-12 00:01:42 +03:00
wapplay
d9022e80c5 LICENSE file added 2020-07-11 23:48:23 +03:00
wapplay
c10c425f7e Improved Windows compatibility, fix #54 2020-07-11 23:04:33 +03:00
wapplay
0655e282e9 Merge branch 'feature/overwrite' into develop 2020-07-11 22:21:37 +03:00
wapplay
391a55f378 Merge branch 'release/3.3.2' 2020-06-22 18:05:21 +03:00
wapplay
b64e9ac328 Merge tag '3.3.2' into develop
Tagging version 3.3.2 3.3.2
2020-06-22 18:05:21 +03:00
Ne-Lexa
f503fc164e Merge pull request #60 from Araqel/master
removed extra commas from .phpstorm.meta.php
2020-06-22 18:02:56 +03:00
Araqel Araqelyan
8ce666fb5e removed extra commas from .phpstorm.meta.php 2020-06-22 18:53:58 +04:00
wapplay
dbddcda001 #54 overwriting open zip archive fixed 2020-05-04 14:13:39 +03:00
wapplay
52ce79d4d2 Merge branch 'release/3.3.1' 2020-04-01 16:33:45 +03:00
wapplay
3942bf2005 Merge tag '3.3.1' into develop
Tagging version 3.3.1 3.3.1
2020-04-01 16:33:45 +03:00
Ne-Lexa
decf4f5095 Merge pull request #47 from chx/chx-patch-1
Do not pin random_compat version
2020-04-01 16:29:06 +03:00
chx
70d8b8200b Do not pin random_compat version 2020-03-23 09:49:26 -07:00
Ne-Lexa
8fdc21eece Merge branch 'release/3.3.0' 2020-02-04 12:01:18 +03:00
Ne-Lexa
74c0a49057 Merge tag '3.3.0' into develop
Tagging version 3.3.0 3.3.0
2020-02-04 12:01:18 +03:00
Ne-Lexa
ae6d47f47a removed test ZipInfo::__toString() (depends on time zone) 2020-02-04 11:58:47 +03:00
Ne-Lexa
b3b676e3af Merge branch 'release/3.3.0' 2020-02-04 11:47:43 +03:00
Ne-Lexa
f3d769739b Merge tag '3.2.2' into develop
Tagging hotfix 3.2.2 3.2.2

# Conflicts:
#	.travis.yml
#	tests/ZipFileTest.php
2020-02-04 11:36:29 +03:00
Ne-Lexa
074443dbc4 Merge branch 'hotfix/3.2.2' 2020-02-04 11:29:58 +03:00
Ne-Lexa
cbb693213e fix replace contents by file 2020-02-04 11:18:32 +03:00
Ne-Lexa
820c63c30f ZipInfo tests 2020-01-31 13:42:19 +03:00
Ne-Lexa
2235de6b35 Merge branch 'feature/zipcontainer-override-support' into develop 2020-01-31 12:13:16 +03:00
Ne-Lexa
6e33ed08ef run php 7.4 in xenial 2020-01-22 16:26:24 +03:00
Ne-Lexa
79e77a8c88 fix close container in ZipFile::close() 2020-01-22 16:07:22 +03:00
Ne-Lexa
8dcde47072 Added a new option for extracting unix symlinks.
Added new parameter to get the list of extracted files.
2020-01-22 12:53:16 +03:00
Ne-Lexa
47161bdb02 zip extra fields tests 2020-01-22 12:48:47 +03:00
Ne-Lexa
943cf3e777 Fixed problem with cloning a zip container. 2020-01-22 12:48:15 +03:00
Ne-Lexa
5ec656fde4 Implemented the ability to override the instance of ZipContainer.
ZipContainer will be cloned before writing the zip file.
Tested custom ZipWriter, ZipReader, ZipContainer and ZipFile.
2020-01-21 14:13:47 +03:00
Ne-Lexa
d305ab68bc Merge branch 'hotfix/3.2.1' 2020-01-14 16:44:56 +03:00
Ne-Lexa
e0da8c94be Merge tag '3.2.1' into develop
Tagging hotfix 3.2.1 3.2.1
2020-01-14 16:44:56 +03:00
Ne-Lexa
8487dac9df zip extra tests, php 32-bit compat 2020-01-14 16:31:22 +03:00
Ne-Lexa
d21fdb35bb fix#13 fix#16 fix#27 fix#31 fix#41
Tagging version 3.2.0 3.2.0
- Fix memory leak problem
- Add new methods: `getEntry()`, `getEntries()`, `addSplFile()`, `addFromFinder()`
- Fix large zip files problem
2020-01-09 17:41:47 +03:00
123 changed files with 7447 additions and 6946 deletions

5
.gitattributes vendored
View File

@@ -2,7 +2,10 @@
.github export-ignore
.gitignore export-ignore
.php_cs export-ignore
.travis.yml export-ignore
bootstrap.php export-ignore
logo.svg export-ignore
phpunit.xml export-ignore
phpunit.xml.dist export-ignore
psalm.xml export-ignore
psalm.xml.dist export-ignore
tests export-ignore

132
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,132 @@
name: build
on:
- push
- pull_request
jobs:
tests:
name: PHP ${{ matrix.php }} Test on ${{ matrix.os }}
env:
extensions: bz2, dom, iconv, fileinfo, openssl, xml, zlib
key: cache-v1
PHPUNIT_COVERAGE: 0
PHP_INI: date.timezone='UTC', memory_limit=-1, opcache.enable=1, opcache.enable_cli=1
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
php:
- '7.4'
- '8.0'
- '8.1'
runs-on: ${{ matrix.os }}
steps:
-
name: Checkout
uses: actions/checkout@v1
-
name: Install linux dependencies
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install unzip p7zip-full
-
name: Install windows dependencies
if: matrix.os == 'windows-latest'
run: choco install zip unzip 7zip
-
name: Install macos dependencies
if: matrix.os == 'macos-latest'
run: brew install zip unzip p7zip
-
name: Disable JIT for PHP 8 on Linux and Mac
if: (matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest') && matrix.php != '7.4'
run: echo "PHP_INI=\"${PHP_INI}, opcache.jit=0, opcache.jit_buffer_size=0\"" >> $GITHUB_ENV
-
name: Disable JIT for PHP 8 on Windows
if: matrix.os == 'windows-latest' && matrix.php != '7.4'
run: echo "PHP_INI=\"$PHP_INI, opcache.jit=0, opcache.jit_buffer_size=0\"" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
-
name: Install PHP with extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: ${{ env.extensions }}
coverage: pcov
ini-values: ${{ env.PHP_INI }}
tools: composer:v2, cs2pr
-
name: Determine composer cache directory on Linux or MacOS
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV
-
name: Determine composer cache directory on Windows
if: matrix.os == 'windows-latest'
run: echo "COMPOSER_CACHE_DIR=~\AppData\Local\Composer" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
-
name: Set coverage args
if: matrix.os == 'ubuntu-latest' && matrix.php == '7.4'
run: echo "PHPUNIT_COVERAGE=1" >> $GITHUB_ENV
-
name: Cache composer dependencies
uses: actions/cache@v2
with:
path: ${{ env.COMPOSER_CACHE_DIR }}
key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: php${{ matrix.php }}-composer-
-
name: Check PHP Version
run: php -v
-
name: Check Composer Version
run: composer -V
-
name: Check PHP Extensions
run: php -m
-
name: Validate composer.json and composer.lock
run: composer validate
-
name: Install dependencies with composer
run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
-
name: Run tests with phpunit
if: env.PHPUNIT_COVERAGE == 0
run: vendor/bin/phpunit -v --testsuite "all_tests" --group small,medium,large
-
name: Run tests with phpunit and coverage
if: env.PHPUNIT_COVERAGE == 1
run: vendor/bin/phpunit -v --coverage-clover=coverage.clover --testsuite "all_tests" --group small,medium,large
-
name: Static analysis
run: vendor/bin/psalm --shepherd --stats --output-format=checkstyle | cs2pr --graceful-warnings --colorize
-
name: Upload code coverage scrutinizer
if: env.PHPUNIT_COVERAGE == 1
run: |
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage.clover

9
.gitignore vendored
View File

@@ -1,5 +1,6 @@
/vendor
*.iml
/.idea
/composer.lock
/.php_cs.cache
build/
/vendor
*.cache
composer.lock
psalm.xml

File diff suppressed because it is too large Load Diff

View File

@@ -65,6 +65,12 @@ namespace PHPSTORM_META {
expectedArguments(\PhpZip\ZipFile::outputAsResponse(), 2, argumentsSet("zip_mime_types"));
expectedArguments(\PhpZip\ZipFile::outputAsResponse(), 3, argumentsSet("bool"));
expectedArguments(\PhpZip\ZipFile::outputAsPsr7Response(), 2, argumentsSet("zip_mime_types"));
expectedArguments(\PhpZip\ZipFile::outputAsPsr7Response(), 3, argumentsSet("bool"));
expectedArguments(\PhpZip\ZipFile::outputAsSymfonyResponse(), 1, argumentsSet("zip_mime_types"));
expectedArguments(\PhpZip\ZipFile::outputAsSymfonyResponse(), 2, argumentsSet("bool"));
registerArgumentsSet(
'dos_charset',
\PhpZip\Constants\DosCodePage::CP_LATIN_US,
@@ -82,7 +88,7 @@ namespace PHPSTORM_META {
\PhpZip\Constants\DosCodePage::CP_NORDIC,
\PhpZip\Constants\DosCodePage::CP_CYRILLIC_RUSSIAN,
\PhpZip\Constants\DosCodePage::CP_GREEK2,
\PhpZip\Constants\DosCodePage::CP_THAI,
\PhpZip\Constants\DosCodePage::CP_THAI
);
expectedArguments(\PhpZip\Model\ZipEntry::setCharset(), 0, argumentsSet('dos_charset'));
expectedArguments(\PhpZip\Constants\DosCodePage::toUTF8(), 1, argumentsSet('dos_charset'));
@@ -92,7 +98,7 @@ namespace PHPSTORM_META {
"zip_os",
\PhpZip\Constants\ZipPlatform::OS_UNIX,
\PhpZip\Constants\ZipPlatform::OS_DOS,
\PhpZip\Constants\ZipPlatform::OS_MAC_OSX,
\PhpZip\Constants\ZipPlatform::OS_MAC_OSX
);
expectedArguments(\PhpZip\Model\ZipEntry::setCreatedOS(), 0, argumentsSet('zip_os'));
expectedArguments(\PhpZip\Model\ZipEntry::setExtractedOS(), 0, argumentsSet('zip_os'));
@@ -107,4 +113,22 @@ namespace PHPSTORM_META {
\PhpZip\Constants\GeneralPurposeBitFlag::UTF8
);
expectedArguments(\PhpZip\Model\ZipEntry::setGeneralPurposeBitFlags(), 0, argumentsSet('zip_gpbf'));
registerArgumentsSet(
"winzip_aes_vendor_version",
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::VERSION_AE1,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::VERSION_AE2
);
registerArgumentsSet(
"winzip_aes_key_strength",
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_256BIT,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_128BIT,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_192BIT
);
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 0, argumentsSet('winzip_aes_vendor_version'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 1, argumentsSet('winzip_aes_key_strength'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 2, argumentsSet('compression_methods'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setVendorVersion(), 0, argumentsSet('winzip_aes_vendor_version'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setKeyStrength(), 0, argumentsSet('winzip_aes_key_strength'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setCompressionMethod(), 0, argumentsSet('compression_methods'));
}

View File

@@ -1,67 +0,0 @@
language: php
env:
global:
- ZIPALIGN_INSTALL=false
- COVERAGE=false
- PHPUNIT_FLAGS="-v -c phpunit.xml --testsuite only_fast_tests"
matrix:
include:
- php: 5.5
os: linux
dist: trusty
- php: 5.6
os: linux
dist: xenial
env: ZIPALIGN_INSTALL=true
- php: 7.0
os: linux
dist: xenial
env: ZIPALIGN_INSTALL=true
- php: 7.1
os: linux
dist: xenial
env: ZIPALIGN_INSTALL=true
- php: 7.2
os: linux
dist: xenial
env: ZIPALIGN_INSTALL=true
- php: 7.3
os: linux
dist: xenial
env: ZIPALIGN_INSTALL=true
- php: 7.4
os: linux
dist: bionic
env: COVERAGE=true ZIPALIGN_INSTALL=true PHPUNIT_FLAGS="-v -c phpunit.xml --testsuite only_fast_tests --coverage-clover=coverage.clover"
before_install:
- if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi
install:
- travis_retry composer self-update && composer --version
- travis_retry composer install --no-interaction
addons:
apt:
packages:
- unzip
- p7zip-full
before_script:
- if [[ $ZIPALIGN_INSTALL = true ]]; then sudo apt-get install -y zipalign; fi
script:
- composer validate --no-check-lock
- vendor/bin/phpunit ${PHPUNIT_FLAGS}
after_success:
- if [[ $COVERAGE = true ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
- if [[ $COVERAGE = true ]]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016-2020 Ne-Lexa
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.

View File

@@ -1,42 +1,43 @@
`PhpZip`
========
<h1 align="center"><img src="logo.svg" alt="PhpZip" width="250" height="51"></h1>
`PhpZip` - php библиотека для продвинутой работы с ZIP-архивами.
[![Build Status](https://travis-ci.org/Ne-Lexa/php-zip.svg?branch=master)](https://travis-ci.org/Ne-Lexa/php-zip)
[![Latest Stable Version](https://poser.pugx.org/nelexa/zip/v/stable)](https://packagist.org/packages/nelexa/zip)
[![Total Downloads](https://poser.pugx.org/nelexa/zip/downloads)](https://packagist.org/packages/nelexa/zip)
[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.5-8892BF.svg)](https://php.net/)
[![License](https://poser.pugx.org/nelexa/zip/license)](https://packagist.org/packages/nelexa/zip)
[![Packagist Version](https://img.shields.io/packagist/v/nelexa/zip.svg)](https://packagist.org/packages/nelexa/zip)
[![Packagist Downloads](https://img.shields.io/packagist/dt/nelexa/zip.svg?color=%23ff007f)](https://packagist.org/packages/nelexa/zip)
[![Code Coverage](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/?branch=master)
[![Build Status](https://github.com/Ne-Lexa/php-zip/workflows/build/badge.svg)](https://github.com/Ne-Lexa/php-zip/actions)
[![License](https://img.shields.io/packagist/l/nelexa/zip.svg)](https://github.com/Ne-Lexa/php-zip/blob/master/LICENSE)
[English Documentation](README.md)
Содержание
----------
- [Функционал](#Features)
- [Требования](#Requirements)
- [Установка](#Installation)
- [Примеры](#Examples)
- [Глоссарий](#Glossary)
- [Документация](#Documentation)
+ [Обзор методов класса `\PhpZip\ZipFile`](#Documentation-Overview)
+ [Создание/Открытие ZIP-архива](#Documentation-Open-Zip-Archive)
+ [Чтение записей из архива](#Documentation-Open-Zip-Entries)
+ [Перебор записей/Итератор](#Documentation-Zip-Iterate)
+ [Получение информации о записях](#Documentation-Zip-Info)
+ [Добавление записей в архив](#Documentation-Add-Zip-Entries)
+ [Удаление записей из архива](#Documentation-Remove-Zip-Entries)
+ [Работа с записями и с архивом](#Documentation-Entries)
+ [Работа с паролями](#Documentation-Password)
+ [zipalign - выравнивание архива для оптимизации Android пакетов (APK)](#Documentation-ZipAlign-Usage)
+ [Отмена изменений](#Documentation-Unchanged)
+ [Сохранение файла или вывод в браузер](#Documentation-Save-Or-Output-Entries)
+ [Закрытие архива](#Documentation-Close-Zip-Archive)
- [Запуск тестов](#Running-Tests)
- [История изменений](#Changelog)
- [Обновление версий](#Upgrade)
+ [Обновление с версии 2 до версии 3.0](#Upgrade-v2-to-v3)
- [Функционал](#функционал)
- [Требования](#требования)
- [Установка](#установка)
- [Примеры](#примеры)
- [Глоссарий](#глоссарий)
- [Документация](#документация)
+ [Обзор методов класса `\PhpZip\ZipFile`](#обзор-методов-класса-phpzipzipfile)
+ [Создание/Открытие ZIP-архива](#созданиеоткрытие-zip-архива)
+ [Чтение записей из архива](#чтение-записей-из-архива)
+ [Перебор записей/Итератор](#перебор-записейитератор)
+ [Получение информации о записях](#получение-информации-о-записях)
+ [Добавление записей в архив](#добавление-записей-в-архив)
+ [Удаление записей из архива](#удаление-записей-из-архива)
+ [Работа с записями и с архивом](#работа-с-записями-и-с-архивом)
+ [Работа с паролями](#работа-с-паролями)
+ [Отмена изменений](#отмена-изменений)
+ [Сохранение файла или вывод в браузер](#сохранение-файла-или-вывод-в-браузер)
+ [Закрытие архива](#закрытие-архива)
- [Запуск тестов](#запуск-тестов)
- [История изменений](#история-изменений)
- [Обновление версий](#обновление-версий)
+ [Обновление с версии 3 до версии 4](#обновление-с-версии-3-до-версии-4)
+ [Обновление с версии 2 до версии 3](#обновление-с-версии-2-до-версии-3)
### <a name="Features"></a> Функционал
### Функционал
- Открытие и разархивирование ZIP-архивов.
- Создание ZIP-архивов.
- Модификация ZIP-архивов.
@@ -49,8 +50,7 @@
+ Deflate сжатие.
+ BZIP2 сжатие при наличии расширения `php-bz2`.
- Поддержка `ZIP64` (размер файла более 4 GB или количество записей в архиве более 65535).
- Встроенная поддержка выравнивания архива для оптимизации Android пакетов (APK) [`zipalign`](https://developer.android.com/studio/command-line/zipalign.html).
- Работа с паролями для PHP 5.5
- Работа с паролями
> **Внимание!**
>
> Для 32-bit систем, в данный момент не поддерживается метод шифрование `Traditional PKWARE Encryption (ZipCrypto)`.
@@ -64,17 +64,17 @@
+ Поддержка методов шифрования `Traditional PKWARE Encryption (ZipCrypto)` и `WinZIP AES Encryption (128, 192 или 256 bit)`.
+ Установка метода шифрования для всех или для отдельных записей в архиве.
### <a name="Requirements"></a> Требования
- `PHP` >= 5.5 (предпочтительно 64-bit).
### Требования
- `PHP` 7.4 или ^8.0 (предпочтительно 64-bit).
- Опционально php-расширение `bzip2` для поддержки BZIP2 компрессии.
- Опционально php-расширение `openssl` или `mcrypt` для `WinZip Aes Encryption` шифрования.
- Опционально php-расширение `openssl` для `WinZip Aes Encryption` шифрования.
### <a name="Installation"></a> Установка
### Установка
`composer require nelexa/zip`
Последняя стабильная версия: [![Latest Stable Version](https://poser.pugx.org/nelexa/zip/v/stable)](https://packagist.org/packages/nelexa/zip)
### <a name="Examples"></a> Примеры
### Примеры
```php
// создание нового архива
$zipFile = new \PhpZip\ZipFile();
@@ -104,95 +104,95 @@ finally{
```
Другие примеры можно посмотреть в папке `tests/`.
### <a name="Glossary"></a> Глоссарий
### Глоссарий
**Запись в ZIP-архиве (Zip Entry)** - файл или папка в ZIP-архиве. У каждой записи в архиве есть определённые свойства, например: имя файла, метод сжатия, метод шифрования, размер файла до сжатия, размер файла после сжатия, CRC32 и другие.
### <a name="Documentation"></a> Документация
#### <a name="Documentation-Overview"></a> Обзор методов класса `\PhpZip\ZipFile`
- [ZipFile::__construct](#Documentation-ZipFile-__construct) - инициализацирует ZIP-архив.
- [ZipFile::addAll](#Documentation-ZipFile-addAll) - добавляет все записи из массива.
- [ZipFile::addDir](#Documentation-ZipFile-addDir) - добавляет файлы из директории по указанному пути без вложенных директорий.
- [ZipFile::addDirRecursive](#Documentation-ZipFile-addDirRecursive) - добавляет файлы из директории по указанному пути c вложенными директориями.
- [ZipFile::addEmptyDir](#Documentation-ZipFile-addEmptyDir) - добавляет в ZIP-архив новую директорию.
- [ZipFile::addFile](#Documentation-ZipFile-addFile) - добавляет в ZIP-архив файл по указанному пути.
- [ZipFile::addSplFile](#Documentation-ZipFile-addSplFile) - добавляет объект `\SplFileInfo` в zip-архив.
- [ZipFile::addFromFinder](#Documentation-ZipFile-addFromFinder) - добавляет файлы из `Symfony\Component\Finder\Finder` в zip архив.
- [ZipFile::addFilesFromIterator](#Documentation-ZipFile-addFilesFromIterator) - добавляет файлы из итератора директорий.
- [ZipFile::addFilesFromGlob](#Documentation-ZipFile-addFilesFromGlob) - добавляет файлы из директории в соответствии с glob шаблоном без вложенных директорий.
- [ZipFile::addFilesFromGlobRecursive](#Documentation-ZipFile-addFilesFromGlobRecursive) - добавляет файлы из директории в соответствии с glob шаблоном c вложенными директориями.
- [ZipFile::addFilesFromRegex](#Documentation-ZipFile-addFilesFromRegex) - добавляет файлы из директории в соответствии с регулярным выражением без вложенных директорий.
- [ZipFile::addFilesFromRegexRecursive](#Documentation-ZipFile-addFilesFromRegexRecursive) - добавляет файлы из директории в соответствии с регулярным выражением c вложенными директориями.
- [ZipFile::addFromStream](#Documentation-ZipFile-addFromStream) - добавляет в ZIP-архив запись из потока.
- [ZipFile::addFromString](#Documentation-ZipFile-addFromString) - добавляет файл в ZIP-архив, используя его содержимое в виде строки.
- [ZipFile::close](#Documentation-ZipFile-close) - закрывает ZIP-архив.
- [ZipFile::count](#Documentation-ZipFile-count) - возвращает количество записей в архиве.
- [ZipFile::deleteFromName](#Documentation-ZipFile-deleteFromName) - удаляет запись по имени.
- [ZipFile::deleteFromGlob](#Documentation-ZipFile-deleteFromGlob) - удаляет записи в соответствии с glob шаблоном.
- [ZipFile::deleteFromRegex](#Documentation-ZipFile-deleteFromRegex) - удаляет записи в соответствии с регулярным выражением.
- [ZipFile::deleteAll](#Documentation-ZipFile-deleteAll) - удаляет все записи в ZIP-архиве.
- [ZipFile::disableEncryption](#Documentation-ZipFile-disableEncryption) - отключает шифрования всех записей, находящихся в архиве.
- [ZipFile::disableEncryptionEntry](#Documentation-ZipFile-disableEncryptionEntry) - отключает шифрование записи по её имени.
- [ZipFile::extractTo](#Documentation-ZipFile-extractTo) - извлекает содержимое архива в заданную директорию.
- [ZipFile::getAllInfo](#Documentation-ZipFile-getAllInfo) - возвращает подробную информацию обо всех записях в архиве.
- [ZipFile::getArchiveComment](#Documentation-ZipFile-getArchiveComment) - возвращает комментарий ZIP-архива.
- [ZipFile::getEntryComment](#Documentation-ZipFile-getEntryComment) - возвращает комментарий к записи, используя её имя.
- [ZipFile::getEntryContent](#Documentation-ZipFile-getEntryContent) - возвращает содержимое записи.
- [ZipFile::getEntryInfo](#Documentation-ZipFile-getEntryInfo) - возвращает подробную информацию о записи в архиве.
- [ZipFile::getListFiles](#Documentation-ZipFile-getListFiles) - возвращает список файлов архива.
- [ZipFile::hasEntry](#Documentation-ZipFile-hasEntry) - проверяет, присутствует ли запись в архиве.
- [ZipFile::isDirectory](#Documentation-ZipFile-isDirectory) - проверяет, является ли запись в архиве директорией.
- [ZipFile::matcher](#Documentation-ZipFile-matcher) - выборка записей в архиве для проведения операций над выбранными записями.
- [ZipFile::openFile](#Documentation-ZipFile-openFile) - открывает ZIP-архив из файла.
- [ZipFile::openFromString](#Documentation-ZipFile-openFromString) - открывает ZIP-архив из строки.
- [ZipFile::openFromStream](#Documentation-ZipFile-openFromStream) - открывает ZIP-архив из потока.
- [ZipFile::outputAsAttachment](#Documentation-ZipFile-outputAsAttachment) - выводит ZIP-архив в браузер.
- [ZipFile::outputAsResponse](#Documentation-ZipFile-outputAsResponse) - выводит ZIP-архив, как Response PSR-7.
- [ZipFile::outputAsString](#Documentation-ZipFile-outputAsString) - выводит ZIP-архив в виде строки.
- [ZipFile::rename](#Documentation-ZipFile-rename) - переименовывает запись по имени.
- [ZipFile::rewrite](#Documentation-ZipFile-rewrite) - сохраняет изменения и заново открывает изменившийся архив.
- [ZipFile::saveAsFile](#Documentation-ZipFile-saveAsFile) - сохраняет архив в файл.
- [ZipFile::saveAsStream](#Documentation-ZipFile-saveAsStream) - записывает архив в поток.
- [ZipFile::setArchiveComment](#Documentation-ZipFile-setArchiveComment) - устанавливает комментарий к ZIP-архиву.
- [ZipFile::setCompressionLevel](#Documentation-ZipFile-setCompressionLevel) - устанавливает уровень сжатия для всех файлов, находящихся в архиве.
- [ZipFile::setCompressionLevelEntry](#Documentation-ZipFile-setCompressionLevelEntry) - устанавливает уровень сжатия для определённой записи в архиве.
- [ZipFile::setCompressionMethodEntry](#Documentation-ZipFile-setCompressionMethodEntry) - устанавливает метод сжатия для определённой записи в архиве.
- [ZipFile::setEntryComment](#Documentation-ZipFile-setEntryComment) - устанавливает комментарий к записи, используя её имя.
- [ZipFile::setReadPassword](#Documentation-ZipFile-setReadPassword) - устанавливает пароль на чтение открытого запароленного архива для всех зашифрованных записей.
- [ZipFile::setReadPasswordEntry](#Documentation-ZipFile-setReadPasswordEntry) - устанавливает пароль на чтение конкретной зашифрованной записи открытого запароленного архива.
- ~~ZipFile::withNewPassword~~ - устаревший метод (**deprecated**) используйте метод [ZipFile::setPassword](#Documentation-ZipFile-setPassword).
- [ZipFile::setPassword](#Documentation-ZipFile-setPassword) - устанавливает новый пароль для всех файлов, находящихся в архиве.
- [ZipFile::setPasswordEntry](#Documentation-ZipFile-setPasswordEntry) - устанавливает новый пароль для конкретного файла.
- [ZipFile::setZipAlign](#Documentation-ZipFile-setZipAlign) - устанавливает выравнивание архива для оптимизации APK файлов (Android packages).
- [ZipFile::unchangeAll](#Documentation-ZipFile-unchangeAll) - отменяет все изменения, сделанные в архиве.
- [ZipFile::unchangeArchiveComment](#Documentation-ZipFile-unchangeArchiveComment) - отменяет изменения в комментарии к архиву.
- [ZipFile::unchangeEntry](#Documentation-ZipFile-unchangeEntry) - отменяет изменения для конкретной записи архива.
- ~~ZipFile::withoutPassword~~ - устаревший метод (**deprecated**) используйте метод [ZipFile::disableEncryption](#Documentation-ZipFile-disableEncryption).
- ~~ZipFile::withReadPassword~~ - устаревший метод (**deprecated**) используйте метод [ZipFile::setReadPassword](#Documentation-ZipFile-setReadPassword).
### Документация
#### Обзор методов класса `\PhpZip\ZipFile`
- [ZipFile::__construct](#zipfile__construct) - инициализирует ZIP-архив.
- [ZipFile::addAll](#zipfileaddall) - добавляет все записи из массива.
- [ZipFile::addDir](#zipfileadddir) - добавляет файлы из директории по указанному пути без вложенных директорий.
- [ZipFile::addDirRecursive](#zipfileadddirrecursive) - добавляет файлы из директории по указанному пути с вложенными директориями.
- [ZipFile::addEmptyDir](#zipfileaddemptydir) - добавляет в ZIP-архив новую директорию.
- [ZipFile::addFile](#zipfileaddfile) - добавляет в ZIP-архив файл по указанному пути.
- [ZipFile::addSplFile](#zipfileaddsplfile) - добавляет объект `\SplFileInfo` в zip-архив.
- [ZipFile::addFromFinder](#zipfileaddfromfinder) - добавляет файлы из `Symfony\Component\Finder\Finder` в zip архив.
- [ZipFile::addFilesFromIterator](#zipfileaddfilesfromiterator) - добавляет файлы из итератора директорий.
- [ZipFile::addFilesFromGlob](#zipfileaddfilesfromglob) - добавляет файлы из директории в соответствии с glob шаблоном без вложенных директорий.
- [ZipFile::addFilesFromGlobRecursive](#zipfileaddfilesfromglobrecursive) - добавляет файлы из директории в соответствии с glob шаблоном c вложенными директориями.
- [ZipFile::addFilesFromRegex](#zipfileaddfilesfromregex) - добавляет файлы из директории в соответствии с регулярным выражением без вложенных директорий.
- [ZipFile::addFilesFromRegexRecursive](#zipfileaddfilesfromregexrecursive) - добавляет файлы из директории в соответствии с регулярным выражением с вложенными директориями.
- [ZipFile::addFromStream](#zipfileaddfromstream) - добавляет в ZIP-архив запись из потока.
- [ZipFile::addFromString](#zipfileaddfromstring) - добавляет файл в ZIP-архив, используя его содержимое в виде строки.
- [ZipFile::close](#zipfileclose) - закрывает ZIP-архив.
- [ZipFile::count](#zipfilecount) - возвращает количество записей в архиве.
- [ZipFile::deleteFromName](#zipfiledeletefromname) - удаляет запись по имени.
- [ZipFile::deleteFromGlob](#zipfiledeletefromglob) - удаляет записи в соответствии с glob шаблоном.
- [ZipFile::deleteFromRegex](#zipfiledeletefromregex) - удаляет записи в соответствии с регулярным выражением.
- [ZipFile::deleteAll](#zipfiledeleteall) - удаляет все записи в ZIP-архиве.
- [ZipFile::disableEncryption](#zipfiledisableencryption) - отключает шифрования всех записей, находящихся в архиве.
- [ZipFile::disableEncryptionEntry](#zipfiledisableencryptionentry) - отключает шифрование записи по её имени.
- [ZipFile::extractTo](#zipfileextractto) - извлекает содержимое архива в заданную директорию.
- [ZipFile::getArchiveComment](#zipfilegetarchivecomment) - возвращает комментарий ZIP-архива.
- [ZipFile::getEntryComment](#zipfilegetentrycomment) - возвращает комментарий к записи, используя её имя.
- [ZipFile::getEntryContent](#zipfilegetentrycontent) - возвращает содержимое записи.
- [ZipFile::getListFiles](#zipfilegetlistfiles) - возвращает список файлов архива.
- [ZipFile::hasEntry](#zipfilehasentry) - проверяет, присутствует ли запись в архиве.
- [ZipFile::isDirectory](#zipfileisdirectory) - проверяет, является ли запись в архиве директорией.
- [ZipFile::matcher](#zipfilematcher) - выборка записей в архиве для проведения операций над выбранными записями.
- [ZipFile::openFile](#zipfileopenfile) - открывает ZIP-архив из файла.
- [ZipFile::openFromString](#zipfileopenfromstring) - открывает ZIP-архив из строки.
- [ZipFile::openFromStream](#zipfileopenfromstream) - открывает ZIP-архив из потока.
- [ZipFile::outputAsAttachment](#zipfileoutputasattachment) - выводит ZIP-архив в браузер.
- [ZipFile::outputAsPsr7Response](#zipfileoutputaspsr7response) - выводит ZIP-архив, как PSR-7 Response.
- [ZipFile::outputAsSymfonyResponse](#zipfileoutputassymfonyresponse) - выводит ZIP-архив, как Symfony Response.
- [ZipFile::outputAsString](#zipfileoutputasstring) - выводит ZIP-архив в виде строки.
- [ZipFile::rename](#zipfilerename) - переименовывает запись по имени.
- [ZipFile::rewrite](#zipfilerewrite) - сохраняет изменения и заново открывает изменившийся архив.
- [ZipFile::saveAsFile](#zipfilesaveasfile) - сохраняет архив в файл.
- [ZipFile::saveAsStream](#zipfilesaveasstream) - записывает архив в поток.
- [ZipFile::setArchiveComment](#zipfilesetarchivecomment) - устанавливает комментарий к ZIP-архиву.
- [ZipFile::setCompressionLevel](#zipfilesetcompressionlevel) - устанавливает уровень сжатия для всех файлов, находящихся в архиве.
- [ZipFile::setCompressionLevelEntry](#zipfilesetcompressionlevelentry) - устанавливает уровень сжатия для определённой записи в архиве.
- [ZipFile::setCompressionMethodEntry](#zipfilesetcompressionmethodentry) - устанавливает метод сжатия для определённой записи в архиве.
- [ZipFile::setEntryComment](#zipfilesetentrycomment) - устанавливает комментарий к записи, используя её имя.
- [ZipFile::setReadPassword](#zipfilesetreadpassword) - устанавливает пароль на чтение открытого запароленного архива для всех зашифрованных записей.
- [ZipFile::setReadPasswordEntry](#zipfilesetreadpasswordentry) - устанавливает пароль на чтение конкретной зашифрованной записи открытого запароленного архива.
- [ZipFile::setPassword](#zipfilesetpassword) - устанавливает новый пароль для всех файлов, находящихся в архиве.
- [ZipFile::setPasswordEntry](#zipfilesetpasswordentry) - устанавливает новый пароль для конкретного файла.
- [ZipFile::unchangeAll](#zipfileunchangeall) - отменяет все изменения, сделанные в архиве.
- [ZipFile::unchangeArchiveComment](#zipfileunchangearchivecomment) - отменяет изменения в комментарии к архиву.
- [ZipFile::unchangeEntry](#zipfileunchangeentry) - отменяет изменения для конкретной записи архива.
#### <a name="Documentation-Open-Zip-Archive"></a> Создание/Открытие ZIP-архива
<a name="Documentation-ZipFile-__construct"></a>**ZipFile::__construct** - Инициализацирует ZIP-архив.
#### Создание/Открытие ZIP-архива
##### ZipFile::__construct
Инициализирует ZIP-архив.
```php
$zipFile = new \PhpZip\ZipFile();
```
<a name="Documentation-ZipFile-openFile"></a> **ZipFile::openFile** - открывает ZIP-архив из файла.
##### ZipFile::openFile
Открывает ZIP-архив из файла.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->openFile('file.zip');
```
<a name="Documentation-ZipFile-openFromString"></a> **ZipFile::openFromString** - открывает ZIP-архив из строки.
##### ZipFile::openFromString
Открывает ZIP-архив из строки.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->openFromString($stringContents);
```
<a name="Documentation-ZipFile-openFromStream"></a> **ZipFile::openFromStream** - открывает ZIP-архив из потока.
##### ZipFile::openFromStream
Открывает ZIP-архив из потока.
```php
$stream = fopen('file.zip', 'rb');
$zipFile = new \PhpZip\ZipFile();
$zipFile->openFromStream($stream);
```
#### <a name="Documentation-Open-Zip-Entries"></a> Чтение записей из архива
<a name="Documentation-ZipFile-count"></a> **ZipFile::count** - возвращает количество записей в архиве.
#### Чтение записей из архива
##### ZipFile::count
Возвращает количество записей в архиве.
```php
$zipFile = new \PhpZip\ZipFile();
@@ -200,7 +200,8 @@ $count = count($zipFile);
// или
$count = $zipFile->count();
```
<a name="Documentation-ZipFile-getListFiles"></a> **ZipFile::getListFiles** - возвращает список файлов архива.
##### ZipFile::getListFiles
Возвращает список файлов архива.
```php
$zipFile = new \PhpZip\ZipFile();
$listFiles = $zipFile->getListFiles();
@@ -212,7 +213,8 @@ $listFiles = $zipFile->getListFiles();
// 2 => 'another path/',
// )
```
<a name="Documentation-ZipFile-getEntryContent"></a> **ZipFile::getEntryContent** - возвращает содержимое записи.
##### ZipFile::getEntryContent
Возвращает содержимое записи.
```php
// $entryName = 'path/to/example-entry-name.txt';
$zipFile = new \PhpZip\ZipFile();
@@ -221,7 +223,8 @@ $contents = $zipFile[$entryName];
// или
$contents = $zipFile->getEntryContents($entryName);
```
<a name="Documentation-ZipFile-hasEntry"></a> **ZipFile::hasEntry** - проверяет, присутствует ли запись в архиве.
##### ZipFile::hasEntry
Проверяет, присутствует ли запись в архиве.
```php
// $entryName = 'path/to/example-entry-name.txt';
$zipFile = new \PhpZip\ZipFile();
@@ -230,14 +233,16 @@ $hasEntry = isset($zipFile[$entryName]);
// или
$hasEntry = $zipFile->hasEntry($entryName);
```
<a name="Documentation-ZipFile-isDirectory"></a> **ZipFile::isDirectory** - проверяет, является ли запись в архиве директорией.
##### ZipFile::isDirectory
Проверяет, является ли запись в архиве директорией.
```php
// $entryName = 'path/to/';
$zipFile = new \PhpZip\ZipFile();
$isDirectory = $zipFile->isDirectory($entryName);
```
<a name="Documentation-ZipFile-extractTo"></a> **ZipFile::extractTo** - извлекает содержимое архива в заданную директорию.
##### ZipFile::extractTo
Извлекает содержимое архива в заданную директорию.
Директория должна существовать.
```php
$zipFile = new \PhpZip\ZipFile();
@@ -254,7 +259,7 @@ $extractOnlyFiles = [
$zipFile = new \PhpZip\ZipFile();
$zipFile->extractTo($toDirectory, $extractOnlyFiles);
```
#### <a name="Documentation-Zip-Iterate"></a> Перебор записей/Итератор
#### Перебор записей/Итератор
`ZipFile` является итератором.
Можно перебрать все записи, через цикл `foreach`.
```php
@@ -279,25 +284,18 @@ while ($iterator->valid())
$iterator->next();
}
```
#### <a name="Documentation-Zip-Info"></a> Получение информации о записях
<a name="Documentation-ZipFile-getArchiveComment"></a> **ZipFile::getArchiveComment** - возвращает комментарий ZIP-архива.
#### Получение информации о записях
##### ZipFile::getArchiveComment
Возвращает комментарий ZIP-архива.
```php
$commentArchive = $zipFile->getArchiveComment();
```
<a name="Documentation-ZipFile-getEntryComment"></a> **ZipFile::getEntryComment** - возвращает комментарий к записи, используя её имя.
##### ZipFile::getEntryComment
Возвращает комментарий к записи, используя её имя.
```php
$commentEntry = $zipFile->getEntryComment($entryName);
```
<a name="Documentation-ZipFile-getEntryInfo"></a> **ZipFile::getEntryInfo** - возвращает подробную информацию о записи в архиве.
```php
$zipFile = new \PhpZip\ZipFile();
$zipInfo = $zipFile->getEntryInfo('file.txt');
```
<a name="Documentation-ZipFile-getAllInfo"></a> **ZipFile::getAllInfo** - возвращает подробную информацию обо всех записях в архиве.
```php
$zipAllInfo = $zipFile->getAllInfo();
```
#### <a name="Documentation-Add-Zip-Entries"></a> Добавление записей в архив
#### Добавление записей в архив
Все методы добавления записей в ZIP-архив позволяют указать метод сжатия содержимого.
@@ -306,7 +304,8 @@ $zipAllInfo = $zipFile->getAllInfo();
- `\PhpZip\Constants\ZipCompressionMethod::DEFLATED` - Deflate сжатие
- `\PhpZip\Constants\ZipCompressionMethod::BZIP2` - Bzip2 сжатие при наличии расширения `ext-bz2`
<a name="Documentation-ZipFile-addFile"></a> **ZipFile::addFile** - добавляет в ZIP-архив файл по указанному пути из файловой системы.
##### ZipFile::addFile
Добавляет в ZIP-архив файл по указанному пути из файловой системы.
```php
$zipFile = new \PhpZip\ZipFile();
// $file = '...../file.ext';
@@ -320,8 +319,8 @@ $zipFile->addFile($file, $entryName, \PhpZip\Constants\ZipCompressionMethod::STO
$zipFile->addFile($file, $entryName, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addFile($file, $entryName, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addSplFile"></a>
**ZipFile::addSplFile"** - добавляет объект `\SplFileInfo` в zip-архив.
##### ZipFile::addSplFile"
Добавляет объект `\SplFileInfo` в zip-архив.
```php
// $file = '...../file.ext';
// $entryName = 'file2.ext'
@@ -339,9 +338,8 @@ $zipFile->addSplFile($splFile, $entryName, $options = [
\PhpZip\Constants\ZipOptions::COMPRESSION_METHOD => \PhpZip\Constants\ZipCompressionMethod::DEFLATED,
]);
```
<a name="Documentation-ZipFile-addFromFinder"></a>
**ZipFile::addFromFinder"** - добавляет файлы из `Symfony\Component\Finder\Finder` в zip архив.
https://symfony.com/doc/current/components/finder.html
##### ZipFile::addFromFinder"
Добавляет файлы из [`Symfony\Component\Finder\Finder`](https://symfony.com/doc/current/components/finder.html) в zip архив.
```php
$finder = new \Symfony\Component\Finder\Finder();
$finder
@@ -357,7 +355,8 @@ $zipFile->addFromFinder($finder, $options = [
\PhpZip\Constants\ZipOptions::MODIFIED_TIME => new \DateTimeImmutable('-1 day 5 min')
]);
```
<a name="Documentation-ZipFile-addFromString"></a> **ZipFile::addFromString** - добавляет файл в ZIP-архив, используя его содержимое в виде строки.
##### ZipFile::addFromString
Добавляет файл в ZIP-архив, используя его содержимое в виде строки.
```php
$zipFile = new \PhpZip\ZipFile();
@@ -370,7 +369,8 @@ $zipFile->addFromString($entryName, $contents, \PhpZip\Constants\ZipCompressionM
$zipFile->addFromString($entryName, $contents, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addFromString($entryName, $contents, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addFromStream"></a> **ZipFile::addFromStream** - добавляет в ZIP-архив запись из потока.
##### ZipFile::addFromStream
Добавляет в ZIP-архив запись из потока.
```php
// $stream = fopen(..., 'rb');
@@ -381,7 +381,8 @@ $zipFile->addFromStream($stream, $entryName, \PhpZip\Constants\ZipCompressionMet
$zipFile->addFromStream($stream, $entryName, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addFromStream($stream, $entryName, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addEmptyDir"></a> **ZipFile::addEmptyDir** - добавляет в ZIP-архив новую (пустую) директорию.
##### ZipFile::addEmptyDir
Добавляет в ZIP-архив новую (пустую) директорию.
```php
// $path = "path/to/";
@@ -389,18 +390,20 @@ $zipFile->addEmptyDir($path);
// или
$zipFile[$path] = null;
```
<a name="Documentation-ZipFile-addAll"></a> **ZipFile::addAll** - добавляет все записи из массива.
##### ZipFile::addAll
Добавляет все записи из массива.
```php
$entries = [
'file.txt' => 'file contents', // запись из строки данных
'empty dir/' => null, // пустой каталог
'path/to/file.jpg' => fopen('..../filename', 'r'), // запись из потока
'path/to/file.jpg' => fopen('..../filename', 'rb'), // запись из потока
'path/to/file.dat' => new \SplFileInfo('..../filename'), // запись из файла
];
$zipFile->addAll($entries);
```
<a name="Documentation-ZipFile-addDir"></a> **ZipFile::addDir** - добавляет файлы из директории по указанному пути без вложенных директорий.
##### ZipFile::addDir
Добавляет файлы из директории по указанному пути без вложенных директорий.
```php
$zipFile->addDir($dirName);
@@ -413,7 +416,8 @@ $zipFile->addDir($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::S
$zipFile->addDir($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addDir($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addDirRecursive"></a> **ZipFile::addDirRecursive** - добавляет файлы из директории по указанному пути c вложенными директориями.
##### ZipFile::addDirRecursive
Добавляет файлы из директории по указанному пути c вложенными директориями.
```php
$zipFile->addDirRecursive($dirName);
@@ -426,7 +430,8 @@ $zipFile->addDirRecursive($dirName, $localPath, \PhpZip\Constants\ZipCompression
$zipFile->addDirRecursive($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addDirRecursive($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addFilesFromIterator"></a> **ZipFile::addFilesFromIterator** - добавляет файлы из итератора директорий.
##### ZipFile::addFilesFromIterator
Добавляет файлы из итератора директорий.
```php
// $directoryIterator = new \DirectoryIterator($dir); // без вложенных директорий
// $directoryIterator = new \RecursiveDirectoryIterator($dir); // с вложенными директориями
@@ -462,7 +467,8 @@ $ignoreIterator = new \PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator(
$zipFile->addFilesFromIterator($ignoreIterator);
```
<a name="Documentation-ZipFile-addFilesFromGlob"></a> **ZipFile::addFilesFromGlob** - добавляет файлы из директории в соответствии с [glob шаблоном](https://en.wikipedia.org/wiki/Glob_(programming)) без вложенных директорий.
##### ZipFile::addFilesFromGlob
Добавляет файлы из директории в соответствии с [glob шаблоном](https://en.wikipedia.org/wiki/Glob_(programming)) без вложенных директорий.
```php
$globPattern = '**.{jpg,jpeg,png,gif}'; // пример glob шаблона -> добавить все .jpg, .jpeg, .png и .gif файлы
@@ -477,7 +483,8 @@ $zipFile->addFilesFromGlob($dir, $globPattern, $localPath, \PhpZip\Constants\Zip
$zipFile->addFilesFromGlob($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addFilesFromGlob($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addFilesFromGlobRecursive"></a> **ZipFile::addFilesFromGlobRecursive** - добавляет файлы из директории в соответствии с [glob шаблоном](https://en.wikipedia.org/wiki/Glob_(programming)) c вложенными директориями.
##### ZipFile::addFilesFromGlobRecursive
Добавляет файлы из директории в соответствии с [glob шаблоном](https://en.wikipedia.org/wiki/Glob_(programming)) c вложенными директориями.
```php
$globPattern = '**.{jpg,jpeg,png,gif}'; // пример glob шаблона -> добавить все .jpg, .jpeg, .png и .gif файлы
@@ -492,7 +499,8 @@ $zipFile->addFilesFromGlobRecursive($dir, $globPattern, $localPath, \PhpZip\Cons
$zipFile->addFilesFromGlobRecursive($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addFilesFromGlobRecursive($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addFilesFromRegex"></a> **ZipFile::addFilesFromRegex** - добавляет файлы из директории в соответствии с [регулярным выражением](https://en.wikipedia.org/wiki/Regular_expression) без вложенных директорий.
##### ZipFile::addFilesFromRegex
Добавляет файлы из директории в соответствии с [регулярным выражением](https://en.wikipedia.org/wiki/Regular_expression) без вложенных директорий.
```php
$regexPattern = '/\.(jpe?g|png|gif)$/si'; // пример регулярного выражения -> добавить все .jpg, .jpeg, .png и .gif файлы
@@ -507,7 +515,8 @@ $zipFile->addFilesFromRegex($dir, $regexPattern, $localPath, \PhpZip\Constants\Z
$zipFile->addFilesFromRegex($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addFilesFromRegex($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
<a name="Documentation-ZipFile-addFilesFromRegexRecursive"></a> **ZipFile::addFilesFromRegexRecursive** - добавляет файлы из директории в соответствии с [регулярным выражением](https://en.wikipedia.org/wiki/Regular_expression) с вложенными директориями.
##### ZipFile::addFilesFromRegexRecursive
Добавляет файлы из директории в соответствии с [регулярным выражением](https://en.wikipedia.org/wiki/Regular_expression) с вложенными директориями.
```php
$regexPattern = '/\.(jpe?g|png|gif)$/si'; // пример регулярного выражения -> добавить все .jpg, .jpeg, .png и .gif файлы
@@ -522,33 +531,39 @@ $zipFile->addFilesFromRegexRecursive($dir, $regexPattern, $localPath, \PhpZip\Co
$zipFile->addFilesFromRegexRecursive($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate сжатие
$zipFile->addFilesFromRegexRecursive($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 сжатие
```
#### <a name="Documentation-Remove-Zip-Entries"></a> Удаление записей из архива
<a name="Documentation-ZipFile-deleteFromName"></a> **ZipFile::deleteFromName** - удаляет запись по имени.
#### Удаление записей из архива
##### ZipFile::deleteFromName
Удаляет запись по имени.
```php
$zipFile->deleteFromName($entryName);
```
<a name="Documentation-ZipFile-deleteFromGlob"></a> **ZipFile::deleteFromGlob** - удаляет записи в соответствии с [glob шаблоном](https://en.wikipedia.org/wiki/Glob_(programming)).
##### ZipFile::deleteFromGlob
Удаляет записи в соответствии с [glob шаблоном](https://en.wikipedia.org/wiki/Glob_(programming)).
```php
$globPattern = '**.{jpg,jpeg,png,gif}'; // пример glob шаблона -> удалить все .jpg, .jpeg, .png и .gif файлы
$zipFile->deleteFromGlob($globPattern);
```
<a name="Documentation-ZipFile-deleteFromRegex"></a> **ZipFile::deleteFromRegex** - удаляет записи в соответствии с [регулярным выражением](https://en.wikipedia.org/wiki/Regular_expression).
##### ZipFile::deleteFromRegex
Удаляет записи в соответствии с [регулярным выражением](https://en.wikipedia.org/wiki/Regular_expression).
```php
$regexPattern = '/\.(jpe?g|png|gif)$/si'; // пример регулярному выражения -> удалить все .jpg, .jpeg, .png и .gif файлы
$zipFile->deleteFromRegex($regexPattern);
```
<a name="Documentation-ZipFile-deleteAll"></a> **ZipFile::deleteAll** - удаляет все записи в ZIP-архиве.
##### ZipFile::deleteAll
Удаляет все записи в ZIP-архиве.
```php
$zipFile->deleteAll();
```
#### <a name="Documentation-Entries"></a> Работа с записями и с архивом
<a name="Documentation-ZipFile-rename"></a> **ZipFile::rename** - переименовывает запись по имени.
#### Работа с записями и с архивом
##### ZipFile::rename
Переименовывает запись по имени.
```php
$zipFile->rename($oldName, $newName);
```
<a name="Documentation-ZipFile-setCompressionLevel"></a> **ZipFile::setCompressionLevel** - устанавливает уровень сжатия для всех файлов, находящихся в архиве.
##### ZipFile::setCompressionLevel
Устанавливает уровень сжатия для всех файлов, находящихся в архиве.
> _Обратите внимание, что действие данного метода не распространяется на записи, добавленные после выполнения этого метода._
@@ -558,13 +573,15 @@ $zipFile->rename($oldName, $newName);
```php
$zipFile->setCompressionLevel(\PhpZip\Constants\ZipCompressionLevel::MAXIMUM);
```
<a name="Documentation-ZipFile-setCompressionLevelEntry"></a> **ZipFile::setCompressionLevelEntry** - устанавливает уровень сжатия для определённой записи в архиве.
##### ZipFile::setCompressionLevelEntry
Устанавливает уровень сжатия для определённой записи в архиве.
Поддерживаются диапазон значений от 1 (`\PhpZip\Constants\ZipCompressionLevel::SUPER_FAST`) до 9 (`\PhpZip\Constants\ZipCompressionLevel::MAXIMUM`). Чем выше число, тем лучше и дольше сжатие.
```php
$zipFile->setCompressionLevelEntry($entryName, \PhpZip\Constants\ZipCompressionLevel::MAXIMUM);
```
<a name="Documentation-ZipFile-setCompressionMethodEntry"></a> **ZipFile::setCompressionMethodEntry** - устанавливает метод сжатия для определённой записи в архиве.
##### ZipFile::setCompressionMethodEntry
Устанавливает метод сжатия для определённой записи в архиве.
Доступны следующие методы сжатия:
- `\PhpZip\Constants\ZipCompressionMethod::STORED` - без сжатия
@@ -573,15 +590,18 @@ $zipFile->setCompressionLevelEntry($entryName, \PhpZip\Constants\ZipCompressionL
```php
$zipFile->setCompressionMethodEntry($entryName, \PhpZip\Constants\ZipCompressionMethod::DEFLATED);
```
<a name="Documentation-ZipFile-setArchiveComment"></a> **ZipFile::setArchiveComment** - устанавливает комментарий к ZIP-архиву.
##### ZipFile::setArchiveComment
Устанавливает комментарий к ZIP-архиву.
```php
$zipFile->setArchiveComment($commentArchive);
```
<a name="Documentation-ZipFile-setEntryComment"></a> **ZipFile::setEntryComment** - устанавливает комментарий к записи, используя её имя.
##### ZipFile::setEntryComment
Устанавливает комментарий к записи, используя её имя.
```php
$zipFile->setEntryComment($entryName, $comment);
```
<a name="Documentation-ZipFile-matcher"></a> **ZipFile::matcher** - выборка записей в архиве для проведения операций над выбранными записями.
##### ZipFile::matcher
Выборка записей в архиве для проведения операций над выбранными записями.
```php
$matcher = $zipFile->matcher();
```
@@ -634,7 +654,7 @@ $matcher->setPassword($password, $encryptionMethod); // устанавливае
$matcher->setEncryptionMethod($encryptionMethod); // устанавливает метод шифрования на выбранные записи
$matcher->disableEncryption(); // отключает шифрование для выбранных записей
```
#### <a name="Documentation-Password"></a> Работа с паролями
#### Работа с паролями
Реализована поддержка методов шифрования:
- `\PhpZip\Constants\ZipEncryptionMethod::PKWARE` - Traditional PKWARE encryption
@@ -642,17 +662,20 @@ $matcher->disableEncryption(); // отключает шифрование для
- `\PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_192` - WinZip AES encryption 192 bit
- `\PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_128` - WinZip AES encryption 128 bit
<a name="Documentation-ZipFile-setReadPassword"></a> **ZipFile::setReadPassword** - устанавливает пароль на чтение открытого запароленного архива для всех зашифрованных записей.
##### ZipFile::setReadPassword
Устанавливает пароль на чтение открытого запароленного архива для всех зашифрованных записей.
> _Установка пароля не является обязательной для добавления новых записей или удаления существующих, но если вы захотите извлечь контент или изменить метод/уровень сжатия, метод шифрования или изменить пароль, то в этом случае пароль необходимо указать._
```php
$zipFile->setReadPassword($password);
```
<a name="Documentation-ZipFile-setReadPasswordEntry"></a> **ZipFile::setReadPasswordEntry** - устанавливает пароль на чтение конкретной зашифрованной записи открытого запароленного архива.
##### ZipFile::setReadPasswordEntry
Устанавливает пароль на чтение конкретной зашифрованной записи открытого запароленного архива.
```php
$zipFile->setReadPasswordEntry($entryName, $password);
```
<a name="Documentation-ZipFile-setPassword"></a> **ZipFile::setPassword** - устанавливает новый пароль для всех файлов, находящихся в архиве.
##### ZipFile::setPassword
Устанавливает новый пароль для всех файлов, находящихся в архиве.
> _Обратите внимание, что действие данного метода не распространяется на записи, добавленные после выполнения этого метода._
```php
@@ -663,7 +686,8 @@ $zipFile->setPassword($password);
$encryptionMethod = \PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_256;
$zipFile->setPassword($password, $encryptionMethod);
```
<a name="Documentation-ZipFile-setPasswordEntry"></a> **ZipFile::setPasswordEntry** - устанавливает новый пароль для конкретного файла.
##### ZipFile::setPasswordEntry
Устанавливает новый пароль для конкретного файла.
```php
$zipFile->setPasswordEntry($entryName, $password);
```
@@ -672,57 +696,54 @@ $zipFile->setPasswordEntry($entryName, $password);
$encryptionMethod = \PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_256;
$zipFile->setPasswordEntry($entryName, $password, $encryptionMethod);
```
<a name="Documentation-ZipFile-disableEncryption"></a> **ZipFile::disableEncryption** - отключает шифрования всех записей, находящихся в архиве.
##### ZipFile::disableEncryption
Отключает шифрования всех записей, находящихся в архиве.
> _Обратите внимание, что действие данного метода не распространяется на записи, добавленные после выполнения этого метода._
```php
$zipFile->disableEncryption();
```
<a name="Documentation-ZipFile-disableEncryptionEntry"></a> **ZipFile::disableEncryptionEntry** - отключает шифрование записи по её имени.
##### ZipFile::disableEncryptionEntry
Отключает шифрование записи по её имени.
```php
$zipFile->disableEncryptionEntry($entryName);
```
#### <a name="Documentation-ZipAlign-Usage"></a> zipalign
<a name="Documentation-ZipFile-setZipAlign"></a> **ZipFile::setZipAlign** - устанавливает выравнивание архива для оптимизации APK файлов (Android packages).
Метод добавляет паддинги незашифрованным и не сжатым записям, для оптимизации расхода памяти в системе Android. Рекомендуется использовать для `APK` файлов. Файл может незначительно увеличиться.
Этот метод является альтернативой вызова команды `zipalign -f -v 4 filename.zip`.
Подробнее можно ознакомиться по [ссылке](https://developer.android.com/studio/command-line/zipalign.html).
```php
// вызовите до сохранения или вывода архива
$zipFile->setZipAlign(4);
```
#### <a name="Documentation-Unchanged"></a> Отмена изменений
<a name="Documentation-ZipFile-unchangeAll"></a> **ZipFile::unchangeAll** - отменяет все изменения, сделанные в архиве.
#### Отмена изменений
##### ZipFile::unchangeAll
Отменяет все изменения, сделанные в архиве.
```php
$zipFile->unchangeAll();
```
<a name="Documentation-ZipFile-unchangeArchiveComment"></a> **ZipFile::unchangeArchiveComment** - отменяет изменения в комментарии к архиву.
##### ZipFile::unchangeArchiveComment
Отменяет изменения в комментарии к архиву.
```php
$zipFile->unchangeArchiveComment();
```
<a name="Documentation-ZipFile-unchangeEntry"></a> **ZipFile::unchangeEntry** - отменяет изменения для конкретной записи архива.
##### ZipFile::unchangeEntry
Отменяет изменения для конкретной записи архива.
```php
$zipFile->unchangeEntry($entryName);
```
#### <a name="Documentation-Save-Or-Output-Entries"></a> Сохранение файла или вывод в браузер
<a name="Documentation-ZipFile-saveAsFile"></a> **ZipFile::saveAsFile** - сохраняет архив в файл.
#### Сохранение файла или вывод в браузер
##### ZipFile::saveAsFile
Сохраняет архив в файл.
```php
$zipFile->saveAsFile($filename);
```
<a name="Documentation-ZipFile-saveAsStream"></a> **ZipFile::saveAsStream** - записывает архив в поток.
##### ZipFile::saveAsStream
Записывает архив в поток.
```php
// $fp = fopen($filename, 'w+b');
$zipFile->saveAsStream($fp);
```
<a name="Documentation-ZipFile-outputAsString"></a> **ZipFile::outputAsString** - выводит ZIP-архив в виде строки.
##### ZipFile::outputAsString
Выводит ZIP-архив в виде строки.
```php
$rawZipArchiveBytes = $zipFile->outputAsString();
```
<a name="Documentation-ZipFile-outputAsAttachment"></a> **ZipFile::outputAsAttachment** - выводит ZIP-архив в браузер.
##### ZipFile::outputAsAttachment
Выводит ZIP-архив в браузер.
При выводе устанавливаются необходимые заголовки, а после вывода завершается работа скрипта.
```php
@@ -730,41 +751,73 @@ $zipFile->outputAsAttachment($outputFilename);
```
Можно установить MIME-тип:
```php
$mimeType = 'application/zip'
$mimeType = 'application/zip';
$zipFile->outputAsAttachment($outputFilename, $mimeType);
```
<a name="Documentation-ZipFile-outputAsResponse"></a> **ZipFile::outputAsResponse** - выводит ZIP-архив, как Response [PSR-7](http://www.php-fig.org/psr/psr-7/).
##### ZipFile::outputAsPsr7Response
Выводит ZIP-архив, как [PSR-7 Response](http://www.php-fig.org/psr/psr-7/).
Метод вывода может использоваться в любом PSR-7 совместимом фреймворке.
```php
// $response = ....; // instance Psr\Http\Message\ResponseInterface
$zipFile->outputAsResponse($response, $outputFilename);
$zipFile->outputAsPsr7Response($response, $outputFilename);
```
Можно установить MIME-тип:
```php
$mimeType = 'application/zip'
$zipFile->outputAsResponse($response, $outputFilename, $mimeType);
$mimeType = 'application/zip';
$zipFile->outputAsPsr7Response($response, $outputFilename, $mimeType);
```
Пример для Slim Framework:
##### ZipFile::outputAsSymfonyResponse
Выводит ZIP-архив, как [Symfony Response](https://symfony.com/doc/current/components/http_foundation.html#response).
Метод вывода можно использовать в фреймворке Symfony.
```php
$app = new \Slim\App;
$app->get('/download', function ($req, $res, $args) {
$zipFile = new \PhpZip\ZipFile();
$zipFile['file.txt'] = 'content';
return $zipFile->outputAsResponse($res, 'file.zip');
});
$app->run();
$response = $zipFile->outputAsSymfonyResponse($outputFilename);
```
<a name="Documentation-ZipFile-rewrite"></a> **ZipFile::rewrite** - сохраняет изменения и заново открывает изменившийся архив.
Вы можете установить Mime-Type:
```php
$mimeType = 'application/zip';
$response = $zipFile->outputAsSymfonyResponse($outputFilename, $mimeType);
```
Пример использования в Symfony Controller:
```php
<?php
namespace App\Controller;
use PhpZip\ZipFile;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DownloadZipController
{
/**
* @Route("/downloads/{id}")
*
* @throws \PhpZip\Exception\ZipException
*/
public function __invoke(string $id): Response
{
$zipFile = new ZipFile();
$zipFile['file'] = 'contents';
$outputFilename = $id . '.zip';
return $zipFile->outputAsSymfonyResponse($outputFilename);
}
}
```
##### ZipFile::rewrite
Сохраняет изменения и заново открывает изменившийся архив.
```php
$zipFile->rewrite();
```
#### <a name="Documentation-Close-Zip-Archive"></a> Закрытие архива
<a name="Documentation-ZipFile-close"></a> **ZipFile::close** - закрывает ZIP-архив.
#### Закрытие архива
##### ZipFile::close
Закрывает ZIP-архив.
```php
$zipFile->close();
```
### <a name="Running-Tests"></a> Запуск тестов
### Запуск тестов
Установите зависимости для разработки.
```bash
composer install --dev
@@ -773,11 +826,27 @@ composer install --dev
```bash
vendor/bin/phpunit -v -c phpunit.xml
```
### <a name="Changelog"></a> История изменений
### История изменений
История изменений на [странице релизов](https://github.com/Ne-Lexa/php-zip/releases).
### <a name="Upgrade"></a> Обновление версий
#### <a name="Upgrade-v2-to-v3"></a> Обновление с версии 2 до версии 3.0
### Обновление версий
#### Обновление с версии 3 до версии 4
Обновите мажорную версию в файле `composer.json` до `^4.0`.
```json
{
"require": {
"nelexa/zip": "^4.0"
}
}
```
Затем установите обновления с помощью `Composer`:
```bash
composer update nelexa/zip
```
Обновите ваш код для работы с новой версией:
- удалены устаревшие метроды
- удалён zipalign функционал (он будет помещен в отдельный пакет nelexa/apkfile)
#### Обновление с версии 2 до версии 3
Обновите мажорную версию в файле `composer.json` до `^3.0`.
```json
{

476
README.md
View File

@@ -1,42 +1,48 @@
`PhpZip`
========
<h1 align="center"><img src="logo.svg" alt="PhpZip" width="250" height="51"></h1>
`PhpZip` is a php-library for extended work with ZIP-archives.
[![Build Status](https://travis-ci.org/Ne-Lexa/php-zip.svg?branch=master)](https://travis-ci.org/Ne-Lexa/php-zip)
[![Latest Stable Version](https://poser.pugx.org/nelexa/zip/v/stable)](https://packagist.org/packages/nelexa/zip)
[![Total Downloads](https://poser.pugx.org/nelexa/zip/downloads)](https://packagist.org/packages/nelexa/zip)
[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.5-8892BF.svg)](https://php.net/)
[![License](https://poser.pugx.org/nelexa/zip/license)](https://packagist.org/packages/nelexa/zip)
[![Packagist Version](https://img.shields.io/packagist/v/nelexa/zip.svg)](https://packagist.org/packages/nelexa/zip)
[![Packagist Downloads](https://img.shields.io/packagist/dt/nelexa/zip.svg?color=%23ff007f)](https://packagist.org/packages/nelexa/zip)
[![Code Coverage](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/?branch=master)
[![Build Status](https://github.com/Ne-Lexa/php-zip/workflows/build/badge.svg)](https://github.com/Ne-Lexa/php-zip/actions)
[![License](https://img.shields.io/packagist/l/nelexa/zip.svg)](https://github.com/Ne-Lexa/php-zip/blob/master/LICENSE)
[Russian Documentation](README.RU.md)
### Versions & Dependencies
| Version | PHP | Documentation |
| ------------------- | ---------- | -------------------------------------------------------------------- |
| ^4.0 (master) | ^7.4\|^8.0 | current |
| ^3.0 | ^5.5\|^7.0 | [Docs v3.3](https://github.com/Ne-Lexa/php-zip/blob/3.3.3/README.md) |
Table of contents
-----------------
- [Features](#Features)
- [Requirements](#Requirements)
- [Installation](#Installation)
- [Examples](#Examples)
- [Glossary](#Glossary)
- [Documentation](#Documentation)
+ [Overview of methods of the class `\PhpZip\ZipFile`](#Documentation-Overview)
+ [Creation/Opening of ZIP-archive](#Documentation-Open-Zip-Archive)
+ [Reading entries from the archive](#Documentation-Open-Zip-Entries)
+ [Iterating entries](#Documentation-Zip-Iterate)
+ [Getting information about entries](#Documentation-Zip-Info)
+ [Adding entries to the archive](#Documentation-Add-Zip-Entries)
+ [Deleting entries from the archive](#Documentation-Remove-Zip-Entries)
+ [Working with entries and archive](#Documentation-Entries)
+ [Working with passwords](#Documentation-Password)
+ [zipalign - alignment tool for Android (APK) files](#Documentation-ZipAlign-Usage)
+ [Undo changes](#Documentation-Unchanged)
+ [Saving a file or output to a browser](#Documentation-Save-Or-Output-Entries)
+ [Closing the archive](#Documentation-Close-Zip-Archive)
- [Running the tests](#Running-Tests)
- [Changelog](#Changelog)
- [Upgrade](#Upgrade)
+ [Upgrade version 2 to version 3.0](#Upgrade-v2-to-v3)
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Examples](#examples)
- [Glossary](#glossary)
- [Documentation](#documentation)
+ [Overview of methods of the class `\PhpZip\ZipFile`](#overview-of-methods-of-the-class-phpzipzipfile)
+ [Creation/Opening of ZIP-archive](#creationopening-of-zip-archive)
+ [Reading entries from the archive](#reading-entries-from-the-archive)
+ [Iterating entries](#iterating-entries)
+ [Getting information about entries](#getting-information-about-entries)
+ [Adding entries to the archive](#adding-entries-to-the-archive)
+ [Deleting entries from the archive](#deleting-entries-from-the-archive)
+ [Working with entries and archive](#working-with-entries-and-archive)
+ [Working with passwords](#working-with-passwords)
+ [Undo changes](#undo-changes)
+ [Saving a file or output to a browser](#saving-a-file-or-output-to-a-browser)
+ [Closing the archive](#closing-the-archive)
- [Running the tests](#running-the-tests)
- [Changelog](#changelog)
- [Upgrade](#upgrade)
+ [Upgrade version 3 to version 4](#upgrade-version-3-to-version-4)
+ [Upgrade version 2 to version 3](#upgrade-version-2-to-version-3)
### <a name="Features"></a> Features
### Features
- Opening and unzipping zip files.
- Creating ZIP-archives.
- Modifying ZIP archives.
@@ -49,8 +55,7 @@ Table of contents
+ Deflate compression.
+ BZIP2 compression with the extension `php-bz2`.
- Support for `ZIP64` (file size is more than 4 GB or the number of entries in the archive is more than 65535).
- Built-in support for aligning the archive to optimize Android packages (APK) [`zipalign`](https://developer.android.com/studio/command-line/zipalign.html).
- Working with passwords for PHP 5.5
- Working with passwords
> **Attention!**
>
> For 32-bit systems, the `Traditional PKWARE Encryption (ZipCrypto)` encryption method is not currently supported.
@@ -64,17 +69,17 @@ Table of contents
+ Support `Traditional PKWARE Encryption (ZipCrypto)` and `WinZIP AES Encryption` encryption methods.
+ Set the encryption method for all or individual entries in the archive.
### <a name="Requirements"></a> Requirements
- `PHP` >= 5.5 (preferably 64-bit).
### Requirements
- `PHP` >= 7.4 or `PHP` >= 8.0 (preferably 64-bit).
- Optional php-extension `bzip2` for BZIP2 compression.
- Optional php-extension `openssl` or `mcrypt` for `WinZip Aes Encryption` support.
- Optional php-extension `openssl` for `WinZip Aes Encryption` support.
### <a name="Installation"></a> Installation
### Installation
`composer require nelexa/zip`
Latest stable version: [![Latest Stable Version](https://poser.pugx.org/nelexa/zip/v/stable)](https://packagist.org/packages/nelexa/zip)
### <a name="Examples"></a> Examples
### Examples
```php
// create new archive
$zipFile = new \PhpZip\ZipFile();
@@ -104,95 +109,95 @@ finally{
```
Other examples can be found in the `tests/` folder
### <a name="Glossary"></a> Glossary
### Glossary
**Zip Entry** - file or folder in a ZIP-archive. Each entry in the archive has certain properties, for example: file name, compression method, encryption method, file size before compression, file size after compression, CRC32 and others.
### <a name="Documentation"></a> Documentation:
#### <a name="Documentation-Overview"></a> Overview of methods of the class `\PhpZip\ZipFile`
- [ZipFile::__construct](#Documentation-ZipFile-__construct) - initializes the ZIP archive.
- [ZipFile::addAll](#Documentation-ZipFile-addAll) - adds all entries from an array.
- [ZipFile::addDir](#Documentation-ZipFile-addDir) - adds files to the archive from the directory on the specified path without subdirectories.
- [ZipFile::addDirRecursive](#Documentation-ZipFile-addDirRecursive) - adds files to the archive from the directory on the specified path with subdirectories.
- [ZipFile::addEmptyDir](#Documentation-ZipFile-addEmptyDir) - add a new directory.
- [ZipFile::addFile](#Documentation-ZipFile-addFile) - adds a file to a ZIP archive from the given path.
- [ZipFile::addSplFile](#Documentation-ZipFile-addSplFile) - adds a `\SplFileInfo` to a ZIP archive.
- [ZipFile::addFromFinder](#Documentation-ZipFile-addFromFinder) - adds files from the `Symfony\Component\Finder\Finder` to a ZIP archive.
- [ZipFile::addFilesFromIterator](#Documentation-ZipFile-addFilesFromIterator) - adds files from the iterator of directories.
- [ZipFile::addFilesFromGlob](#Documentation-ZipFile-addFilesFromGlob) - adds files from a directory by glob pattern without subdirectories.
- [ZipFile::addFilesFromGlobRecursive](#Documentation-ZipFile-addFilesFromGlobRecursive) - adds files from a directory by glob pattern with subdirectories.
- [ZipFile::addFilesFromRegex](#Documentation-ZipFile-addFilesFromRegex) - adds files from a directory by PCRE pattern without subdirectories.
- [ZipFile::addFilesFromRegexRecursive](#Documentation-ZipFile-addFilesFromRegexRecursive) - adds files from a directory by PCRE pattern with subdirectories.
- [ZipFile::addFromStream](#Documentation-ZipFile-addFromStream) - adds a entry from the stream to the ZIP archive.
- [ZipFile::addFromString](#Documentation-ZipFile-addFromString) - adds a file to a ZIP archive using its contents.
- [ZipFile::close](#Documentation-ZipFile-close) - close the archive.
- [ZipFile::count](#Documentation-ZipFile-count) - returns the number of entries in the archive.
- [ZipFile::deleteFromName](#Documentation-ZipFile-deleteFromName) - deletes an entry in the archive using its name.
- [ZipFile::deleteFromGlob](#Documentation-ZipFile-deleteFromGlob) - deletes a entries in the archive using glob pattern.
- [ZipFile::deleteFromRegex](#Documentation-ZipFile-deleteFromRegex) - deletes a entries in the archive using PCRE pattern.
- [ZipFile::deleteAll](#Documentation-ZipFile-deleteAll) - deletes all entries in the ZIP archive.
- [ZipFile::disableEncryption](#Documentation-ZipFile-disableEncryption) - disable encryption for all entries that are already in the archive.
- [ZipFile::disableEncryptionEntry](#Documentation-ZipFile-disableEncryptionEntry) - disable encryption of an entry defined by its name.
- [ZipFile::extractTo](#Documentation-ZipFile-extractTo) - extract the archive contents.
- [ZipFile::getAllInfo](#Documentation-ZipFile-getAllInfo) - returns detailed information about all entries in the archive.
- [ZipFile::getArchiveComment](#Documentation-ZipFile-getArchiveComment) - returns the Zip archive comment.
- [ZipFile::getEntryComment](#Documentation-ZipFile-getEntryComment) - returns the comment of an entry using the entry name.
- [ZipFile::getEntryContent](#Documentation-ZipFile-getEntryContent) - returns the entry contents using its name.
- [ZipFile::getEntryInfo](#Documentation-ZipFile-getEntryInfo) - returns detailed information about the entry in the archive.
- [ZipFile::getListFiles](#Documentation-ZipFile-getListFiles) - returns list of archive files.
- [ZipFile::hasEntry](#Documentation-ZipFile-hasEntry) - checks if there is an entry in the archive.
- [ZipFile::isDirectory](#Documentation-ZipFile-isDirectory) - checks that the entry in the archive is a directory.
- [ZipFile::matcher](#Documentation-ZipFile-matcher) - selecting entries in the archive to perform operations on them.
- [ZipFile::openFile](#Documentation-ZipFile-openFile) - opens a zip-archive from a file.
- [ZipFile::openFromString](#Documentation-ZipFile-openFromString) - opens a zip-archive from a string.
- [ZipFile::openFromStream](#Documentation-ZipFile-openFromStream) - opens a zip-archive from the stream.
- [ZipFile::outputAsAttachment](#Documentation-ZipFile-outputAsAttachment) - outputs a ZIP-archive to the browser.
- [ZipFile::outputAsResponse](#Documentation-ZipFile-outputAsResponse) - outputs a ZIP-archive as PSR-7 Response.
- [ZipFile::outputAsString](#Documentation-ZipFile-outputAsString) - outputs a ZIP-archive as string.
- [ZipFile::rename](#Documentation-ZipFile-rename) - renames an entry defined by its name.
- [ZipFile::rewrite](#Documentation-ZipFile-rewrite) - save changes and re-open the changed archive.
- [ZipFile::saveAsFile](#Documentation-ZipFile-saveAsFile) - saves the archive to a file.
- [ZipFile::saveAsStream](#Documentation-ZipFile-saveAsStream) - writes the archive to the stream.
- [ZipFile::setArchiveComment](#Documentation-ZipFile-setArchiveComment) - set the comment of a ZIP archive.
- [ZipFile::setCompressionLevel](#Documentation-ZipFile-setCompressionLevel) - set the compression level for all files in the archive.
- [ZipFile::setCompressionLevelEntry](#Documentation-ZipFile-setCompressionLevelEntry) - sets the compression level for the entry by its name.
- [ZipFile::setCompressionMethodEntry](#Documentation-ZipFile-setCompressionMethodEntry) - sets the compression method for the entry by its name.
- [ZipFile::setEntryComment](#Documentation-ZipFile-setEntryComment) - set the comment of an entry defined by its name.
- [ZipFile::setReadPassword](#Documentation-ZipFile-setReadPassword) - set the password for the open archive.
- [ZipFile::setReadPasswordEntry](#Documentation-ZipFile-setReadPasswordEntry) - sets a password for reading of an entry defined by its name.
- ~~ZipFile::withNewPassword~~ - is an deprecated method, use the [ZipFile::setPassword](#Documentation-ZipFile-setPassword) method.
- [ZipFile::setPassword](#Documentation-ZipFile-setPassword) - sets a new password for all files in the archive.
- [ZipFile::setPasswordEntry](#Documentation-ZipFile-setPasswordEntry) - sets a new password of an entry defined by its name.
- [ZipFile::setZipAlign](#Documentation-ZipFile-setZipAlign) - sets the alignment of the archive to optimize APK files (Android packages).
- [ZipFile::unchangeAll](#Documentation-ZipFile-unchangeAll) - undo all changes done in the archive.
- [ZipFile::unchangeArchiveComment](#Documentation-ZipFile-unchangeArchiveComment) - undo changes to the archive comment.
- [ZipFile::unchangeEntry](#Documentation-ZipFile-unchangeEntry) - undo changes of an entry defined by its name.
- ~~ZipFile::withoutPassword~~ - is an deprecated method, use the [ZipFile::disableEncryption](#Documentation-ZipFile-disableEncryption) method.
- ~~ZipFile::withReadPassword~~ - is an deprecated method, use the [ZipFile::setReadPassword](#Documentation-ZipFile-setReadPassword) method.
### Documentation:
#### Overview of methods of the class `\PhpZip\ZipFile`
- [ZipFile::__construct](#zipfile__construct) - initializes the ZIP archive.
- [ZipFile::addAll](#zipfileaddall) - adds all entries from an array.
- [ZipFile::addDir](#zipfileadddir) - adds files to the archive from the directory on the specified path without subdirectories.
- [ZipFile::addDirRecursive](#zipfileadddirrecursive) - adds files to the archive from the directory on the specified path with subdirectories.
- [ZipFile::addEmptyDir](#zipfileaddemptydir) - add a new directory.
- [ZipFile::addFile](#zipfileaddfile) - adds a file to a ZIP archive from the given path.
- [ZipFile::addSplFile](#zipfileaddsplfile) - adds a `\SplFileInfo` to a ZIP archive.
- [ZipFile::addFromFinder](#zipfileaddfromfinder) - adds files from the `Symfony\Component\Finder\Finder` to a ZIP archive.
- [ZipFile::addFilesFromIterator](#zipfileaddfilesfromiterator) - adds files from the iterator of directories.
- [ZipFile::addFilesFromGlob](#zipfileaddfilesfromglob) - adds files from a directory by glob pattern without subdirectories.
- [ZipFile::addFilesFromGlobRecursive](#zipfileaddfilesfromglobrecursive) - adds files from a directory by glob pattern with subdirectories.
- [ZipFile::addFilesFromRegex](#zipfileaddfilesfromregex) - adds files from a directory by PCRE pattern without subdirectories.
- [ZipFile::addFilesFromRegexRecursive](#zipfileaddfilesfromregexrecursive) - adds files from a directory by PCRE pattern with subdirectories.
- [ZipFile::addFromStream](#zipfileaddfromstream) - adds an entry from the stream to the ZIP archive.
- [ZipFile::addFromString](#zipfileaddfromstring) - adds a file to a ZIP archive using its contents.
- [ZipFile::close](#zipfileclose) - close the archive.
- [ZipFile::count](#zipfilecount) - returns the number of entries in the archive.
- [ZipFile::deleteFromName](#zipfiledeletefromname) - deletes an entry in the archive using its name.
- [ZipFile::deleteFromGlob](#zipfiledeletefromglob) - deletes an entries in the archive using glob pattern.
- [ZipFile::deleteFromRegex](#zipfiledeletefromregex) - deletes an entries in the archive using PCRE pattern.
- [ZipFile::deleteAll](#zipfiledeleteall) - deletes all entries in the ZIP archive.
- [ZipFile::disableEncryption](#zipfiledisableencryption) - disable encryption for all entries that are already in the archive.
- [ZipFile::disableEncryptionEntry](#zipfiledisableencryptionentry) - disable encryption of an entry defined by its name.
- [ZipFile::extractTo](#zipfileextractto) - extract the archive contents.
- [ZipFile::getArchiveComment](#zipfilegetarchivecomment) - returns the Zip archive comment.
- [ZipFile::getEntryComment](#zipfilegetentrycomment) - returns the comment of an entry using the entry name.
- [ZipFile::getEntryContent](#zipfilegetentrycontent) - returns the entry contents using its name.
- [ZipFile::getListFiles](#zipfilegetlistfiles) - returns list of archive files.
- [ZipFile::hasEntry](#zipfilehasentry) - checks if there is an entry in the archive.
- [ZipFile::isDirectory](#zipfileisdirectory) - checks that the entry in the archive is a directory.
- [ZipFile::matcher](#zipfilematcher) - selecting entries in the archive to perform operations on them.
- [ZipFile::openFile](#zipfileopenfile) - opens a zip-archive from a file.
- [ZipFile::openFromString](#zipfileopenfromstring) - opens a zip-archive from a string.
- [ZipFile::openFromStream](#zipfileopenfromstream) - opens a zip-archive from the stream.
- [ZipFile::outputAsAttachment](#zipfileoutputasattachment) - outputs a ZIP-archive to the browser.
- [ZipFile::outputAsPsr7Response](#zipfileoutputaspsr7response) - outputs a ZIP-archive as PSR-7 Response.
- [ZipFile::outputAsSymfonyResponse](#zipfileoutputaspsr7response) - outputs a ZIP-archive as Symfony Response.
- [ZipFile::outputAsString](#zipfileoutputasstring) - outputs a ZIP-archive as string.
- [ZipFile::rename](#zipfilerename) - renames an entry defined by its name.
- [ZipFile::rewrite](#zipfilerewrite) - save changes and re-open the changed archive.
- [ZipFile::saveAsFile](#zipfilesaveasfile) - saves the archive to a file.
- [ZipFile::saveAsStream](#zipfilesaveasstream) - writes the archive to the stream.
- [ZipFile::setArchiveComment](#zipfilesetarchivecomment) - set the comment of a ZIP archive.
- [ZipFile::setCompressionLevel](#zipfilesetcompressionlevel) - set the compression level for all files in the archive.
- [ZipFile::setCompressionLevelEntry](#zipfilesetcompressionlevelentry) - sets the compression level for the entry by its name.
- [ZipFile::setCompressionMethodEntry](#zipfilesetcompressionmethodentry) - sets the compression method for the entry by its name.
- [ZipFile::setEntryComment](#zipfilesetentrycomment) - set the comment of an entry defined by its name.
- [ZipFile::setReadPassword](#zipfilesetreadpassword) - set the password for the open archive.
- [ZipFile::setReadPasswordEntry](#zipfilesetreadpasswordentry) - sets a password for reading of an entry defined by its name.
- [ZipFile::setPassword](#zipfilesetpassword) - sets a new password for all files in the archive.
- [ZipFile::setPasswordEntry](#zipfilesetpasswordentry) - sets a new password of an entry defined by its name.
- [ZipFile::unchangeAll](#zipfileunchangeall) - undo all changes done in the archive.
- [ZipFile::unchangeArchiveComment](#zipfileunchangearchivecomment) - undo changes to the archive comment.
- [ZipFile::unchangeEntry](#zipfileunchangeentry) - undo changes of an entry defined by its name.
#### <a name="Documentation-Open-Zip-Archive"></a> Creation/Opening of ZIP-archive
<a name="Documentation-ZipFile-__construct"></a>**ZipFile::__construct** - initializes the ZIP archive.
#### Creation/Opening of ZIP-archive
##### ZipFile::__construct**
Initializes the ZIP archive
```php
$zipFile = new \PhpZip\ZipFile();
```
<a name="Documentation-ZipFile-openFile"></a> **ZipFile::openFile** - opens a zip-archive from a file.
##### ZipFile::openFile
Opens a zip-archive from a file.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->openFile('file.zip');
```
<a name="Documentation-ZipFile-openFromString"></a> **ZipFile::openFromString** - opens a zip-archive from a string.
##### ZipFile::openFromString
Opens a zip-archive from a string.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->openFromString($stringContents);
```
<a name="Documentation-ZipFile-openFromStream"></a> **ZipFile::openFromStream** - opens a zip-archive from the stream.
##### ZipFile::openFromStream
Opens a zip-archive from the stream.
```php
$stream = fopen('file.zip', 'rb');
$zipFile = new \PhpZip\ZipFile();
$zipFile->openFromStream($stream);
```
#### <a name="Documentation-Open-Zip-Entries"></a> Reading entries from the archive
<a name="Documentation-ZipFile-count"></a> **ZipFile::count** - returns the number of entries in the archive.
#### Reading entries from the archive
##### ZipFile::count
Returns the number of entries in the archive.
```php
$zipFile = new \PhpZip\ZipFile();
@@ -200,7 +205,8 @@ $count = count($zipFile);
// or
$count = $zipFile->count();
```
<a name="Documentation-ZipFile-getListFiles"></a> **ZipFile::getListFiles** - returns list of archive files.
##### ZipFile::getListFiles
Returns list of archive files.
```php
$zipFile = new \PhpZip\ZipFile();
$listFiles = $zipFile->getListFiles();
@@ -213,7 +219,8 @@ $listFiles = $zipFile->getListFiles();
// 3 => '0',
// )
```
<a name="Documentation-ZipFile-getEntryContent"></a> **ZipFile::getEntryContent** - returns the entry contents using its name.
##### ZipFile::getEntryContent
Returns the entry contents using its name.
```php
// $entryName = 'path/to/example-entry-name.txt';
$zipFile = new \PhpZip\ZipFile();
@@ -222,7 +229,8 @@ $contents = $zipFile[$entryName];
// or
$contents = $zipFile->getEntryContents($entryName);
```
<a name="Documentation-ZipFile-hasEntry"></a> **ZipFile::hasEntry** - checks if there is an entry in the archive.
##### ZipFile::hasEntry
Checks if there is an entry in the archive.
```php
// $entryName = 'path/to/example-entry-name.txt';
$zipFile = new \PhpZip\ZipFile();
@@ -231,14 +239,16 @@ $hasEntry = isset($zipFile[$entryName]);
// or
$hasEntry = $zipFile->hasEntry($entryName);
```
<a name="Documentation-ZipFile-isDirectory"></a> **ZipFile::isDirectory** - checks that the entry in the archive is a directory.
##### ZipFile::isDirectory
Checks that the entry in the archive is a directory.
```php
// $entryName = 'path/to/';
$zipFile = new \PhpZip\ZipFile();
$isDirectory = $zipFile->isDirectory($entryName);
```
<a name="Documentation-ZipFile-extractTo"></a> **ZipFile::extractTo** - extract the archive contents.
##### ZipFile::extractTo
Extract the archive contents.
The directory must exist.
```php
$zipFile = new \PhpZip\ZipFile();
@@ -256,7 +266,7 @@ $extractOnlyFiles = [
$zipFile = new \PhpZip\ZipFile();
$zipFile->extractTo($toDirectory, $extractOnlyFiles);
```
#### <a name="Documentation-Zip-Iterate"></a> Iterating entries
#### Iterating entries
`ZipFile` is an iterator.
Can iterate all the entries in the `foreach` loop.
```php
@@ -281,28 +291,21 @@ while ($iterator->valid())
$iterator->next();
}
```
#### <a name="Documentation-Zip-Info"></a> Getting information about entries
<a name="Documentation-ZipFile-getArchiveComment"></a> **ZipFile::getArchiveComment** - returns the Zip archive comment.
#### Getting information about entries
##### ZipFile::getArchiveComment
Returns the Zip archive comment.
```php
$zipFile = new \PhpZip\ZipFile();
$commentArchive = $zipFile->getArchiveComment();
```
<a name="Documentation-ZipFile-getEntryComment"></a> **ZipFile::getEntryComment** - returns the comment of an entry using the entry name.
##### ZipFile::getEntryComment
Returns the comment of an entry using the entry name.
```php
$zipFile = new \PhpZip\ZipFile();
$commentEntry = $zipFile->getEntryComment($entryName);
```
<a name="Documentation-ZipFile-getEntryInfo"></a> **ZipFile::getEntryInfo** - returns detailed information about the entry in the archive
```php
$zipFile = new \PhpZip\ZipFile();
$zipInfo = $zipFile->getEntryInfo('file.txt');
```
<a name="Documentation-ZipFile-getAllInfo"></a> **ZipFile::getAllInfo** - returns detailed information about all entries in the archive.
```php
$zipAllInfo = $zipFile->getAllInfo();
```
#### <a name="Documentation-Add-Zip-Entries"></a> Adding entries to the archive
#### Adding entries to the archive
All methods of adding entries to a ZIP archive allow you to specify a method for compressing content.
The following methods of compression are available:
@@ -310,7 +313,8 @@ The following methods of compression are available:
- `\PhpZip\Constants\ZipCompressionMethod::DEFLATED` - Deflate compression
- `\PhpZip\Constants\ZipCompressionMethod::BZIP2` - Bzip2 compression with the extension `ext-bz2`
<a name="Documentation-ZipFile-addFile"></a> **ZipFile::addFile** - adds a file to a ZIP archive from the given path.
##### ZipFile::addFile
Adds a file to a ZIP archive from the given path.
```php
$zipFile = new \PhpZip\ZipFile();
// $file = '...../file.ext';
@@ -325,8 +329,8 @@ $zipFile->addFile($file, $entryName, \PhpZip\Constants\ZipCompressionMethod::STO
$zipFile->addFile($file, $entryName, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addFile($file, $entryName, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addSplFile"></a>
**ZipFile::addSplFile"** - adds a `\SplFileInfo` to a ZIP archive.
##### ZipFile::addSplFile
Adds a `\SplFileInfo` to a ZIP archive.
```php
// $file = '...../file.ext';
// $entryName = 'file2.ext'
@@ -344,9 +348,8 @@ $zipFile->addSplFile($splFile, $entryName, $options = [
\PhpZip\Constants\ZipOptions::COMPRESSION_METHOD => \PhpZip\Constants\ZipCompressionMethod::DEFLATED,
]);
```
<a name="Documentation-ZipFile-addFromFinder"></a>
**ZipFile::addFromFinder"** - adds files from the `Symfony\Component\Finder\Finder` to a ZIP archive.
https://symfony.com/doc/current/components/finder.html
##### ZipFile::addFromFinder
Adds files from the [`Symfony\Component\Finder\Finder`](https://symfony.com/doc/current/components/finder.html) to a ZIP archive.
```php
$finder = new \Symfony\Component\Finder\Finder();
$finder
@@ -362,7 +365,8 @@ $zipFile->addFromFinder($finder, $options = [
\PhpZip\Constants\ZipOptions::MODIFIED_TIME => new \DateTimeImmutable('-1 day 5 min')
]);
```
<a name="Documentation-ZipFile-addFromString"></a> **ZipFile::addFromString** - adds a file to a ZIP archive using its contents.
##### ZipFile::addFromString
Adds a file to a ZIP archive using its contents.
```php
$zipFile = new \PhpZip\ZipFile();
@@ -375,7 +379,8 @@ $zipFile->addFromString($entryName, $contents, \PhpZip\Constants\ZipCompressionM
$zipFile->addFromString($entryName, $contents, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addFromString($entryName, $contents, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addFromStream"></a> **ZipFile::addFromStream** - adds a entry from the stream to the ZIP archive.
##### ZipFile::addFromStream
Adds an entry from the stream to the ZIP archive.
```php
$zipFile = new \PhpZip\ZipFile();
// $stream = fopen(..., 'rb');
@@ -389,7 +394,8 @@ $zipFile->addFromStream($stream, $entryName, \PhpZip\Constants\ZipCompressionMet
$zipFile->addFromStream($stream, $entryName, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addFromStream($stream, $entryName, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addEmptyDir"></a> **ZipFile::addEmptyDir** - add a new directory.
##### ZipFile::addEmptyDir
Add a new directory.
```php
$zipFile = new \PhpZip\ZipFile();
// $path = "path/to/";
@@ -397,7 +403,8 @@ $zipFile->addEmptyDir($path);
// or
$zipFile[$path] = null;
```
<a name="Documentation-ZipFile-addAll"></a> **ZipFile::addAll** - adds all entries from an array.
##### ZipFile::addAll
Adds all entries from an array.
```php
$entries = [
'file.txt' => 'file contents', // add an entry from the string contents
@@ -409,7 +416,8 @@ $entries = [
$zipFile = new \PhpZip\ZipFile();
$zipFile->addAll($entries);
```
<a name="Documentation-ZipFile-addDir"></a> **ZipFile::addDir** - adds files to the archive from the directory on the specified path without subdirectories.
##### ZipFile::addDir
Adds files to the archive from the directory on the specified path without subdirectories.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->addDir($dirName);
@@ -423,7 +431,8 @@ $zipFile->addDir($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::S
$zipFile->addDir($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addDir($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addDirRecursive"></a> **ZipFile::addDirRecursive** - adds files to the archive from the directory on the specified path with subdirectories.
##### ZipFile::addDirRecursive
Adds files to the archive from the directory on the specified path with subdirectories.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->addDirRecursive($dirName);
@@ -437,7 +446,8 @@ $zipFile->addDirRecursive($dirName, $localPath, \PhpZip\Constants\ZipCompression
$zipFile->addDirRecursive($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addDirRecursive($dirName, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addFilesFromIterator"></a> **ZipFile::addFilesFromIterator** - adds files from the iterator of directories.
##### ZipFile::addFilesFromIterator
Adds files from the iterator of directories.
```php
// $directoryIterator = new \DirectoryIterator($dir); // without subdirectories
// $directoryIterator = new \RecursiveDirectoryIterator($dir); // with subdirectories
@@ -474,7 +484,8 @@ $ignoreIterator = new \PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator(
$zipFile->addFilesFromIterator($ignoreIterator);
```
<a name="Documentation-ZipFile-addFilesFromGlob"></a> **ZipFile::addFilesFromGlob** - adds files from a directory by [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) without subdirectories.
##### ZipFile::addFilesFromGlob
Adds files from a directory by [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) without subdirectories.
```php
$globPattern = '**.{jpg,jpeg,png,gif}'; // example glob pattern -> add all .jpg, .jpeg, .png and .gif files
@@ -490,7 +501,8 @@ $zipFile->addFilesFromGlob($dir, $globPattern, $localPath, \PhpZip\Constants\Zip
$zipFile->addFilesFromGlob($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addFilesFromGlob($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addFilesFromGlobRecursive"></a> **ZipFile::addFilesFromGlobRecursive** - adds files from a directory by [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) with subdirectories.
##### ZipFile::addFilesFromGlobRecursive
Adds files from a directory by [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) with subdirectories.
```php
$globPattern = '**.{jpg,jpeg,png,gif}'; // example glob pattern -> add all .jpg, .jpeg, .png and .gif files
@@ -506,7 +518,8 @@ $zipFile->addFilesFromGlobRecursive($dir, $globPattern, $localPath, \PhpZip\Cons
$zipFile->addFilesFromGlobRecursive($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addFilesFromGlobRecursive($dir, $globPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addFilesFromRegex"></a> **ZipFile::addFilesFromRegex** - adds files from a directory by [PCRE pattern](https://en.wikipedia.org/wiki/Regular_expression) without subdirectories.
##### ZipFile::addFilesFromRegex
Adds files from a directory by [PCRE pattern](https://en.wikipedia.org/wiki/Regular_expression) without subdirectories.
```php
$regexPattern = '/\.(jpe?g|png|gif)$/si'; // example regex pattern -> add all .jpg, .jpeg, .png and .gif files
@@ -522,11 +535,11 @@ $zipFile->addFilesFromRegex($dir, $regexPattern, $localPath, \PhpZip\Constants\Z
$zipFile->addFilesFromRegex($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addFilesFromRegex($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
<a name="Documentation-ZipFile-addFilesFromRegexRecursive"></a> **ZipFile::addFilesFromRegexRecursive** - adds files from a directory by [PCRE pattern](https://en.wikipedia.org/wiki/Regular_expression) with subdirectories.
##### ZipFile::addFilesFromRegexRecursive
Adds files from a directory by [PCRE pattern](https://en.wikipedia.org/wiki/Regular_expression) with subdirectories.
```php
$regexPattern = '/\.(jpe?g|png|gif)$/si'; // example regex pattern -> add all .jpg, .jpeg, .png and .gif files
$zipFile->addFilesFromRegexRecursive($dir, $regexPattern);
// you can specify the path in the archive to which you want to put entries
@@ -538,38 +551,44 @@ $zipFile->addFilesFromRegexRecursive($dir, $regexPattern, $localPath, \PhpZip\Co
$zipFile->addFilesFromRegexRecursive($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::DEFLATED); // Deflate compression
$zipFile->addFilesFromRegexRecursive($dir, $regexPattern, $localPath, \PhpZip\Constants\ZipCompressionMethod::BZIP2); // BZIP2 compression
```
#### <a name="Documentation-Remove-Zip-Entries"></a> Deleting entries from the archive
<a name="Documentation-ZipFile-deleteFromName"></a> **ZipFile::deleteFromName** - deletes an entry in the archive using its name.
#### Deleting entries from the archive
##### ZipFile::deleteFromName
Deletes an entry in the archive using its name.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->deleteFromName($entryName);
```
<a name="Documentation-ZipFile-deleteFromGlob"></a> **ZipFile::deleteFromGlob** - deletes a entries in the archive using [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)).
##### ZipFile::deleteFromGlob
Deletes a entries in the archive using [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)).
```php
$globPattern = '**.{jpg,jpeg,png,gif}'; // example glob pattern -> delete all .jpg, .jpeg, .png and .gif files
$zipFile = new \PhpZip\ZipFile();
$zipFile->deleteFromGlob($globPattern);
```
<a name="Documentation-ZipFile-deleteFromRegex"></a> **ZipFile::deleteFromRegex** - deletes a entries in the archive using [PCRE pattern](https://en.wikipedia.org/wiki/Regular_expression).
##### ZipFile::deleteFromRegex
Deletes a entries in the archive using [PCRE pattern](https://en.wikipedia.org/wiki/Regular_expression).
```php
$regexPattern = '/\.(jpe?g|png|gif)$/si'; // example regex pattern -> delete all .jpg, .jpeg, .png and .gif files
$zipFile = new \PhpZip\ZipFile();
$zipFile->deleteFromRegex($regexPattern);
```
<a name="Documentation-ZipFile-deleteAll"></a> **ZipFile::deleteAll** - deletes all entries in the ZIP archive.
##### ZipFile::deleteAll
Deletes all entries in the ZIP archive.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->deleteAll();
```
#### <a name="Documentation-Entries"></a> Working with entries and archive
<a name="Documentation-ZipFile-rename"></a> **ZipFile::rename** - renames an entry defined by its name.
#### Working with entries and archive
##### ZipFile::rename
Renames an entry defined by its name.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->rename($oldName, $newName);
```
<a name="Documentation-ZipFile-setCompressionLevel"></a> **ZipFile::setCompressionLevel** - set the compression level for all files in the archive.
##### ZipFile::setCompressionLevel
Set the compression level for all files in the archive.
> _Note that this method does not apply to entries that are added after this method is run._
@@ -580,14 +599,16 @@ The values range from 1 (`\PhpZip\Constants\ZipCompressionLevel::SUPER_FAST`) to
$zipFile = new \PhpZip\ZipFile();
$zipFile->setCompressionLevel(\PhpZip\Constants\ZipCompressionLevel::MAXIMUM);
```
<a name="Documentation-ZipFile-setCompressionLevelEntry"></a> **ZipFile::setCompressionLevelEntry** - sets the compression level for the entry by its name.
##### ZipFile::setCompressionLevelEntry
Sets the compression level for the entry by its name.
The values range from 1 (`\PhpZip\Constants\ZipCompressionLevel::SUPER_FAST`) to 9 (`\PhpZip\Constants\ZipCompressionLevel::MAXIMUM`) are supported. The higher the number, the better and longer the compression.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->setCompressionLevelEntry($entryName, \PhpZip\Constants\ZipCompressionLevel::FAST);
```
<a name="Documentation-ZipFile-setCompressionMethodEntry"></a> **ZipFile::setCompressionMethodEntry** - sets the compression method for the entry by its name.
##### ZipFile::setCompressionMethodEntry
Sets the compression method for the entry by its name.
The following compression methods are available:
- `\PhpZip\Constants\ZipCompressionMethod::STORED` - No compression
@@ -597,17 +618,20 @@ The following compression methods are available:
$zipFile = new \PhpZip\ZipFile();
$zipFile->setCompressionMethodEntry($entryName, \PhpZip\Constants\ZipCompressionMethod::DEFLATED);
```
<a name="Documentation-ZipFile-setArchiveComment"></a> **ZipFile::setArchiveComment** - set the comment of a ZIP archive.
##### ZipFile::setArchiveComment
Set the comment of a ZIP archive.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->setArchiveComment($commentArchive);
```
<a name="Documentation-ZipFile-setEntryComment"></a> **ZipFile::setEntryComment** - set the comment of an entry defined by its name.
##### ZipFile::setEntryComment
Set the comment of an entry defined by its name.
```php
$zipFile = new \PhpZip\ZipFile();
$zipFile->setEntryComment($entryName, $comment);
```
<a name="Documentation-ZipFile-matcher"></a> **ZipFile::matcher** - selecting entries in the archive to perform operations on them.
##### ZipFile::matcher
Selecting entries in the archive to perform operations on them.
```php
$zipFile = new \PhpZip\ZipFile();
$matcher = $zipFile->matcher();
@@ -661,7 +685,7 @@ $matcher->setPassword($password, $encryptionMethod); // sets a new password and
$matcher->setEncryptionMethod($encryptionMethod); // sets the encryption method to the selected entries
$matcher->disableEncryption(); // disables encryption for selected entries
```
#### <a name="Documentation-Password"></a> Working with passwords
#### Working with passwords
Implemented support for encryption methods:
- `\PhpZip\Constants\ZipEncryptionMethod::PKWARE` - Traditional PKWARE encryption (legacy)
@@ -669,17 +693,20 @@ Implemented support for encryption methods:
- `\PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_192` - WinZip AES encryption 192 bit
- `\PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_128` - WinZip AES encryption 128 bit
<a name="Documentation-ZipFile-setReadPassword"></a> **ZipFile::setReadPassword** - set the password for the open archive.
##### ZipFile::setReadPassword
Set the password for the open archive.
> _Setting a password is not required for adding new entries or deleting existing ones, but if you want to extract the content or change the method / compression level, the encryption method, or change the password, in this case the password must be specified._
```php
$zipFile->setReadPassword($password);
```
<a name="Documentation-ZipFile-setReadPasswordEntry"></a> **ZipFile::setReadPasswordEntry** - gets a password for reading of an entry defined by its name.
##### ZipFile::setReadPasswordEntry
Gets a password for reading of an entry defined by its name.
```php
$zipFile->setReadPasswordEntry($entryName, $password);
```
<a name="Documentation-ZipFile-setPassword"></a> **ZipFile::setPassword** - sets a new password for all files in the archive.
##### ZipFile::setPassword
Sets a new password for all files in the archive.
> _Note that this method does not apply to entries that are added after this method is run._
```php
@@ -690,7 +717,8 @@ You can set the encryption method:
$encryptionMethod = \PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_256;
$zipFile->setPassword($password, $encryptionMethod);
```
<a name="Documentation-ZipFile-setPasswordEntry"></a> **ZipFile::setPasswordEntry** - sets a new password of an entry defined by its name.
##### ZipFile::setPasswordEntry
Sets a new password of an entry defined by its name.
```php
$zipFile->setPasswordEntry($entryName, $password);
```
@@ -699,56 +727,54 @@ You can set the encryption method:
$encryptionMethod = \PhpZip\Constants\ZipEncryptionMethod::WINZIP_AES_256;
$zipFile->setPasswordEntry($entryName, $password, $encryptionMethod);
```
<a name="Documentation-ZipFile-disableEncryption"></a> **ZipFile::disableEncryption** - disable encryption for all entries that are already in the archive.
##### ZipFile::disableEncryption
Disable encryption for all entries that are already in the archive.
> _Note that this method does not apply to entries that are added after this method is run._
```php
$zipFile->disableEncryption();
```
<a name="Documentation-ZipFile-disableEncryptionEntry"></a> **ZipFile::disableEncryptionEntry** - disable encryption of an entry defined by its name.
##### ZipFile::disableEncryptionEntry
Disable encryption of an entry defined by its name.
```php
$zipFile->disableEncryptionEntry($entryName);
```
#### <a name="Documentation-ZipAlign-Usage"></a> zipalign
<a name="Documentation-ZipFile-setZipAlign"></a> **ZipFile::setZipAlign** - sets the alignment of the archive to optimize APK files (Android packages).
This method adds padding to unencrypted and not compressed entries, to optimize memory consumption in the Android system. It is recommended to use for `APK` files. The file may grow slightly.
This method is an alternative to executing the `zipalign -f -v 4 filename.zip`.
More details can be found on the [link](https://developer.android.com/studio/command-line/zipalign.html).
```php
$zipFile->setZipAlign(4);
```
#### <a name="Documentation-Unchanged"></a> Undo changes
<a name="Documentation-ZipFile-unchangeAll"></a> **ZipFile::unchangeAll** - undo all changes done in the archive.
#### Undo changes
##### ZipFile::unchangeAll
Undo all changes done in the archive.
```php
$zipFile->unchangeAll();
```
<a name="Documentation-ZipFile-unchangeArchiveComment"></a> **ZipFile::unchangeArchiveComment** - undo changes to the archive comment.
##### ZipFile::unchangeArchiveComment
Undo changes to the archive comment.
```php
$zipFile->unchangeArchiveComment();
```
<a name="Documentation-ZipFile-unchangeEntry"></a> **ZipFile::unchangeEntry** - undo changes of an entry defined by its name.
##### ZipFile::unchangeEntry
Undo changes of an entry defined by its name.
```php
$zipFile->unchangeEntry($entryName);
```
#### <a name="Documentation-Save-Or-Output-Entries"></a> Saving a file or output to a browser
<a name="Documentation-ZipFile-saveAsFile"></a> **ZipFile::saveAsFile** - saves the archive to a file.
#### Saving a file or output to a browser
##### ZipFile::saveAsFile
Saves the archive to a file.
```php
$zipFile->saveAsFile($filename);
```
<a name="Documentation-ZipFile-saveAsStream"></a> **ZipFile::saveAsStream** - writes the archive to the stream.
##### ZipFile::saveAsStream
Writes the archive to the stream.
```php
// $fp = fopen($filename, 'w+b');
$zipFile->saveAsStream($fp);
```
<a name="Documentation-ZipFile-outputAsString"></a> **ZipFile::outputAsString** - outputs a ZIP-archive as string.
##### ZipFile::outputAsString
Outputs a ZIP-archive as string.
```php
$rawZipArchiveBytes = $zipFile->outputAsString();
```
<a name="Documentation-ZipFile-outputAsAttachment"></a> **ZipFile::outputAsAttachment** - outputs a ZIP-archive to the browser.
##### ZipFile::outputAsAttachment
Outputs a ZIP-archive to the browser.
```php
$zipFile->outputAsAttachment($outputFilename);
```
@@ -757,28 +783,70 @@ You can set the Mime-Type:
$mimeType = 'application/zip';
$zipFile->outputAsAttachment($outputFilename, $mimeType);
```
<a name="Documentation-ZipFile-outputAsResponse"></a> **ZipFile::outputAsResponse** - outputs a ZIP-archive as [PSR-7 Response](http://www.php-fig.org/psr/psr-7/).
##### ZipFile::outputAsPsr7Response
Outputs a ZIP-archive as [PSR-7 Response](http://www.php-fig.org/psr/psr-7/).
The output method can be used in any PSR-7 compatible framework.
```php
// $response = ....; // instance Psr\Http\Message\ResponseInterface
$zipFile->outputAsResponse($response, $outputFilename);
$zipFile->outputAsPsr7Response($response, $outputFilename);
```
You can set the Mime-Type:
```php
$mimeType = 'application/zip';
$zipFile->outputAsResponse($response, $outputFilename, $mimeType);
$zipFile->outputAsPsr7Response($response, $outputFilename, $mimeType);
```
<a name="Documentation-ZipFile-rewrite"></a> **ZipFile::rewrite** - save changes and re-open the changed archive.
##### ZipFile::outputAsSymfonyResponse
Outputs a ZIP-archive as [Symfony Response](https://symfony.com/doc/current/components/http_foundation.html#response).
The output method can be used in Symfony framework.
```php
$response = $zipFile->outputAsSymfonyResponse($outputFilename);
```
You can set the Mime-Type:
```php
$mimeType = 'application/zip';
$response = $zipFile->outputAsSymfonyResponse($outputFilename, $mimeType);
```
Example use in Symfony Controller:
```php
<?php
namespace App\Controller;
use PhpZip\ZipFile;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DownloadZipController
{
/**
* @Route("/downloads/{id}")
*
* @throws \PhpZip\Exception\ZipException
*/
public function __invoke(string $id): Response
{
$zipFile = new ZipFile();
$zipFile['file'] = 'contents';
$outputFilename = $id . '.zip';
return $zipFile->outputAsSymfonyResponse($outputFilename);
}
}
```
##### ZipFile::rewrite
Save changes and re-open the changed archive.
```php
$zipFile->rewrite();
```
#### <a name="Documentation-Close-Zip-Archive"></a> Closing the archive
<a name="Documentation-ZipFile-close"></a> **ZipFile::close** - close the archive.
#### Closing the archive
##### ZipFile::close
Close the archive.
```php
$zipFile->close();
```
### <a name="Running-Tests"></a> Running the tests
### Running the tests
Install the dependencies for the development:
```bash
composer install --dev
@@ -787,11 +855,29 @@ Run the tests:
```bash
vendor/bin/phpunit
```
### <a name="Changelog"></a> Changelog
### Changelog
Changes are documented in the [releases page](https://github.com/Ne-Lexa/php-zip/releases).
### <a name="Upgrade"></a> Upgrade
#### <a name="Upgrade-v2-to-v3"></a> Upgrade version 2 to version 3.0
### Upgrade
#### Upgrade version 3 to version 4
Update the major version in the file `composer.json` to `^4.0`.
```json
{
"require": {
"nelexa/zip": "^4.0"
}
}
```
Then install updates using `Composer`:
```bash
composer update nelexa/zip
```
Update your code to work with the new version:
**BC**
- removed deprecated classes and methods.
- removed `zipalign` functional. This functionality will be placed in a separate package `nelexa/apkfile`.
#### Upgrade version 2 to version 3
Update the major version in the file `composer.json` to `^3.0`.
```json
{
@@ -827,5 +913,3 @@ Update your code to work with the new version:
+ `getLevel`
+ `setCompressionMethod`
+ `setEntryPassword`

View File

@@ -1,6 +1,14 @@
<?php
// see https://stackoverflow.com/questions/33299149/phpstorm-8-and-phpunit-problems-with-runinseparateprocess/37174348#37174348
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/vendor/autoload.php');
}

View File

@@ -1,14 +1,13 @@
{
"name": "nelexa/zip",
"type": "library",
"description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, ZipAlign tool, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.",
"description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.",
"keywords": [
"zip",
"unzip",
"archive",
"extract",
"winzip",
"zipalign",
"ziparchive"
],
"homepage": "https://github.com/Ne-Lexa/php-zip",
@@ -21,19 +20,24 @@
}
],
"require": {
"php": "^5.5.9 || ^7.0",
"php": "^7.4 || ^8.0",
"ext-zlib": "*",
"psr/http-message": "^1.0",
"paragonie/random_compat": ">=1 <9.99",
"symfony/finder": "^3.0|^4.0|^5.0"
"psr/http-message": "*",
"symfony/finder": "*"
},
"require-dev": {
"ext-iconv": "*",
"ext-bz2": "*",
"ext-openssl": "*",
"ext-fileinfo": "*",
"ext-xml": "*",
"ext-dom": "*",
"guzzlehttp/psr7": "^1.6",
"phpunit/phpunit": "^4.8|^5.7",
"symfony/var-dumper": "^3.0|^4.0|^5.0"
"phpunit/phpunit": "^9",
"symfony/var-dumper": "*",
"friendsofphp/php-cs-fixer": "^3.4.0",
"vimeo/psalm": "^4.6",
"symfony/http-foundation": "*"
},
"autoload": {
"psr-4": {
@@ -46,14 +50,15 @@
}
},
"suggest": {
"ext-iconv": "Needed to support convert zip entry name to requested character encoding",
"ext-openssl": "Needed to support encrypt zip entries or use ext-mcrypt",
"ext-mcrypt": "Needed to support encrypt zip entries or use ext-openssl",
"ext-bz2": "Needed to support BZIP2 compression",
"ext-fileinfo": "Needed to get mime-type file"
},
"minimum-stability": "stable",
"scripts": {
"php:fix": "php .php_cs --force",
"php:fix:debug": "php .php_cs"
"test": "phpunit --configuration phpunit.xml --do-not-cache-result --colors=always",
"test:coverage": "phpunit --configuration phpunit.xml --do-not-cache-result --colors=always --coverage-clover build/logs/clover.xml --coverage-html build/coverage"
}
}

1
logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,30 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="bootstrap.php">
<php>
<ini name="error_reporting" value="-1"/>
</php>
<testsuites>
<testsuite name="all_tests">
<directory>tests</directory>
</testsuite>
<testsuite name="only_fast_tests">
<directory>tests</directory>
<exclude>tests/SlowTests</exclude>
</testsuite>
<testsuite name="only_slow_tests">
<directory>tests/SlowTests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>src</directory>
</whitelist>
</filter>
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="bootstrap.php">
<coverage>
<include>
<directory>src</directory>
</include>
</coverage>
<php>
<ini name="error_reporting" value="-1"/>
</php>
<testsuites>
<testsuite name="all_tests">
<directory>tests</directory>
</testsuite>
<testsuite name="only_fast_tests">
<directory>tests</directory>
<exclude>tests/SlowTests</exclude>
</testsuite>
<testsuite name="only_slow_tests">
<directory>tests/SlowTests</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>large</group>
</exclude>
</groups>
</phpunit>

16
psalm.xml.dist Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<psalm
errorLevel="3"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
<directory name="tests"/>
</ignoreFiles>
</projectFiles>
</psalm>

View File

@@ -1,33 +1,39 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Interface DosAttrs.
*/
interface DosAttrs
{
/** @var int DOS File Attribute Read Only */
const DOS_READ_ONLY = 0x01;
public const DOS_READ_ONLY = 0x01;
/** @var int DOS File Attribute Hidden */
const DOS_HIDDEN = 0x02;
public const DOS_HIDDEN = 0x02;
/** @var int DOS File Attribute System */
const DOS_SYSTEM = 0x04;
public const DOS_SYSTEM = 0x04;
/** @var int DOS File Attribute Label */
const DOS_LABEL = 0x08;
public const DOS_LABEL = 0x08;
/** @var int DOS File Attribute Directory */
const DOS_DIRECTORY = 0x10;
public const DOS_DIRECTORY = 0x10;
/** @var int DOS File Attribute Archive */
const DOS_ARCHIVE = 0x20;
public const DOS_ARCHIVE = 0x20;
/** @var int DOS File Attribute Link */
const DOS_LINK = 0x40;
public const DOS_LINK = 0x40;
/** @var int DOS File Attribute Execute */
const DOS_EXE = 0x80;
public const DOS_EXE = 0x80;
}

View File

@@ -1,48 +1,52 @@
<?php
/** @noinspection PhpComposerExtensionStubsInspection */
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Class DosCodePage.
*/
final class DosCodePage
{
const CP_LATIN_US = 'cp437';
public const CP_LATIN_US = 'cp437';
const CP_GREEK = 'cp737';
public const CP_GREEK = 'cp737';
const CP_BALT_RIM = 'cp775';
public const CP_BALT_RIM = 'cp775';
const CP_LATIN1 = 'cp850';
public const CP_LATIN1 = 'cp850';
const CP_LATIN2 = 'cp852';
public const CP_LATIN2 = 'cp852';
const CP_CYRILLIC = 'cp855';
public const CP_CYRILLIC = 'cp855';
const CP_TURKISH = 'cp857';
public const CP_TURKISH = 'cp857';
const CP_PORTUGUESE = 'cp860';
public const CP_PORTUGUESE = 'cp860';
const CP_ICELANDIC = 'cp861';
public const CP_ICELANDIC = 'cp861';
const CP_HEBREW = 'cp862';
public const CP_HEBREW = 'cp862';
const CP_CANADA = 'cp863';
public const CP_CANADA = 'cp863';
const CP_ARABIC = 'cp864';
public const CP_ARABIC = 'cp864';
const CP_NORDIC = 'cp865';
public const CP_NORDIC = 'cp865';
const CP_CYRILLIC_RUSSIAN = 'cp866';
public const CP_CYRILLIC_RUSSIAN = 'cp866';
const CP_GREEK2 = 'cp869';
public const CP_GREEK2 = 'cp869';
const CP_THAI = 'cp874';
public const CP_THAI = 'cp874';
/** @var string[] */
private static $CP_CHARSETS = [
private const CP_CHARSETS = [
self::CP_LATIN_US,
self::CP_GREEK,
self::CP_BALT_RIM,
@@ -62,12 +66,9 @@ final class DosCodePage
];
/**
* @param string $str
* @param string $sourceEncoding
*
* @return string
* @noinspection PhpComposerExtensionStubsInspection
*/
public static function toUTF8($str, $sourceEncoding)
public static function toUTF8(string $str, string $sourceEncoding): string
{
$s = iconv($sourceEncoding, 'UTF-8', $str);
@@ -79,12 +80,9 @@ final class DosCodePage
}
/**
* @param string $str
* @param string $destEncoding
*
* @return string
* @noinspection PhpComposerExtensionStubsInspection
*/
public static function fromUTF8($str, $destEncoding)
public static function fromUTF8(string $str, string $destEncoding): string
{
$s = iconv('UTF-8', $destEncoding, $str);
@@ -98,8 +96,8 @@ final class DosCodePage
/**
* @return string[]
*/
public static function getCodePages()
public static function getCodePages(): array
{
return self::$CP_CHARSETS;
return self::CP_CHARSETS;
}
}

View File

@@ -1,17 +1,23 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* General purpose bit flag constants.
*/
interface GeneralPurposeBitFlag
{
/**
* General Purpose Bit Flag mask for encrypted data.
* Bit 0: If set, indicates that the file is encrypted.
*/
const ENCRYPTION = 1; // 1 << 0
public const ENCRYPTION = 1 << 0;
/**
* Compression Flag Bit 1 for method Deflating.
@@ -24,7 +30,7 @@ interface GeneralPurposeBitFlag
*
* @see GeneralPurposeBitFlag::COMPRESSION_FLAG2
*/
const COMPRESSION_FLAG1 = 2; // 1 << 1
public const COMPRESSION_FLAG1 = 1 << 1;
/**
* Compression Flag Bit 2 for method Deflating.
@@ -37,7 +43,7 @@ interface GeneralPurposeBitFlag
*
* @see GeneralPurposeBitFlag::COMPRESSION_FLAG1
*/
const COMPRESSION_FLAG2 = 4; // 1 << 2
public const COMPRESSION_FLAG2 = 1 << 2;
/**
* General Purpose Bit Flag mask for data descriptor.
@@ -47,7 +53,7 @@ interface GeneralPurposeBitFlag
* local header. The correct values are put in the data
* descriptor immediately following the compressed data.
*/
const DATA_DESCRIPTOR = 8; // 1 << 3
public const DATA_DESCRIPTOR = 1 << 3;
/**
* General Purpose Bit Flag mask for strong encryption.
@@ -58,7 +64,7 @@ interface GeneralPurposeBitFlag
* If AES encryption is used, the version needed to extract value
* MUST be at least 51.
*/
const STRONG_ENCRYPTION = 64; // 1 << 6
public const STRONG_ENCRYPTION = 1 << 6;
/**
* General Purpose Bit Flag mask for UTF-8.
@@ -67,5 +73,5 @@ interface GeneralPurposeBitFlag
* If this bit is set, the filename and comment fields
* for this file MUST be encoded using UTF-8. (see APPENDIX D)
*/
const UTF8 = 2048; // 1 << 11
public const UTF8 = 1 << 11;
}

View File

@@ -1,84 +1,90 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Unix stat constants.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
interface UnixStat
{
/** @var int unix file type mask */
const UNX_IFMT = 0170000;
public const UNX_IFMT = 0170000;
/** @var int unix regular file */
const UNX_IFREG = 0100000;
public const UNX_IFREG = 0100000;
/** @var int unix socket (BSD, not SysV or Amiga) */
const UNX_IFSOCK = 0140000;
public const UNX_IFSOCK = 0140000;
/** @var int unix symbolic link (not SysV, Amiga) */
const UNX_IFLNK = 0120000;
public const UNX_IFLNK = 0120000;
/** @var int unix block special (not Amiga) */
const UNX_IFBLK = 0060000;
public const UNX_IFBLK = 0060000;
/** @var int unix directory */
const UNX_IFDIR = 0040000;
public const UNX_IFDIR = 0040000;
/** @var int unix character special (not Amiga) */
const UNX_IFCHR = 0020000;
public const UNX_IFCHR = 0020000;
/** @var int unix fifo (BCC, not MSC or Amiga) */
const UNX_IFIFO = 0010000;
public const UNX_IFIFO = 0010000;
/** @var int unix set user id on execution */
const UNX_ISUID = 04000;
public const UNX_ISUID = 04000;
/** @var int unix set group id on execution */
const UNX_ISGID = 02000;
public const UNX_ISGID = 02000;
/** @var int unix directory permissions control */
const UNX_ISVTX = 01000;
public const UNX_ISVTX = 01000;
/** @var int unix record locking enforcement flag */
const UNX_ENFMT = 02000;
public const UNX_ENFMT = 02000;
/** @var int unix read, write, execute: owner */
const UNX_IRWXU = 00700;
public const UNX_IRWXU = 00700;
/** @var int unix read permission: owner */
const UNX_IRUSR = 00400;
public const UNX_IRUSR = 00400;
/** @var int unix write permission: owner */
const UNX_IWUSR = 00200;
public const UNX_IWUSR = 00200;
/** @var int unix execute permission: owner */
const UNX_IXUSR = 00100;
public const UNX_IXUSR = 00100;
/** @var int unix read, write, execute: group */
const UNX_IRWXG = 00070;
public const UNX_IRWXG = 00070;
/** @var int unix read permission: group */
const UNX_IRGRP = 00040;
public const UNX_IRGRP = 00040;
/** @var int unix write permission: group */
const UNX_IWGRP = 00020;
public const UNX_IWGRP = 00020;
/** @var int unix execute permission: group */
const UNX_IXGRP = 00010;
public const UNX_IXGRP = 00010;
/** @var int unix read, write, execute: other */
const UNX_IRWXO = 00007;
public const UNX_IRWXO = 00007;
/** @var int unix read permission: other */
const UNX_IROTH = 00004;
public const UNX_IROTH = 00004;
/** @var int unix write permission: other */
const UNX_IWOTH = 00002;
public const UNX_IWOTH = 00002;
/** @var int unix execute permission: other */
const UNX_IXOTH = 00001;
public const UNX_IXOTH = 00001;
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
@@ -27,28 +36,28 @@ namespace PhpZip\Constants;
interface ZipCompressionLevel
{
/** @var int Compression level for super fast compression. */
const SUPER_FAST = 1;
public const SUPER_FAST = 1;
/** @var int compression level for fast compression */
const FAST = 2;
public const FAST = 2;
/** @var int compression level for normal compression */
const NORMAL = 5;
public const NORMAL = 5;
/** @var int compression level for maximum compression */
const MAXIMUM = 9;
public const MAXIMUM = 9;
/**
* @var int int Minimum compression level
*
* @internal
*/
const LEVEL_MIN = self::SUPER_FAST;
public const LEVEL_MIN = self::SUPER_FAST;
/**
* @var int int Maximum compression level
*
* @internal
*/
const LEVEL_MAX = self::MAXIMUM;
public const LEVEL_MAX = self::MAXIMUM;
}

View File

@@ -1,28 +1,34 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
use PhpZip\Exception\ZipUnsupportMethodException;
/**
* Class ZipCompressionMethod.
*/
final class ZipCompressionMethod
{
/** @var int Compression method Store */
const STORED = 0;
public const STORED = 0;
/** @var int Compression method Deflate */
const DEFLATED = 8;
public const DEFLATED = 8;
/** @var int Compression method Bzip2 */
const BZIP2 = 12;
public const BZIP2 = 12;
/** @var int Compression method AES-Encryption */
const WINZIP_AES = 99;
public const WINZIP_AES = 99;
/** @var array Compression Methods */
private static $ZIP_COMPRESSION_METHODS = [
private const ZIP_COMPRESSION_METHODS = [
self::STORED => 'Stored',
1 => 'Shrunk',
2 => 'Reduced compression factor 1',
@@ -49,22 +55,15 @@ final class ZipCompressionMethod
self::WINZIP_AES => 'AES Encryption',
];
/**
* @param int $value
*
* @return string
*/
public static function getCompressionMethodName($value)
public static function getCompressionMethodName(int $value): string
{
return isset(self::$ZIP_COMPRESSION_METHODS[$value]) ?
self::$ZIP_COMPRESSION_METHODS[$value] :
'Unknown Method';
return self::ZIP_COMPRESSION_METHODS[$value] ?? 'Unknown Method';
}
/**
* @return int[]
*/
public static function getSupportMethods()
public static function getSupportMethods(): array
{
static $methods;
@@ -83,14 +82,10 @@ final class ZipCompressionMethod
}
/**
* @param int $compressionMethod
*
* @throws ZipUnsupportMethodException
*/
public static function checkSupport($compressionMethod)
public static function checkSupport(int $compressionMethod): void
{
$compressionMethod = (int) $compressionMethod;
if (!\in_array($compressionMethod, self::getSupportMethods(), true)) {
throw new ZipUnsupportMethodException(sprintf(
'Compression method %d (%s) is not supported.',

View File

@@ -1,38 +1,44 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Zip Constants.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
interface ZipConstants
{
/** @var int End Of Central Directory Record signature. */
const END_CD = 0x06054B50; // "PK\005\006"
public const END_CD = 0x06054B50; // "PK\005\006"
/** @var int Zip64 End Of Central Directory Record. */
const ZIP64_END_CD = 0x06064B50; // "PK\006\006"
public const ZIP64_END_CD = 0x06064B50; // "PK\006\006"
/** @var int Zip64 End Of Central Directory Locator. */
const ZIP64_END_CD_LOC = 0x07064B50; // "PK\006\007"
public const ZIP64_END_CD_LOC = 0x07064B50; // "PK\006\007"
/** @var int Central File Header signature. */
const CENTRAL_FILE_HEADER = 0x02014B50; // "PK\001\002"
public const CENTRAL_FILE_HEADER = 0x02014B50; // "PK\001\002"
/** @var int Local File Header signature. */
const LOCAL_FILE_HEADER = 0x04034B50; // "PK\003\004"
public const LOCAL_FILE_HEADER = 0x04034B50; // "PK\003\004"
/** @var int Data Descriptor signature. */
const DATA_DESCRIPTOR = 0x08074B50; // "PK\007\008"
public const DATA_DESCRIPTOR = 0x08074B50; // "PK\007\008"
/**
* @var int value stored in four-byte size and similar fields
* if ZIP64 extensions are used
*/
const ZIP64_MAGIC = 0xFFFFFFFF;
public const ZIP64_MAGIC = 0xFFFFFFFF;
/**
* Local File Header signature 4
@@ -47,7 +53,7 @@ interface ZipConstants
*
* @var int Local File Header filename position
*/
const LFH_FILENAME_LENGTH_POS = 26;
public const LFH_FILENAME_LENGTH_POS = 26;
/**
* The minimum length of the Local File Header record.
@@ -64,13 +70,13 @@ interface ZipConstants
* file name length 2
* extra field length 2
*/
const LFH_FILENAME_POS = 30;
public const LFH_FILENAME_POS = 30;
/** @var int the length of the Zip64 End Of Central Directory Locator */
const ZIP64_END_CD_LOC_LEN = 20;
public const ZIP64_END_CD_LOC_LEN = 20;
/** @var int the minimum length of the End Of Central Directory Record */
const END_CD_MIN_LEN = 22;
public const END_CD_MIN_LEN = 22;
/**
* The minimum length of the Zip64 End Of Central Directory Record.
@@ -95,5 +101,5 @@ interface ZipConstants
*
* @var int ZIP64 End Of Central Directory length
*/
const ZIP64_END_OF_CD_LEN = 56;
public const ZIP64_END_OF_CD_LEN = 56;
}

View File

@@ -1,30 +1,36 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
use PhpZip\Exception\InvalidArgumentException;
/**
* Class ZipEncryptionMethod.
*/
final class ZipEncryptionMethod
{
const NONE = -1;
public const NONE = -1;
/** @var int Traditional PKWARE encryption. */
const PKWARE = 0;
public const PKWARE = 0;
/** @var int WinZip AES-256 */
const WINZIP_AES_256 = 1;
public const WINZIP_AES_256 = 1;
/** @var int WinZip AES-128 */
const WINZIP_AES_128 = 2;
public const WINZIP_AES_128 = 2;
/** @var int WinZip AES-192 */
const WINZIP_AES_192 = 3;
public const WINZIP_AES_192 = 3;
/** @var array<int, string> */
private static $ENCRYPTION_METHODS = [
private const ENCRYPTION_METHODS = [
self::NONE => 'no encryption',
self::PKWARE => 'Traditional PKWARE encryption',
self::WINZIP_AES_128 => 'WinZip AES-128',
@@ -32,39 +38,20 @@ final class ZipEncryptionMethod
self::WINZIP_AES_256 => 'WinZip AES-256',
];
/**
* @param int $value
*
* @return string
*/
public static function getEncryptionMethodName($value)
public static function getEncryptionMethodName(int $value): string
{
$value = (int) $value;
return isset(self::$ENCRYPTION_METHODS[$value]) ?
self::$ENCRYPTION_METHODS[$value] :
'Unknown Encryption Method';
return self::ENCRYPTION_METHODS[$value] ?? 'Unknown Encryption Method';
}
/**
* @param int $encryptionMethod
*
* @return bool
*/
public static function hasEncryptionMethod($encryptionMethod)
public static function hasEncryptionMethod(int $encryptionMethod): bool
{
return isset(self::$ENCRYPTION_METHODS[$encryptionMethod]);
return isset(self::ENCRYPTION_METHODS[$encryptionMethod]);
}
/**
* @param int $encryptionMethod
*
* @return bool
*/
public static function isWinZipAesMethod($encryptionMethod)
public static function isWinZipAesMethod(int $encryptionMethod): bool
{
return \in_array(
(int) $encryptionMethod,
$encryptionMethod,
[
self::WINZIP_AES_256,
self::WINZIP_AES_192,
@@ -75,14 +62,10 @@ final class ZipEncryptionMethod
}
/**
* @param int $encryptionMethod
*
* @throws InvalidArgumentException
*/
public static function checkSupport($encryptionMethod)
public static function checkSupport(int $encryptionMethod): void
{
$encryptionMethod = (int) $encryptionMethod;
if (!self::hasEncryptionMethod($encryptionMethod)) {
throw new InvalidArgumentException(sprintf(
'Encryption method %d is not supported.',

View File

@@ -1,29 +1,68 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Interface ZipOptions.
*/
use PhpZip\IO\ZipReader;
use PhpZip\ZipFile;
interface ZipOptions
{
/**
* Boolean option for store just file names (skip directory names).
*
* @var string
* @see ZipFile::addFromFinder()
*/
const STORE_ONLY_FILES = 'only_files';
/** @var string */
const COMPRESSION_METHOD = 'compression_method';
/** @var string */
const MODIFIED_TIME = 'mtime';
public const STORE_ONLY_FILES = 'only_files';
/**
* @var string
* Uses the specified compression method.
*
* @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile()
*/
public const COMPRESSION_METHOD = 'compression_method';
/**
* Set the specified record modification time.
* The value can be {@see \DateTimeInterface}, integer timestamp
* or a string of any format.
*
* @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile()
*/
public const MODIFIED_TIME = 'mtime';
/**
* Specifies the encoding of the record name for cases when the UTF-8
* usage flag is not set.
*
* The most commonly used encodings are compiled into the constants
* of the {@see DosCodePage} class.
*
* @see ZipFile::openFile()
* @see ZipFile::openFromString()
* @see ZipFile::openFromStream()
* @see ZipReader::getDefaultOptions()
* @see DosCodePage::getCodePages()
*/
const CHARSET = 'charset';
public const CHARSET = 'charset';
/**
* Allows ({@see true}) or denies ({@see false}) unpacking unix symlinks.
*
* This is a potentially dangerous operation for uncontrolled zip files.
* By default is ({@see false}).
*
* @see https://josipfranjkovic.blogspot.com/2014/12/reading-local-files-from-facebooks.html
*/
public const EXTRACT_SYMLINKS = 'extract_symlinks';
}

View File

@@ -1,23 +1,29 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Class ZipPlatform.
*/
final class ZipPlatform
{
/** @var int MS-DOS OS */
const OS_DOS = 0;
public const OS_DOS = 0;
/** @var int Unix OS */
const OS_UNIX = 3;
public const OS_UNIX = 3;
/** MacOS platform */
const OS_MAC_OSX = 19;
/** @var int MacOS platform */
public const OS_MAC_OSX = 19;
/** @var array Zip Platforms */
private static $platforms = [
private const PLATFORMS = [
self::OS_DOS => 'MS-DOS',
1 => 'Amiga',
2 => 'OpenVMS',
@@ -41,13 +47,8 @@ final class ZipPlatform
30 => 'AtheOS/Syllable',
];
/**
* @param int $platform
*
* @return string
*/
public static function getPlatformName($platform)
public static function getPlatformName(int $platform): string
{
return isset(self::$platforms[$platform]) ? self::$platforms[$platform] : 'Unknown';
return self::PLATFORMS[$platform] ?? 'Unknown';
}
}

View File

@@ -1,22 +1,28 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Version needed to extract or software version.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT Section 4.4.3
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
interface ZipVersion
{
/** @var int 1.0 - Default value */
const v10_DEFAULT_MIN = 10;
public const v10_DEFAULT_MIN = 10;
/** @var int 1.1 - File is a volume label */
const v11_FILE_VOLUME_LABEL = 11;
public const v11_FILE_VOLUME_LABEL = 11;
/**
* 2.0 - File is a folder (directory)
@@ -25,22 +31,22 @@ interface ZipVersion
*
* @var int
*/
const v20_DEFLATED_FOLDER_ZIPCRYPTO = 20;
public const v20_DEFLATED_FOLDER_ZIPCRYPTO = 20;
/** @var int 2.1 - File is compressed using Deflate64(tm) */
const v21_DEFLATED64 = 21;
public const v21_DEFLATED64 = 21;
/** @var int 2.5 - File is compressed using PKWARE DCL Implode */
const v25_IMPLODED = 25;
public const v25_IMPLODED = 25;
/** @var int 2.7 - File is a patch data set */
const v27_PATCH_DATA = 27;
public const v27_PATCH_DATA = 27;
/** @var int 4.5 - File uses ZIP64 format extensions */
const v45_ZIP64_EXT = 45;
public const v45_ZIP64_EXT = 45;
/** @var int 4.6 - File is compressed using BZIP2 compression */
const v46_BZIP2 = 46;
public const v46_BZIP2 = 46;
/**
* 5.0 - File is encrypted using DES
@@ -50,7 +56,7 @@ interface ZipVersion
*
* @var int
*/
const v50_ENCR_DES_3DES_RC2_ORIG_RC4 = 50;
public const v50_ENCR_DES_3DES_RC2_ORIG_RC4 = 50;
/**
* 5.1 - File is encrypted using AES encryption
@@ -58,16 +64,16 @@ interface ZipVersion
*
* @var int
*/
const v51_ENCR_AES_RC2_CORRECT = 51;
public const v51_ENCR_AES_RC2_CORRECT = 51;
/** @var int 5.2 - File is encrypted using corrected RC2-64 encryption** */
const v52_ENCR_RC2_64_CORRECT = 52;
public const v52_ENCR_RC2_64_CORRECT = 52;
/** @var int 6.1 - File is encrypted using non-OAEP key wrapping*** */
const v61_ENCR_NON_OAE_KEY_WRAP = 61;
public const v61_ENCR_NON_OAE_KEY_WRAP = 61;
/** @var int 6.2 - Central directory encryption */
const v62_ENCR_CENTRAL_DIR = 62;
public const v62_ENCR_CENTRAL_DIR = 62;
/**
* 6.3 - File is compressed using LZMA
@@ -77,5 +83,5 @@ interface ZipVersion
*
* @var int
*/
const v63_LZMA_PPMD_BLOWFISH_TWOFISH = 63;
public const v63_LZMA_PPMD_BLOWFISH_TWOFISH = 63;
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
/**
@@ -8,34 +17,16 @@ namespace PhpZip\Exception;
* and the computed value from the decompressed data.
*
* The exception detail message is the name of the ZIP entry.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class Crc32Exception extends ZipException
{
/**
* Expected crc.
*
* @var int
*/
private $expectedCrc;
/** Expected crc. */
private int $expectedCrc;
/**
* Actual crc.
*
* @var int
*/
private $actualCrc;
/** Actual crc. */
private int $actualCrc;
/**
* Crc32Exception constructor.
*
* @param string $name
* @param int $expected
* @param int $actual
*/
public function __construct($name, $expected, $actual)
public function __construct(string $name, int $expected, int $actual)
{
parent::__construct(
sprintf(
@@ -51,20 +42,16 @@ class Crc32Exception extends ZipException
/**
* Returns expected crc.
*
* @return int
*/
public function getExpectedCrc()
public function getExpectedCrc(): int
{
return $this->expectedCrc;
}
/**
* Returns actual crc.
*
* @return int
*/
public function getActualCrc()
public function getActualCrc(): int
{
return $this->actualCrc;
}

View File

@@ -1,13 +1,19 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
/**
* Thrown to indicate that a method has been passed an illegal or
* inappropriate argument.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class InvalidArgumentException extends RuntimeException
{

View File

@@ -1,13 +1,19 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
/**
* Runtime exception.
* Exception thrown if an error which can only be found on runtime occurs.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class RuntimeException extends \RuntimeException
{

View File

@@ -1,12 +1,18 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
/**
* Thrown to indicate that an authenticated ZIP entry has been tampered with.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class ZipAuthenticationException extends ZipCryptoException
{

View File

@@ -1,13 +1,19 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
/**
* Thrown if there is an issue when reading or writing an encrypted ZIP file
* or entry.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class ZipCryptoException extends ZipException
{

View File

@@ -1,23 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
use PhpZip\Model\ZipEntry;
/**
* Thrown if entry not found.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class ZipEntryNotFoundException extends ZipException
{
/** @var string */
private $entryName;
private string $entryName;
/**
* ZipEntryNotFoundException constructor.
*
* @param ZipEntry|string $entryName
*/
public function __construct($entryName)
@@ -30,10 +33,7 @@ class ZipEntryNotFoundException extends ZipException
$this->entryName = $entryName;
}
/**
* @return string
*/
public function getEntryName()
public function getEntryName(): string
{
return $this->entryName;
}

View File

@@ -1,13 +1,19 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
/**
* Signals that a Zip exception of some sort has occurred.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @see \Exception
*/
class ZipException extends \Exception

View File

@@ -1,10 +1,16 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Exception;
/**
* Class ZipUnsupportMethodException.
*/
class ZipUnsupportMethodException extends ZipException
{
}

View File

@@ -1,301 +1,298 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\Pkware;
use PhpZip\Exception\RuntimeException;
use PhpZip\Exception\ZipAuthenticationException;
use PhpZip\Util\PackUtil;
use PhpZip\Util\MathUtil;
/**
* Traditional PKWARE Encryption Engine.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class PKCryptContext
{
/** Encryption header size */
const STD_DEC_HDR_SIZE = 12;
/** @var int Encryption header size */
public const STD_DEC_HDR_SIZE = 12;
/**
* Crc table.
*
* @var int[]|array
*/
private static $CRC_TABLE = [
private const CRC_TABLE = [
0x00000000,
0x77073096,
0xee0e612c,
0x990951ba,
0x076dc419,
0x706af48f,
0xe963a535,
0x9e6495a3,
0x0edb8832,
0x79dcb8a4,
0xe0d5e91e,
0x97d2d988,
0x09b64c2b,
0x7eb17cbd,
0xe7b82d07,
0x90bf1d91,
0x1db71064,
0x6ab020f2,
0xf3b97148,
0x84be41de,
0x1adad47d,
0x6ddde4eb,
0xf4d4b551,
0x83d385c7,
0x136c9856,
0x646ba8c0,
0xfd62f97a,
0x8a65c9ec,
0x14015c4f,
0x63066cd9,
0xfa0f3d63,
0x8d080df5,
0x3b6e20c8,
0x4c69105e,
0xd56041e4,
0xa2677172,
0x3c03e4d1,
0x4b04d447,
0xd20d85fd,
0xa50ab56b,
0x35b5a8fa,
0x42b2986c,
0xdbbbc9d6,
0xacbcf940,
0x32d86ce3,
0x45df5c75,
0xdcd60dcf,
0xabd13d59,
0x26d930ac,
0x51de003a,
0xc8d75180,
0xbfd06116,
0x21b4f4b5,
0x56b3c423,
0xcfba9599,
0xb8bda50f,
0x2802b89e,
0x5f058808,
0xc60cd9b2,
0xb10be924,
0x2f6f7c87,
0x58684c11,
0xc1611dab,
0xb6662d3d,
0x76dc4190,
0x01db7106,
0x98d220bc,
0xefd5102a,
0x71b18589,
0x06b6b51f,
0x9fbfe4a5,
0xe8b8d433,
0x7807c9a2,
0x0f00f934,
0x9609a88e,
0xe10e9818,
0x7f6a0dbb,
0x086d3d2d,
0x91646c97,
0xe6635c01,
0x6b6b51f4,
0x1c6c6162,
0x856530d8,
0xf262004e,
0x6c0695ed,
0x1b01a57b,
0x8208f4c1,
0xf50fc457,
0x65b0d9c6,
0x12b7e950,
0x8bbeb8ea,
0xfcb9887c,
0x62dd1ddf,
0x15da2d49,
0x8cd37cf3,
0xfbd44c65,
0x4db26158,
0x3ab551ce,
0xa3bc0074,
0xd4bb30e2,
0x4adfa541,
0x3dd895d7,
0xa4d1c46d,
0xd3d6f4fb,
0x4369e96a,
0x346ed9fc,
0xad678846,
0xda60b8d0,
0x44042d73,
0x33031de5,
0xaa0a4c5f,
0xdd0d7cc9,
0x5005713c,
0x270241aa,
0xbe0b1010,
0xc90c2086,
0x5768b525,
0x206f85b3,
0xb966d409,
0xce61e49f,
0x5edef90e,
0x29d9c998,
0xb0d09822,
0xc7d7a8b4,
0x59b33d17,
0x2eb40d81,
0xb7bd5c3b,
0xc0ba6cad,
0xedb88320,
0x9abfb3b6,
0x03b6e20c,
0x74b1d29a,
0xead54739,
0x9dd277af,
0x04db2615,
0x73dc1683,
0xe3630b12,
0x94643b84,
0x0d6d6a3e,
0x7a6a5aa8,
0xe40ecf0b,
0x9309ff9d,
0x0a00ae27,
0x7d079eb1,
0xf00f9344,
0x8708a3d2,
0x1e01f268,
0x6906c2fe,
0xf762575d,
0x806567cb,
0x196c3671,
0x6e6b06e7,
0xfed41b76,
0x89d32be0,
0x10da7a5a,
0x67dd4acc,
0xf9b9df6f,
0x8ebeeff9,
0x17b7be43,
0x60b08ed5,
0xd6d6a3e8,
0xa1d1937e,
0x38d8c2c4,
0x4fdff252,
0xd1bb67f1,
0xa6bc5767,
0x3fb506dd,
0x48b2364b,
0xd80d2bda,
0xaf0a1b4c,
0x36034af6,
0x41047a60,
0xdf60efc3,
0xa867df55,
0x316e8eef,
0x4669be79,
0xcb61b38c,
0xbc66831a,
0x256fd2a0,
0x5268e236,
0xcc0c7795,
0xbb0b4703,
0x220216b9,
0x5505262f,
0xc5ba3bbe,
0xb2bd0b28,
0x2bb45a92,
0x5cb36a04,
0xc2d7ffa7,
0xb5d0cf31,
0x2cd99e8b,
0x5bdeae1d,
0x9b64c2b0,
0xec63f226,
0x756aa39c,
0x026d930a,
0x9c0906a9,
0xeb0e363f,
0xEE0E612C,
0x990951BA,
0x076DC419,
0x706AF48F,
0xE963A535,
0x9E6495A3,
0x0EDB8832,
0x79DCB8A4,
0xE0D5E91E,
0x97D2D988,
0x09B64C2B,
0x7EB17CBD,
0xE7B82D07,
0x90BF1D91,
0x1DB71064,
0x6AB020F2,
0xF3B97148,
0x84BE41DE,
0x1ADAD47D,
0x6DDDE4EB,
0xF4D4B551,
0x83D385C7,
0x136C9856,
0x646BA8C0,
0xFD62F97A,
0x8A65C9EC,
0x14015C4F,
0x63066CD9,
0xFA0F3D63,
0x8D080DF5,
0x3B6E20C8,
0x4C69105E,
0xD56041E4,
0xA2677172,
0x3C03E4D1,
0x4B04D447,
0xD20D85FD,
0xA50AB56B,
0x35B5A8FA,
0x42B2986C,
0xDBBBC9D6,
0xACBCF940,
0x32D86CE3,
0x45DF5C75,
0xDCD60DCF,
0xABD13D59,
0x26D930AC,
0x51DE003A,
0xC8D75180,
0xBFD06116,
0x21B4F4B5,
0x56B3C423,
0xCFBA9599,
0xB8BDA50F,
0x2802B89E,
0x5F058808,
0xC60CD9B2,
0xB10BE924,
0x2F6F7C87,
0x58684C11,
0xC1611DAB,
0xB6662D3D,
0x76DC4190,
0x01DB7106,
0x98D220BC,
0xEFD5102A,
0x71B18589,
0x06B6B51F,
0x9FBFE4A5,
0xE8B8D433,
0x7807C9A2,
0x0F00F934,
0x9609A88E,
0xE10E9818,
0x7F6A0DBB,
0x086D3D2D,
0x91646C97,
0xE6635C01,
0x6B6B51F4,
0x1C6C6162,
0x856530D8,
0xF262004E,
0x6C0695ED,
0x1B01A57B,
0x8208F4C1,
0xF50FC457,
0x65B0D9C6,
0x12B7E950,
0x8BBEB8EA,
0xFCB9887C,
0x62DD1DDF,
0x15DA2D49,
0x8CD37CF3,
0xFBD44C65,
0x4DB26158,
0x3AB551CE,
0xA3BC0074,
0xD4BB30E2,
0x4ADFA541,
0x3DD895D7,
0xA4D1C46D,
0xD3D6F4FB,
0x4369E96A,
0x346ED9FC,
0xAD678846,
0xDA60B8D0,
0x44042D73,
0x33031DE5,
0xAA0A4C5F,
0xDD0D7CC9,
0x5005713C,
0x270241AA,
0xBE0B1010,
0xC90C2086,
0x5768B525,
0x206F85B3,
0xB966D409,
0xCE61E49F,
0x5EDEF90E,
0x29D9C998,
0xB0D09822,
0xC7D7A8B4,
0x59B33D17,
0x2EB40D81,
0xB7BD5C3B,
0xC0BA6CAD,
0xEDB88320,
0x9ABFB3B6,
0x03B6E20C,
0x74B1D29A,
0xEAD54739,
0x9DD277AF,
0x04DB2615,
0x73DC1683,
0xE3630B12,
0x94643B84,
0x0D6D6A3E,
0x7A6A5AA8,
0xE40ECF0B,
0x9309FF9D,
0x0A00AE27,
0x7D079EB1,
0xF00F9344,
0x8708A3D2,
0x1E01F268,
0x6906C2FE,
0xF762575D,
0x806567CB,
0x196C3671,
0x6E6B06E7,
0xFED41B76,
0x89D32BE0,
0x10DA7A5A,
0x67DD4ACC,
0xF9B9DF6F,
0x8EBEEFF9,
0x17B7BE43,
0x60B08ED5,
0xD6D6A3E8,
0xA1D1937E,
0x38D8C2C4,
0x4FDFF252,
0xD1BB67F1,
0xA6BC5767,
0x3FB506DD,
0x48B2364B,
0xD80D2BDA,
0xAF0A1B4C,
0x36034AF6,
0x41047A60,
0xDF60EFC3,
0xA867DF55,
0x316E8EEF,
0x4669BE79,
0xCB61B38C,
0xBC66831A,
0x256FD2A0,
0x5268E236,
0xCC0C7795,
0xBB0B4703,
0x220216B9,
0x5505262F,
0xC5BA3BBE,
0xB2BD0B28,
0x2BB45A92,
0x5CB36A04,
0xC2D7FFA7,
0xB5D0CF31,
0x2CD99E8B,
0x5BDEAE1D,
0x9B64C2B0,
0xEC63F226,
0x756AA39C,
0x026D930A,
0x9C0906A9,
0xEB0E363F,
0x72076785,
0x05005713,
0x95bf4a82,
0xe2b87a14,
0x7bb12bae,
0x0cb61b38,
0x92d28e9b,
0xe5d5be0d,
0x7cdcefb7,
0x0bdbdf21,
0x86d3d2d4,
0xf1d4e242,
0x68ddb3f8,
0x1fda836e,
0x81be16cd,
0xf6b9265b,
0x6fb077e1,
0x18b74777,
0x88085ae6,
0xff0f6a70,
0x66063bca,
0x11010b5c,
0x8f659eff,
0xf862ae69,
0x616bffd3,
0x166ccf45,
0xa00ae278,
0xd70dd2ee,
0x4e048354,
0x3903b3c2,
0xa7672661,
0xd06016f7,
0x4969474d,
0x3e6e77db,
0xaed16a4a,
0xd9d65adc,
0x40df0b66,
0x37d83bf0,
0xa9bcae53,
0xdebb9ec5,
0x47b2cf7f,
0x30b5ffe9,
0xbdbdf21c,
0xcabac28a,
0x53b39330,
0x24b4a3a6,
0xbad03605,
0xcdd70693,
0x54de5729,
0x23d967bf,
0xb3667a2e,
0xc4614ab8,
0x5d681b02,
0x2a6f2b94,
0xb40bbe37,
0xc30c8ea1,
0x5a05df1b,
0x2d02ef8d,
0x95BF4A82,
0xE2B87A14,
0x7BB12BAE,
0x0CB61B38,
0x92D28E9B,
0xE5D5BE0D,
0x7CDCEFB7,
0x0BDBDF21,
0x86D3D2D4,
0xF1D4E242,
0x68DDB3F8,
0x1FDA836E,
0x81BE16CD,
0xF6B9265B,
0x6FB077E1,
0x18B74777,
0x88085AE6,
0xFF0F6A70,
0x66063BCA,
0x11010B5C,
0x8F659EFF,
0xF862AE69,
0x616BFFD3,
0x166CCF45,
0xA00AE278,
0xD70DD2EE,
0x4E048354,
0x3903B3C2,
0xA7672661,
0xD06016F7,
0x4969474D,
0x3E6E77DB,
0xAED16A4A,
0xD9D65ADC,
0x40DF0B66,
0x37D83BF0,
0xA9BCAE53,
0xDEBB9EC5,
0x47B2CF7F,
0x30B5FFE9,
0xBDBDF21C,
0xCABAC28A,
0x53B39330,
0x24B4A3A6,
0xBAD03605,
0xCDD70693,
0x54DE5729,
0x23D967BF,
0xB3667A2E,
0xC4614AB8,
0x5D681B02,
0x2A6F2B94,
0xB40BBE37,
0xC30C8EA1,
0x5A05DF1B,
0x2D02EF8D,
];
/**
* Encryption keys.
*
* @var array
*/
private $keys;
/** @var array encryption keys */
private array $keys;
/**
* PKCryptContext constructor.
*
* @param string $password
*/
public function __construct($password)
public function __construct(string $password)
{
if (\PHP_INT_SIZE === 4) {
throw new RuntimeException('Traditional PKWARE Encryption is not supported in 32-bit PHP.');
@@ -307,42 +304,34 @@ class PKCryptContext
878082192,
];
foreach (unpack('C*', $password) as $b) {
$this->updateKeys($b);
foreach (unpack('C*', $password) as $byte) {
$this->updateKeys($byte);
}
}
/**
* @param string $header
* @param int $checkByte
*
* @throws ZipAuthenticationException
*/
public function checkHeader($header, $checkByte)
public function checkHeader(string $header, int $checkByte): void
{
$byte = 0;
foreach (unpack('C*', $header) as $byte) {
$byte = ($byte ^ $this->decryptByte()) & 0xff;
$byte = ($byte ^ $this->decryptByte()) & 0xFF;
$this->updateKeys($byte);
}
if ($byte !== $checkByte) {
throw new ZipAuthenticationException(sprintf('Invalid password'));
throw new ZipAuthenticationException('Invalid password');
}
}
/**
* @param string $content
*
* @return string
*/
public function decryptString($content)
public function decryptString(string $content): string
{
$decryptContent = '';
foreach (unpack('C*', $content) as $byte) {
$byte = ($byte ^ $this->decryptByte()) & 0xff;
$byte = ($byte ^ $this->decryptByte()) & 0xFF;
$this->updateKeys($byte);
$decryptContent .= \chr($byte);
}
@@ -352,48 +341,34 @@ class PKCryptContext
/**
* Decrypt byte.
*
* @return int
*/
private function decryptByte()
private function decryptByte(): int
{
$temp = $this->keys[2] | 2;
return (($temp * ($temp ^ 1)) >> 8) & 0xffffff;
return (($temp * ($temp ^ 1)) >> 8) & 0xFFFFFF;
}
/**
* Update keys.
*
* @param int $charAt
*/
private function updateKeys($charAt)
private function updateKeys(int $charAt): void
{
$this->keys[0] = $this->crc32($this->keys[0], $charAt);
$this->keys[1] += ($this->keys[0] & 0xff);
$this->keys[1] = PackUtil::toSignedInt32($this->keys[1] * 134775813 + 1);
$this->keys[2] = PackUtil::toSignedInt32($this->crc32($this->keys[2], ($this->keys[1] >> 24) & 0xff));
$this->keys[1] += ($this->keys[0] & 0xFF);
$this->keys[1] = MathUtil::toSignedInt32($this->keys[1] * 134775813 + 1);
$this->keys[2] = MathUtil::toSignedInt32($this->crc32($this->keys[2], ($this->keys[1] >> 24) & 0xFF));
}
/**
* Update crc.
*
* @param int $oldCrc
* @param int $charAt
*
* @return int
*/
private function crc32($oldCrc, $charAt)
private function crc32(int $oldCrc, int $charAt): int
{
return (($oldCrc >> 8) & 0xffffff) ^ self::$CRC_TABLE[($oldCrc ^ $charAt) & 0xff];
return (($oldCrc >> 8) & 0xFFFFFF) ^ self::CRC_TABLE[($oldCrc ^ $charAt) & 0xFF];
}
/**
* @param string $content
*
* @return string
*/
public function encryptString($content)
public function encryptString(string $content): string
{
$encryptContent = '';
@@ -404,14 +379,9 @@ class PKCryptContext
return $encryptContent;
}
/**
* @param int $byte
*
* @return int
*/
private function encryptByte($byte)
private function encryptByte(int $byte): int
{
$tempVal = $byte ^ $this->decryptByte() & 0xff;
$tempVal = $byte ^ $this->decryptByte() & 0xFF;
$this->updateKeys($byte);
return $tempVal;

View File

@@ -1,8 +1,17 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\Pkware;
use PhpZip\Exception\ZipException;
use PhpZip\Exception\ZipAuthenticationException;
use PhpZip\Model\ZipEntry;
/**
@@ -10,37 +19,27 @@ use PhpZip\Model\ZipEntry;
*/
class PKDecryptionStreamFilter extends \php_user_filter
{
const FILTER_NAME = 'phpzip.decryption.pkware';
public const FILTER_NAME = 'phpzip.decryption.pkware';
/** @var int */
private $checkByte = 0;
private int $checkByte = 0;
/** @var int */
private $readLength = 0;
private int $readLength = 0;
/** @var int */
private $size = 0;
private int $size = 0;
/** @var bool */
private $readHeader = false;
private bool $readHeader = false;
/** @var PKCryptContext */
private $context;
private PKCryptContext $context;
/**
* @return bool
*/
public static function register()
public static function register(): bool
{
return stream_filter_register(self::FILTER_NAME, __CLASS__);
}
/**
* @see https://php.net/manual/en/php-user-filter.oncreate.php
*
* @return bool
*/
public function onCreate()
public function onCreate(): bool
{
if (!isset($this->params['entry'])) {
return false;
@@ -64,9 +63,9 @@ class PKDecryptionStreamFilter extends \php_user_filter
// init check byte
if ($entry->isDataDescriptorEnabled()) {
$this->checkByte = ($entry->getDosTime() >> 8) & 0xff;
$this->checkByte = ($entry->getDosTime() >> 8) & 0xFF;
} else {
$this->checkByte = ($entry->getCrc() >> 24) & 0xff;
$this->checkByte = ($entry->getCrc() >> 24) & 0xFF;
}
$this->readLength = 0;
@@ -78,18 +77,17 @@ class PKDecryptionStreamFilter extends \php_user_filter
/**
* Decryption filter.
*
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
*
* @throws ZipException
*
* @return int
*
* @todo USE FFI in php 7.4
* @noinspection PhpDocSignatureInspection
*
* @param mixed $in
* @param mixed $out
* @param mixed $consumed
* @param mixed $closing
*
* @throws ZipAuthenticationException
*/
public function filter($in, $out, &$consumed, $closing)
public function filter($in, $out, &$consumed, $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$buffer = $bucket->data;

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\Pkware;
use PhpZip\Exception\RuntimeException;
@@ -10,37 +19,27 @@ use PhpZip\Model\ZipEntry;
*/
class PKEncryptionStreamFilter extends \php_user_filter
{
const FILTER_NAME = 'phpzip.encryption.pkware';
public const FILTER_NAME = 'phpzip.encryption.pkware';
/** @var int */
private $size;
private int $size;
/** @var string */
private $headerBytes;
private string $headerBytes;
/** @var int */
private $writeLength;
private int $writeLength;
/** @var bool */
private $writeHeader;
private bool $writeHeader;
/** @var PKCryptContext */
private $context;
private PKCryptContext $context;
/**
* @return bool
*/
public static function register()
public static function register(): bool
{
return stream_filter_register(self::FILTER_NAME, __CLASS__);
}
/**
* @see https://php.net/manual/en/php-user-filter.oncreate.php
*
* @return bool
*/
public function onCreate()
public function onCreate(): bool
{
if (\PHP_INT_SIZE === 4) {
throw new RuntimeException('Traditional PKWARE Encryption is not supported in 32-bit PHP.');
@@ -66,9 +65,9 @@ class PKEncryptionStreamFilter extends \php_user_filter
// init keys
$this->context = new PKCryptContext($password);
$crc = $entry->isDataDescriptorRequired() || $entry->getCrc() === ZipEntry::UNKNOWN ?
($entry->getDosTime() & 0x0000ffff) << 16 :
$entry->getCrc();
$crc = $entry->isDataDescriptorRequired() || $entry->getCrc() === ZipEntry::UNKNOWN
? ($entry->getDosTime() & 0x0000FFFF) << 16
: $entry->getCrc();
try {
$headerBytes = random_bytes(PKCryptContext::STD_DEC_HDR_SIZE);
@@ -76,8 +75,8 @@ class PKEncryptionStreamFilter extends \php_user_filter
throw new \RuntimeException('Oops, our server is bust and cannot generate any random data.', 1, $e);
}
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 1] = pack('c', ($crc >> 24) & 0xff);
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 2] = pack('c', ($crc >> 16) & 0xff);
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 1] = pack('c', ($crc >> 24) & 0xFF);
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 2] = pack('c', ($crc >> 16) & 0xFF);
$this->headerBytes = $headerBytes;
$this->writeLength = 0;
@@ -89,16 +88,16 @@ class PKEncryptionStreamFilter extends \php_user_filter
/**
* Encryption filter.
*
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
*
* @return int
*
* @todo USE FFI in php 7.4
*
* @noinspection PhpDocSignatureInspection
*
* @param mixed $in
* @param mixed $out
* @param mixed $consumed
* @param mixed $closing
*/
public function filter($in, $out, &$consumed, $closing)
public function filter($in, $out, &$consumed, $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$buffer = $bucket->data;

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\WinZipAes;
use PhpZip\Exception\RuntimeException;
@@ -12,51 +21,35 @@ use PhpZip\Util\CryptoUtil;
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT APPENDIX E
* @see https://www.winzip.com/win/en/aes_info.html
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @internal
*/
class WinZipAesContext
{
/** @var int AES Block size */
const BLOCK_SIZE = self::IV_SIZE;
public const BLOCK_SIZE = self::IV_SIZE;
/** @var int Footer size */
const FOOTER_SIZE = 10;
public const FOOTER_SIZE = 10;
/** @var int The iteration count for the derived keys of the cipher, KLAC and MAC. */
const ITERATION_COUNT = 1000;
public const ITERATION_COUNT = 1000;
/** @var int Password verifier size */
const PASSWORD_VERIFIER_SIZE = 2;
public const PASSWORD_VERIFIER_SIZE = 2;
/** @var int IV size */
const IV_SIZE = 16;
public const IV_SIZE = 16;
/** @var string */
private $iv;
private string $iv;
/** @var string */
private $key;
private string $key;
/** @var \HashContext|resource */
private $hmacContext;
private \HashContext $hmacContext;
/** @var string */
private $passwordVerifier;
private string $passwordVerifier;
/**
* WinZipAesContext constructor.
*
* @param int $encryptionStrengthBits
* @param string $password
* @param string $salt
*/
public function __construct($encryptionStrengthBits, $password, $salt)
public function __construct(int $encryptionStrengthBits, string $password, string $salt)
{
$encryptionStrengthBits = (int) $encryptionStrengthBits;
if ($password === '') {
throw new RuntimeException('$password is empty');
}
@@ -87,15 +80,12 @@ class WinZipAesContext
$this->passwordVerifier = substr($hash, 2 * $keyStrengthBytes, self::PASSWORD_VERIFIER_SIZE);
}
/**
* @return string
*/
public function getPasswordVerifier()
public function getPasswordVerifier(): string
{
return $this->passwordVerifier;
}
public function updateIv()
public function updateIv(): void
{
for ($ivCharIndex = 0; $ivCharIndex < self::IV_SIZE; $ivCharIndex++) {
$ivByte = \ord($this->iv[$ivCharIndex]);
@@ -112,24 +102,14 @@ class WinZipAesContext
}
}
/**
* @param string $data
*
* @return string
*/
public function decryption($data)
public function decryption(string $data): string
{
hash_update($this->hmacContext, $data);
return CryptoUtil::decryptAesCtr($data, $this->key, $this->iv);
}
/**
* @param string $data
*
* @return string
*/
public function encrypt($data)
public function encrypt(string $data): string
{
$encryptionData = CryptoUtil::encryptAesCtr($data, $this->key, $this->iv);
hash_update($this->hmacContext, $encryptionData);
@@ -138,11 +118,9 @@ class WinZipAesContext
}
/**
* @param string $authCode
*
* @throws ZipAuthenticationException
*/
public function checkAuthCode($authCode)
public function checkAuthCode(string $authCode): void
{
$hmac = $this->getHmac();
@@ -152,10 +130,7 @@ class WinZipAesContext
}
}
/**
* @return string
*/
public function getHmac()
public function getHmac(): string
{
return substr(
hash_final($this->hmacContext, true),

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\WinZipAes;
use PhpZip\Exception\RuntimeException;
@@ -12,43 +21,31 @@ use PhpZip\Model\ZipEntry;
*/
class WinZipAesDecryptionStreamFilter extends \php_user_filter
{
const FILTER_NAME = 'phpzip.decryption.winzipaes';
public const FILTER_NAME = 'phpzip.decryption.winzipaes';
/** @var string */
private $buffer;
private string $buffer;
/** @var string */
private $authenticationCode;
private ?string $authenticationCode = null;
/** @var int */
private $encBlockPosition = 0;
private int $encBlockPosition = 0;
/** @var int */
private $encBlockLength = 0;
private int $encBlockLength = 0;
/** @var int */
private $readLength = 0;
private int $readLength = 0;
/** @var ZipEntry */
private $entry;
private ZipEntry $entry;
/** @var WinZipAesContext|null */
private $context;
private ?WinZipAesContext $context = null;
/**
* @return bool
*/
public static function register()
public static function register(): bool
{
return stream_filter_register(self::FILTER_NAME, __CLASS__);
}
/**
* @return bool
*
* @noinspection DuplicatedCode
*/
public function onCreate()
public function onCreate(): bool
{
if (!isset($this->params['entry'])) {
return false;
@@ -60,9 +57,9 @@ class WinZipAesDecryptionStreamFilter extends \php_user_filter
$this->entry = $this->params['entry'];
if (
$this->entry->getPassword() === null ||
!$this->entry->isEncrypted() ||
!$this->entry->hasExtraField(WinZipAesExtraField::HEADER_ID)
$this->entry->getPassword() === null
|| !$this->entry->isEncrypted()
|| !$this->entry->hasExtraField(WinZipAesExtraField::HEADER_ID)
) {
return false;
}
@@ -73,16 +70,16 @@ class WinZipAesDecryptionStreamFilter extends \php_user_filter
}
/**
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
* @noinspection PhpDocSignatureInspection
*
* @param mixed $in
* @param mixed $out
* @param mixed $consumed
* @param mixed $closing
*
* @throws ZipAuthenticationException
*
* @return int
*/
public function filter($in, $out, &$consumed, $closing)
public function filter($in, $out, &$consumed, $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$this->buffer .= $bucket->data;
@@ -156,8 +153,8 @@ class WinZipAesDecryptionStreamFilter extends \php_user_filter
$this->encBlockPosition += $offset;
if (
$this->encBlockPosition === $this->encBlockLength &&
\strlen($this->buffer) === WinZipAesContext::FOOTER_SIZE
$this->encBlockPosition === $this->encBlockLength
&& \strlen($this->buffer) === WinZipAesContext::FOOTER_SIZE
) {
$this->authenticationCode = $this->buffer;
$this->buffer = '';
@@ -176,11 +173,11 @@ class WinZipAesDecryptionStreamFilter extends \php_user_filter
*
* @throws ZipAuthenticationException
*/
public function onClose()
public function onClose(): void
{
$this->buffer = '';
if ($this->context !== null) {
if ($this->context !== null && $this->authenticationCode !== null) {
$this->context->checkAuthCode($this->authenticationCode);
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\WinZipAes;
use PhpZip\Exception\RuntimeException;
@@ -11,37 +20,27 @@ use PhpZip\Model\ZipEntry;
*/
class WinZipAesEncryptionStreamFilter extends \php_user_filter
{
const FILTER_NAME = 'phpzip.encryption.winzipaes';
public const FILTER_NAME = 'phpzip.encryption.winzipaes';
/** @var string */
private $buffer;
private string $buffer;
/** @var int */
private $remaining = 0;
private int $remaining = 0;
/** @var ZipEntry */
private $entry;
private ZipEntry $entry;
/** @var int */
private $size;
private int $size;
/** @var WinZipAesContext|null */
private $context;
private ?WinZipAesContext $context = null;
/**
* @return bool
*/
public static function register()
public static function register(): bool
{
return stream_filter_register(self::FILTER_NAME, __CLASS__);
}
/**
* @return bool
*
* @noinspection DuplicatedCode
*/
public function onCreate()
public function onCreate(): bool
{
if (!isset($this->params['entry'])) {
return false;
@@ -53,9 +52,9 @@ class WinZipAesEncryptionStreamFilter extends \php_user_filter
$this->entry = $this->params['entry'];
if (
$this->entry->getPassword() === null ||
!$this->entry->isEncrypted() ||
!$this->entry->hasExtraField(WinZipAesExtraField::HEADER_ID)
$this->entry->getPassword() === null
|| !$this->entry->isEncrypted()
|| !$this->entry->hasExtraField(WinZipAesExtraField::HEADER_ID)
) {
return false;
}
@@ -67,15 +66,7 @@ class WinZipAesEncryptionStreamFilter extends \php_user_filter
return true;
}
/**
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
*
* @return int
*/
public function filter($in, $out, &$consumed, $closing)
public function filter($in, $out, &$consumed, $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$this->buffer .= $bucket->data;
@@ -98,6 +89,7 @@ class WinZipAesEncryptionStreamFilter extends \php_user_filter
if ($winZipExtra === null) {
throw new RuntimeException('$winZipExtra is null');
}
/** @psalm-var positive-int $saltSize */
$saltSize = $winZipExtra->getSaltSize();
try {

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Stream;
use Psr\Http\Message\StreamInterface;
@@ -10,7 +19,7 @@ use Psr\Http\Message\StreamInterface;
class ResponseStream implements StreamInterface
{
/** @var array */
private static $readWriteHash = [
private const READ_WRITE_MAP = [
'read' => [
'r' => true,
'w+' => true,
@@ -50,23 +59,18 @@ class ResponseStream implements StreamInterface
],
];
/** @var resource */
/** @var resource|null */
private $stream;
/** @var int|null */
private $size;
private ?int $size = null;
/** @var bool */
private $seekable;
private bool $seekable;
/** @var bool */
private $readable;
private bool $readable;
/** @var bool */
private $writable;
private bool $writable;
/** @var string|null */
private $uri;
private ?string $uri;
/**
* @param resource $stream stream resource to wrap
@@ -81,33 +85,24 @@ class ResponseStream implements StreamInterface
$this->stream = $stream;
$meta = stream_get_meta_data($this->stream);
$this->seekable = $meta['seekable'];
$this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
$this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
$this->readable = isset(self::READ_WRITE_MAP['read'][$meta['mode']]);
$this->writable = isset(self::READ_WRITE_MAP['write'][$meta['mode']]);
$this->uri = $this->getMetadata('uri');
}
/**
* Get stream metadata as an associative array or retrieve a specific key.
* {@inheritDoc}
*
* The keys returned are identical to the keys returned from PHP's
* stream_get_meta_data() function.
*
* @see http://php.net/manual/en/function.stream-get-meta-data.php
*
* @param string $key specific metadata to retrieve
*
* @return array|mixed|null Returns an associative array if no key is
* provided. Returns a specific key value if a key is provided and the
* value is found, or null if the key is not found.
* @noinspection PhpMissingReturnTypeInspection
*/
public function getMetadata($key = null)
{
if (!$this->stream) {
if ($this->stream === null) {
return $key ? null : [];
}
$meta = stream_get_meta_data($this->stream);
return isset($meta[$key]) ? $meta[$key] : null;
return $meta[$key] ?? null;
}
/**
@@ -122,10 +117,8 @@ class ResponseStream implements StreamInterface
* string casting operations.
*
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
*
* @return string
*/
public function __toString()
public function __toString(): string
{
if (!$this->stream) {
return '';
@@ -146,9 +139,9 @@ class ResponseStream implements StreamInterface
* @see http://www.php.net/manual/en/function.fseek.php
* @see seek()
*/
public function rewind()
public function rewind(): void
{
$this->seekable && rewind($this->stream);
$this->stream !== null && $this->seekable && rewind($this->stream);
}
/**
@@ -156,7 +149,7 @@ class ResponseStream implements StreamInterface
*
* @return int|null returns the size in bytes if known, or null if unknown
*/
public function getSize()
public function getSize(): ?int
{
if ($this->size !== null) {
return $this->size;
@@ -180,13 +173,6 @@ class ResponseStream implements StreamInterface
return null;
}
/**
* Returns the current position of the file read/write pointer.
*
* @throws \RuntimeException on error
*
* @return int Position of the file pointer
*/
public function tell()
{
return $this->stream ? ftell($this->stream) : false;
@@ -194,94 +180,60 @@ class ResponseStream implements StreamInterface
/**
* Returns true if the stream is at the end of the stream.
*
* @return bool
*/
public function eof()
public function eof(): bool
{
return !$this->stream || feof($this->stream);
}
/**
* Returns whether or not the stream is seekable.
*
* @return bool
*/
public function isSeekable()
public function isSeekable(): bool
{
return $this->seekable;
}
/**
* Seek to a position in the stream.
*
* @see http://www.php.net/manual/en/function.fseek.php
*
* @param int $offset Stream offset
* @param int $whence Specifies how the cursor position will be calculated
* based on the seek offset. Valid values are identical to the built-in
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
* offset bytes SEEK_CUR: Set position to current location plus offset
* SEEK_END: Set position to end-of-stream plus offset.
*
* @throws \RuntimeException on failure
* {@inheritDoc}
*/
public function seek($offset, $whence = \SEEK_SET)
public function seek($offset, $whence = \SEEK_SET): void
{
$this->seekable && fseek($this->stream, $offset, $whence);
$this->stream !== null && $this->seekable && fseek($this->stream, $offset, $whence);
}
/**
* Returns whether or not the stream is writable.
*
* @return bool
*/
public function isWritable()
public function isWritable(): bool
{
return $this->writable;
}
/**
* Write data to the stream.
*
* @param string $string the string that is to be written
*
* @throws \RuntimeException on failure
*
* @return int returns the number of bytes written to the stream
* {@inheritDoc}
*/
public function write($string)
{
$this->size = null;
return $this->writable ? fwrite($this->stream, $string) : false;
return $this->stream !== null && $this->writable ? fwrite($this->stream, $string) : false;
}
/**
* Returns whether or not the stream is readable.
*
* @return bool
*/
public function isReadable()
public function isReadable(): bool
{
return $this->readable;
}
/**
* Read data from the stream.
*
* @param int $length Read up to $length bytes from the object and return
* them. Fewer than $length bytes may be returned if underlying stream
* call returns fewer bytes.
*
* @throws \RuntimeException if an error occurs
*
* @return string returns the data read from the stream, or an empty string
* if no bytes are available
* {@inheritDoc}
*/
public function read($length)
public function read($length): string
{
return $this->readable ? fread($this->stream, $length) : '';
return $this->stream !== null && $this->readable ? fread($this->stream, $length) : '';
}
/**
@@ -289,10 +241,8 @@ class ResponseStream implements StreamInterface
*
* @throws \RuntimeException if unable to read or an error occurs while
* reading
*
* @return string
*/
public function getContents()
public function getContents(): string
{
return $this->stream ? stream_get_contents($this->stream) : '';
}
@@ -307,8 +257,10 @@ class ResponseStream implements StreamInterface
/**
* Closes the stream and any underlying resources.
*
* @psalm-suppress InvalidPropertyAssignmentValue
*/
public function close()
public function close(): void
{
if (\is_resource($this->stream)) {
fclose($this->stream);

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Stream;
use PhpZip\Exception\ZipException;
@@ -15,7 +24,7 @@ use PhpZip\Model\ZipEntry;
final class ZipEntryStreamWrapper
{
/** @var string the registered protocol */
const PROTOCOL = 'zipentry';
public const PROTOCOL = 'zipentry';
/** @var resource */
public $context;
@@ -23,10 +32,7 @@ final class ZipEntryStreamWrapper
/** @var resource */
private $fp;
/**
* @return bool
*/
public static function register()
public static function register(): bool
{
$protocol = self::PROTOCOL;
@@ -41,14 +47,12 @@ final class ZipEntryStreamWrapper
return false;
}
public static function unregister()
public static function unregister(): void
{
stream_wrapper_unregister(self::PROTOCOL);
}
/**
* @param ZipEntry $entry
*
* @return resource
*/
public static function wrap(ZipEntry $entry)
@@ -79,26 +83,24 @@ final class ZipEntryStreamWrapper
* This method is called immediately after the wrapper is
* initialized (f.e. by {@see fopen()} and {@see file_get_contents()}).
*
* @param string $path specifies the URL that was passed to
* the original function
* @param string $mode the mode used to open the file, as detailed
* for {@see fopen()}
* @param int $options Holds additional flags set by the streams
* API. It can hold one or more of the
* following values OR'd together.
* @param string $opened_path if the path is opened successfully, and
* STREAM_USE_PATH is set in options,
* opened_path should be set to the
* full path of the file/resource that
* was actually opened
* @param string $path specifies the URL that was passed to
* the original function
* @param string $mode the mode used to open the file, as detailed
* for {@see fopen()}
* @param int $options Holds additional flags set by the streams
* API. It can hold one or more of the
* following values OR'd together.
* @param string|null $opened_path if the path is opened successfully, and
* STREAM_USE_PATH is set in options,
* opened_path should be set to the
* full path of the file/resource that
* was actually opened
*
* @throws ZipException
*
* @return bool
*
* @see https://www.php.net/streamwrapper.stream-open
*/
public function stream_open($path, $mode, $options, &$opened_path)
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
{
if ($this->context === null) {
throw new \RuntimeException('stream context is null');
@@ -142,7 +144,7 @@ final class ZipEntryStreamWrapper
*
* @see https://www.php.net/streamwrapper.stream-read
*/
public function stream_read($count)
public function stream_read(int $count)
{
return fread($this->fp, $count);
}
@@ -164,7 +166,7 @@ final class ZipEntryStreamWrapper
*
* @see https://www.php.net/streamwrapper.stream-seek
*/
public function stream_seek($offset, $whence = \SEEK_SET)
public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
{
return fseek($this->fp, $offset, $whence) === 0;
}
@@ -179,7 +181,7 @@ final class ZipEntryStreamWrapper
*
* @see https://www.php.net/streamwrapper.stream-tell
*/
public function stream_tell()
public function stream_tell(): int
{
$pos = ftell($this->fp);
@@ -201,7 +203,7 @@ final class ZipEntryStreamWrapper
*
* @see https://www.php.net/streamwrapper.stream-eof
*/
public function stream_eof()
public function stream_eof(): bool
{
return feof($this->fp);
}
@@ -211,13 +213,11 @@ final class ZipEntryStreamWrapper
*
* This method is called in response to {@see fstat()}.
*
* @return array
*
* @see https://www.php.net/streamwrapper.stream-stat
* @see https://www.php.net/stat
* @see https://www.php.net/fstat
*/
public function stream_stat()
public function stream_stat(): array
{
return fstat($this->fp);
}
@@ -237,7 +237,7 @@ final class ZipEntryStreamWrapper
*
* @see https://www.php.net/streamwrapper.stream-flush
*/
public function stream_flush()
public function stream_flush(): bool
{
return fflush($this->fp);
}
@@ -247,15 +247,15 @@ final class ZipEntryStreamWrapper
*
* Will respond to truncation, e.g., through {@see ftruncate()}.
*
* @param int $new_size the new size
* @param int $newSize the new size
*
* @return bool returns TRUE on success or FALSE on failure
*
* @see https://www.php.net/streamwrapper.stream-truncate
*/
public function stream_truncate($new_size)
public function stream_truncate(int $newSize): bool
{
return ftruncate($this->fp, (int) $new_size);
return ftruncate($this->fp, $newSize);
}
/**
@@ -272,7 +272,7 @@ final class ZipEntryStreamWrapper
*
* @see https://www.php.net/streamwrapper.stream-write
*/
public function stream_write($data)
public function stream_write(string $data): int
{
$bytes = fwrite($this->fp, $data);
@@ -290,7 +290,7 @@ final class ZipEntryStreamWrapper
*
* @return resource
*/
public function stream_cast($cast_as)
public function stream_cast(int $cast_as)
{
return $this->fp;
}
@@ -303,7 +303,7 @@ final class ZipEntryStreamWrapper
*
* @see https://www.php.net/streamwrapper.stream-close
*/
public function stream_close()
public function stream_close(): void
{
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO;
use PhpZip\Constants\DosCodePage;
@@ -24,28 +33,22 @@ use PhpZip\Model\Extra\ZipExtraDriver;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ImmutableZipContainer;
use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/**
* Zip reader.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class ZipReader
{
/** @var int file size */
protected $size;
protected int $size;
/** @var resource */
protected $inStream;
/** @var array */
protected $options;
protected array $options;
/**
* @param resource $inStream
* @param array $options
*/
public function __construct($inStream, array $options = [])
{
@@ -59,7 +62,7 @@ class ZipReader
}
$meta = stream_get_meta_data($inStream);
$wrapperType = isset($meta['wrapper_type']) ? $meta['wrapper_type'] : 'Unknown';
$wrapperType = $meta['wrapper_type'] ?? 'Unknown';
$supportStreamWrapperTypes = ['plainfile', 'PHP', 'user-space'];
if (!\in_array($wrapperType, $supportStreamWrapperTypes, true)) {
@@ -72,10 +75,10 @@ class ZipReader
}
if (
$wrapperType === 'plainfile' &&
(
$meta['stream_type'] === 'dir' ||
(isset($meta['uri']) && is_dir($meta['uri']))
$wrapperType === 'plainfile'
&& (
$meta['stream_type'] === 'dir'
|| (isset($meta['uri']) && is_dir($meta['uri']))
)
) {
throw new InvalidArgumentException('Directory stream not supported');
@@ -94,10 +97,7 @@ class ZipReader
$this->options = $options;
}
/**
* @return array
*/
protected function getDefaultOptions()
protected function getDefaultOptions(): array
{
return [
ZipOptions::CHARSET => null,
@@ -106,10 +106,8 @@ class ZipReader
/**
* @throws ZipException
*
* @return ImmutableZipContainer
*/
public function read()
public function read(): ImmutableZipContainer
{
if ($this->size < ZipConstants::END_CD_MIN_LEN) {
throw new ZipException('Corrupt zip file');
@@ -121,10 +119,7 @@ class ZipReader
return new ImmutableZipContainer($entries, $endOfCentralDirectory->getComment());
}
/**
* @return array
*/
public function getStreamMetaData()
public function getStreamMetaData(): array
{
return stream_get_meta_data($this->inStream);
}
@@ -148,10 +143,8 @@ class ZipReader
* .ZIP file comment (variable size)
*
* @throws ZipException
*
* @return EndOfCentralDirectory
*/
protected function readEndOfCentralDirectory()
protected function readEndOfCentralDirectory(): EndOfCentralDirectory
{
if (!$this->findEndOfCentralDirectory()) {
throw new ZipException('Invalid zip file. The end of the central directory could not be found.');
@@ -161,26 +154,34 @@ class ZipReader
$sizeECD = $this->size - ftell($this->inStream);
$buffer = fread($this->inStream, $sizeECD);
$unpack = unpack(
'vdiskNo/vcdDiskNo/vcdEntriesDisk/' .
'vcdEntries/VcdSize/VcdPos/vcommentLength',
[
'diskNo' => $diskNo,
'cdDiskNo' => $cdDiskNo,
'cdEntriesDisk' => $cdEntriesDisk,
'cdEntries' => $cdEntries,
'cdSize' => $cdSize,
'cdPos' => $cdPos,
'commentLength' => $commentLength,
] = unpack(
'vdiskNo/vcdDiskNo/vcdEntriesDisk/'
. 'vcdEntries/VcdSize/VcdPos/vcommentLength',
substr($buffer, 0, 18)
);
if (
$unpack['diskNo'] !== 0 ||
$unpack['cdDiskNo'] !== 0 ||
$unpack['cdEntriesDisk'] !== $unpack['cdEntries']
$diskNo !== 0
|| $cdDiskNo !== 0
|| $cdEntriesDisk !== $cdEntries
) {
throw new ZipException(
'ZIP file spanning/splitting is not supported!'
);
}
// .ZIP file comment (variable sizeECD)
$comment = null;
if ($unpack['commentLength'] > 0) {
$comment = substr($buffer, 18, $unpack['commentLength']);
if ($commentLength > 0) {
// .ZIP file comment (variable sizeECD)
$comment = substr($buffer, 18, $commentLength);
}
// Check for ZIP64 End Of Central Directory Locator exists.
@@ -188,10 +189,10 @@ class ZipReader
fseek($this->inStream, $zip64ECDLocatorPosition);
// zip64 end of central dir locator
// signature 4 bytes (0x07064b50)
if ($zip64ECDLocatorPosition > 0 && unpack(
'V',
fread($this->inStream, 4)
)[1] === ZipConstants::ZIP64_END_CD_LOC) {
if (
$zip64ECDLocatorPosition > 0
&& unpack('V', fread($this->inStream, 4))[1] === ZipConstants::ZIP64_END_CD_LOC
) {
if (!$this->isZip64Support()) {
throw new ZipException('ZIP64 not supported this archive.');
}
@@ -201,9 +202,9 @@ class ZipReader
$endCentralDirectory->setComment($comment);
} else {
$endCentralDirectory = new EndOfCentralDirectory(
$unpack['cdEntries'],
$unpack['cdPos'],
$unpack['cdSize'],
$cdEntries,
$cdPos,
$cdSize,
false,
$comment
);
@@ -212,13 +213,10 @@ class ZipReader
return $endCentralDirectory;
}
/**
* @return bool
*/
protected function findEndOfCentralDirectory()
protected function findEndOfCentralDirectory(): bool
{
$max = $this->size - ZipConstants::END_CD_MIN_LEN;
$min = $max >= 0xffff ? $max - 0xffff : 0;
$min = $max >= 0xFFFF ? $max - 0xFFFF : 0;
// Search for End of central directory record.
for ($position = $max; $position >= $min; $position--) {
fseek($this->inStream, $position);
@@ -248,11 +246,13 @@ class ZipReader
*
* @return int Zip64 End Of Central Directory position
*/
protected function findZip64ECDPosition()
protected function findZip64ECDPosition(): int
{
$diskNo = unpack('V', fread($this->inStream, 4))[1];
$zip64ECDPos = PackUtil::unpackLongLE(fread($this->inStream, 8));
$totalDisks = unpack('V', fread($this->inStream, 4))[1];
[
'diskNo' => $diskNo,
'zip64ECDPos' => $zip64ECDPos,
'totalDisks' => $totalDisks,
] = unpack('VdiskNo/Pzip64ECDPos/VtotalDisks', fread($this->inStream, 16));
if ($diskNo !== 0 || $totalDisks > 1) {
throw new ZipException('ZIP file spanning/splitting is not supported!');
@@ -284,13 +284,9 @@ class ZipReader
* the starting disk number 8 bytes
* zip64 extensible data sector (variable size)
*
* @param int $zip64ECDPosition
*
* @throws ZipException
*
* @return EndOfCentralDirectory
*/
protected function readZip64EndOfCentralDirectory($zip64ECDPosition)
protected function readZip64EndOfCentralDirectory(int $zip64ECDPosition): EndOfCentralDirectory
{
fseek($this->inStream, $zip64ECDPosition);
@@ -300,25 +296,30 @@ class ZipReader
throw new ZipException('Expected ZIP64 End Of Central Directory Record!');
}
$data = unpack(
// 'Psize/vversionMadeBy/vextractVersion/' .
'VdiskNo/VcdDiskNo',
substr($buffer, 16, 8)
[
// 'size' => $size,
// 'versionMadeBy' => $versionMadeBy,
// 'extractVersion' => $extractVersion,
'diskNo' => $diskNo,
'cdDiskNo' => $cdDiskNo,
'cdEntriesDisk' => $cdEntriesDisk,
'entryCount' => $entryCount,
'cdSize' => $cdSize,
'cdPos' => $cdPos,
] = unpack(
// 'Psize/vversionMadeBy/vextractVersion/'.
'VdiskNo/VcdDiskNo/PcdEntriesDisk/PentryCount/PcdSize/PcdPos',
substr($buffer, 16, 40)
);
$cdEntriesDisk = PackUtil::unpackLongLE(substr($buffer, 24, 8));
$entryCount = PackUtil::unpackLongLE(substr($buffer, 32, 8));
$cdSize = PackUtil::unpackLongLE(substr($buffer, 40, 8));
$cdPos = PackUtil::unpackLongLE(substr($buffer, 48, 8));
// $platform = ZipPlatform::fromValue(($versionMadeBy & 0xFF00) >> 8);
// $softwareVersion = $versionMadeBy & 0x00FF;
// $platform = ZipPlatform::fromValue(($data['versionMadeBy'] & 0xFF00) >> 8);
// $softwareVersion = $data['versionMadeBy'] & 0x00FF;
if ($data['diskNo'] !== 0 || $data['cdDiskNo'] !== 0 || $entryCount !== $cdEntriesDisk) {
if ($diskNo !== 0 || $cdDiskNo !== 0 || $entryCount !== $cdEntriesDisk) {
throw new ZipException('ZIP file spanning/splitting is not supported!');
}
if ($entryCount < 0 || $entryCount > 0x7fffffff) {
if ($entryCount < 0 || $entryCount > 0x7FFFFFFF) {
throw new ZipException('Total Number Of Entries In The Central Directory out of range!');
}
@@ -340,13 +341,11 @@ class ZipReader
* central directory alone, but not the data that requires the local
* file header or additional data to be read.
*
* @param EndOfCentralDirectory $endCD
*
* @throws ZipException
*
* @return ZipEntry[]
*/
protected function readCentralDirectory(EndOfCentralDirectory $endCD)
protected function readCentralDirectory(EndOfCentralDirectory $endCD): array
{
$entries = [];
@@ -354,7 +353,9 @@ class ZipReader
fseek($this->inStream, $cdOffset);
if (!($cdStream = fopen('php://temp', 'w+b'))) {
throw new ZipException('Temp resource can not open from write');
// @codeCoverageIgnoreStart
throw new ZipException('A temporary resource cannot be opened for writing.');
// @codeCoverageIgnoreEnd
}
stream_copy_to_stream($this->inStream, $cdStream, $endCD->getCdSize());
rewind($cdStream);
@@ -366,16 +367,13 @@ class ZipReader
/** @var UnicodePathExtraField|null $unicodePathExtraField */
$unicodePathExtraField = $zipEntry->getExtraField(UnicodePathExtraField::HEADER_ID);
if ($unicodePathExtraField !== null) {
if ($unicodePathExtraField !== null && $unicodePathExtraField->getCrc32() === crc32($entryName)) {
$unicodePath = $unicodePathExtraField->getUnicodeValue();
if ($unicodePath !== null) {
if ($unicodePath !== '') {
$unicodePath = str_replace('\\', '/', $unicodePath);
if (
$unicodePath !== '' &&
substr_count($entryName, '/') === substr_count($unicodePath, '/')
) {
if (substr_count($entryName, '/') === substr_count($unicodePath, '/')) {
$entryName = $unicodePath;
}
}
@@ -415,46 +413,56 @@ class ZipReader
* @param resource $stream
*
* @throws ZipException
*
* @return ZipEntry
*/
protected function readZipEntry($stream)
protected function readZipEntry($stream): ZipEntry
{
if (unpack('V', fread($stream, 4))[1] !== ZipConstants::CENTRAL_FILE_HEADER) {
throw new ZipException('Corrupt zip file. Cannot read zip entry.');
}
$unpack = unpack(
'vversionMadeBy/vversionNeededToExtract/' .
'vgeneralPurposeBitFlag/vcompressionMethod/' .
'VlastModFile/Vcrc/VcompressedSize/' .
'VuncompressedSize/vfileNameLength/vextraFieldLength/' .
'vfileCommentLength/vdiskNumberStart/vinternalFileAttributes/' .
'VexternalFileAttributes/VoffsetLocalHeader',
[
'versionMadeBy' => $versionMadeBy,
'versionNeededToExtract' => $versionNeededToExtract,
'generalPurposeBitFlags' => $generalPurposeBitFlags,
'compressionMethod' => $compressionMethod,
'lastModFile' => $dosTime,
'crc' => $crc,
'compressedSize' => $compressedSize,
'uncompressedSize' => $uncompressedSize,
'fileNameLength' => $fileNameLength,
'extraFieldLength' => $extraFieldLength,
'fileCommentLength' => $fileCommentLength,
'diskNumberStart' => $diskNumberStart,
'internalFileAttributes' => $internalFileAttributes,
'externalFileAttributes' => $externalFileAttributes,
'offsetLocalHeader' => $offsetLocalHeader,
] = unpack(
'vversionMadeBy/vversionNeededToExtract/'
. 'vgeneralPurposeBitFlags/vcompressionMethod/'
. 'VlastModFile/Vcrc/VcompressedSize/'
. 'VuncompressedSize/vfileNameLength/vextraFieldLength/'
. 'vfileCommentLength/vdiskNumberStart/vinternalFileAttributes/'
. 'VexternalFileAttributes/VoffsetLocalHeader',
fread($stream, 42)
);
if ($unpack['diskNumberStart'] !== 0) {
if ($diskNumberStart !== 0) {
throw new ZipException('ZIP file spanning/splitting is not supported!');
}
$generalPurposeBitFlags = $unpack['generalPurposeBitFlag'];
$isUtf8 = ($generalPurposeBitFlags & GeneralPurposeBitFlag::UTF8) !== 0;
$name = fread($stream, $unpack['fileNameLength']);
$name = fread($stream, $fileNameLength);
$createdOS = ($unpack['versionMadeBy'] & 0xFF00) >> 8;
$softwareVersion = $unpack['versionMadeBy'] & 0x00FF;
$extractedOS = ($unpack['versionNeededToExtract'] & 0xFF00) >> 8;
$extractVersion = $unpack['versionNeededToExtract'] & 0x00FF;
$dosTime = $unpack['lastModFile'];
$createdOS = ($versionMadeBy & 0xFF00) >> 8;
$softwareVersion = $versionMadeBy & 0x00FF;
$extractedOS = ($versionNeededToExtract & 0xFF00) >> 8;
$extractVersion = $versionNeededToExtract & 0x00FF;
$comment = null;
if ($unpack['fileCommentLength'] > 0) {
$comment = fread($stream, $unpack['fileCommentLength']);
if ($fileCommentLength > 0) {
$comment = fread($stream, $fileCommentLength);
}
// decode code page names
@@ -477,24 +485,23 @@ class ZipReader
$extractedOS,
$softwareVersion,
$extractVersion,
$unpack['compressionMethod'],
$compressionMethod,
$generalPurposeBitFlags,
$dosTime,
$unpack['crc'],
$unpack['compressedSize'],
$unpack['uncompressedSize'],
$unpack['internalFileAttributes'],
$unpack['externalFileAttributes'],
$unpack['offsetLocalHeader'],
$crc,
$compressedSize,
$uncompressedSize,
$internalFileAttributes,
$externalFileAttributes,
$offsetLocalHeader,
$comment,
$fallbackCharset
);
if ($unpack['extraFieldLength'] > 0) {
if ($extraFieldLength > 0) {
$this->parseExtraFields(
fread($stream, $unpack['extraFieldLength']),
$zipEntry,
false
fread($stream, $extraFieldLength),
$zipEntry
);
/** @var Zip64ExtraField|null $extraZip64 */
@@ -512,67 +519,55 @@ class ZipReader
return $zipEntry;
}
/**
* @param string $buffer
* @param ZipEntry $zipEntry
* @param bool $local
*
* @return ExtraFieldsCollection
*/
protected function parseExtraFields($buffer, ZipEntry $zipEntry, $local = false)
protected function parseExtraFields(string $buffer, ZipEntry $zipEntry, bool $local = false): ExtraFieldsCollection
{
$collection = $local ?
$zipEntry->getLocalExtraFields() :
$zipEntry->getCdExtraFields();
$collection = $local
? $zipEntry->getLocalExtraFields()
: $zipEntry->getCdExtraFields();
if (!empty($buffer)) {
$pos = 0;
$endPos = \strlen($buffer);
while ($endPos - $pos >= 4) {
/** @var int[] $data */
$data = unpack('vheaderId/vdataSize', substr($buffer, $pos, 4));
[
'headerId' => $headerId,
'dataSize' => $dataSize,
] = unpack('vheaderId/vdataSize', substr($buffer, $pos, 4));
$pos += 4;
if ($endPos - $pos - $data['dataSize'] < 0) {
if ($endPos - $pos - $dataSize < 0) {
break;
}
$bufferData = substr($buffer, $pos, $data['dataSize']);
$headerId = $data['headerId'];
$bufferData = substr($buffer, $pos, $dataSize);
/** @var string|ZipExtraField|null $className */
$className = ZipExtraDriver::getClassNameOrNull($headerId);
if ($className !== null) {
try {
$extraField = $local ?
\call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry) :
\call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
} catch (\Throwable $e) {
throw new \RuntimeException(
sprintf(
'Error parse %s extra field 0x%04X',
$local ? 'local' : 'central directory',
$headerId
)
);
try {
if ($className !== null) {
try {
$extraField = $local
? $className::unpackLocalFileData($bufferData, $zipEntry)
: $className::unpackCentralDirData($bufferData, $zipEntry);
} catch (\Throwable $e) {
// skip errors while parsing invalid data
continue;
}
} else {
$extraField = new UnrecognizedExtraField($headerId, $bufferData);
}
} else {
$extraField = new UnrecognizedExtraField($headerId, $bufferData);
$collection->add($extraField);
} finally {
$pos += $dataSize;
}
$collection->add($extraField);
$pos += $data['dataSize'];
}
}
return $collection;
}
/**
* @param Zip64ExtraField $extraZip64
* @param ZipEntry $zipEntry
*/
protected function handleZip64Extra(Zip64ExtraField $extraZip64, ZipEntry $zipEntry)
protected function handleZip64Extra(Zip64ExtraField $extraZip64, ZipEntry $zipEntry): void
{
$uncompressedSize = $extraZip64->getUncompressedSize();
$compressedSize = $extraZip64->getCompressedSize();
@@ -608,11 +603,9 @@ class ZipReader
* file name (variable size)
* extra field (variable size)
*
* @param ZipEntry $entry
*
* @throws ZipException
*/
protected function loadLocalExtraFields(ZipEntry $entry)
protected function loadLocalExtraFields(ZipEntry $entry): void
{
$offsetLocalHeader = $entry->getLocalHeaderOffset();
@@ -623,16 +616,16 @@ class ZipReader
}
fseek($this->inStream, $offsetLocalHeader + ZipConstants::LFH_FILENAME_LENGTH_POS);
$unpack = unpack('vfileNameLength/vextraFieldLength', fread($this->inStream, 4));
$offsetData = ftell($this->inStream)
+ $unpack['fileNameLength']
+ $unpack['extraFieldLength'];
[
'fileNameLength' => $fileNameLength,
'extraFieldLength' => $extraFieldLength,
] = unpack('vfileNameLength/vextraFieldLength', fread($this->inStream, 4));
$offsetData = ftell($this->inStream) + $fileNameLength + $extraFieldLength;
fseek($this->inStream, $fileNameLength, \SEEK_CUR);
fseek($this->inStream, $unpack['fileNameLength'], \SEEK_CUR);
if ($unpack['extraFieldLength'] > 0) {
if ($extraFieldLength > 0) {
$this->parseExtraFields(
fread($this->inStream, $unpack['extraFieldLength']),
fread($this->inStream, $extraFieldLength),
$entry,
true
);
@@ -643,11 +636,9 @@ class ZipReader
}
/**
* @param ZipEntry $zipEntry
*
* @throws ZipException
*/
private function handleExtraEncryptionFields(ZipEntry $zipEntry)
private function handleExtraEncryptionFields(ZipEntry $zipEntry): void
{
if ($zipEntry->isEncrypted()) {
if ($zipEntry->getCompressionMethod() === ZipCompressionMethod::WINZIP_AES) {
@@ -676,16 +667,12 @@ class ZipReader
*
* This is a special method in which you can process ExtraField
* and make changes to ZipEntry.
*
* @param ZipEntry $zipEntry
*/
protected function handleExtraFields(ZipEntry $zipEntry)
protected function handleExtraFields(ZipEntry $zipEntry): void
{
}
/**
* @param ZipSourceFileData $zipFileData
*
* @throws ZipException
* @throws Crc32Exception
*
@@ -701,13 +688,12 @@ class ZipReader
}
/**
* @param ZipSourceFileData $zipFileData
* @param resource $outStream
* @param resource $outStream
*
* @throws Crc32Exception
* @throws ZipException
*/
public function copyUncompressedDataToStream(ZipSourceFileData $zipFileData, $outStream)
public function copyUncompressedDataToStream(ZipSourceFileData $zipFileData, $outStream): void
{
if (!\is_resource($outStream)) {
throw new InvalidArgumentException('outStream is not resource');
@@ -865,10 +851,9 @@ class ZipReader
}
/**
* @param ZipSourceFileData $zipData
* @param resource $outStream
* @param resource $outStream
*/
public function copyCompressedDataToStream(ZipSourceFileData $zipData, $outStream)
public function copyCompressedDataToStream(ZipSourceFileData $zipData, $outStream): void
{
if ($zipData->getCompressedSize() > 0) {
fseek($this->inStream, $zipData->getOffset());
@@ -876,15 +861,15 @@ class ZipReader
}
}
/**
* @return bool
*/
protected function isZip64Support()
protected function isZip64Support(): bool
{
return \PHP_INT_SIZE === 8; // true for 64bit system
}
public function close()
/**
* @psalm-suppress InvalidPropertyAssignmentValue
*/
public function close(): void
{
if (\is_resource($this->inStream)) {
fclose($this->inStream);

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO;
use PhpZip\Constants\DosCodePage;
@@ -13,33 +22,23 @@ use PhpZip\Exception\ZipUnsupportMethodException;
use PhpZip\IO\Filter\Cipher\Pkware\PKEncryptionStreamFilter;
use PhpZip\IO\Filter\Cipher\WinZipAes\WinZipAesEncryptionStreamFilter;
use PhpZip\Model\Data\ZipSourceFileData;
use PhpZip\Model\Extra\Fields\ApkAlignmentExtraField;
use PhpZip\Model\Extra\Fields\WinZipAesExtraField;
use PhpZip\Model\Extra\Fields\Zip64ExtraField;
use PhpZip\Model\ZipContainer;
use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
use PhpZip\Util\StringUtil;
/**
* Class ZipWriter.
*/
class ZipWriter
{
/** @var int Chunk read size */
const CHUNK_SIZE = 8192;
public const CHUNK_SIZE = 8192;
/** @var ZipContainer */
protected $zipContainer;
protected ZipContainer $zipContainer;
/**
* ZipWriter constructor.
*
* @param ZipContainer $container
*/
public function __construct(ZipContainer $container)
{
$this->zipContainer = $container;
// we clone the container so that the changes made to
// it do not affect the data in the ZipFile class
$this->zipContainer = clone $container;
}
/**
@@ -47,7 +46,7 @@ class ZipWriter
*
* @throws ZipException
*/
public function write($outStream)
public function write($outStream): void
{
if (!\is_resource($outStream)) {
throw new \InvalidArgumentException('$outStream must be resource');
@@ -60,7 +59,7 @@ class ZipWriter
$this->writeEndOfCentralDirectoryBlock($outStream, $cdOffset, $cdSize);
}
protected function beforeWrite()
protected function beforeWrite(): void
{
}
@@ -69,7 +68,7 @@ class ZipWriter
*
* @throws ZipException
*/
protected function writeLocalBlock($outStream)
protected function writeLocalBlock($outStream): void
{
$zipEntries = $this->zipContainer->getEntries();
@@ -85,17 +84,11 @@ class ZipWriter
/**
* @param resource $outStream
* @param ZipEntry $entry
*
* @throws ZipException
*/
protected function writeLocalHeader($outStream, ZipEntry $entry)
protected function writeLocalHeader($outStream, ZipEntry $entry): void
{
// todo in 4.0 version move zipalign functional to ApkWriter class
if ($this->zipContainer->isZipAlign()) {
$this->zipAlign($outStream, $entry);
}
$relativeOffset = ftell($outStream);
$entry->setLocalHeaderOffset($relativeOffset);
@@ -103,8 +96,8 @@ class ZipWriter
$entry->enableDataDescriptor(true);
}
$dd = $entry->isDataDescriptorRequired() ||
$entry->isDataDescriptorEnabled();
$dd = $entry->isDataDescriptorRequired()
|| $entry->isDataDescriptorEnabled();
$compressedSize = $entry->getCompressedSize();
$uncompressedSize = $entry->getUncompressedSize();
@@ -150,13 +143,13 @@ class ZipWriter
$size = $nameLength + $extraLength;
if ($size > 0xffff) {
if ($size > 0xFFFF) {
throw new ZipException(
sprintf(
'%s (the total size of %s bytes for the name, extra fields and comment exceeds the maximum size of %d bytes)',
$entry->getName(),
$size,
0xffff
0xFFFF
)
);
}
@@ -200,75 +193,16 @@ class ZipWriter
}
}
/**
* @param resource $outStream
* @param ZipEntry $entry
*
* @throws ZipException
*/
private function zipAlign($outStream, ZipEntry $entry)
{
if (!$entry->isDirectory() && $entry->getCompressionMethod() === ZipCompressionMethod::STORED) {
$entry->removeExtraField(ApkAlignmentExtraField::HEADER_ID);
$extra = $this->getExtraFieldsContents($entry, true);
$extraLength = \strlen($extra);
$name = $entry->getName();
$dosCharset = $entry->getCharset();
if ($dosCharset !== null && !$entry->isUtf8Flag()) {
$name = DosCodePage::fromUTF8($name, $dosCharset);
}
$nameLength = \strlen($name);
$multiple = ApkAlignmentExtraField::ALIGNMENT_BYTES;
if (StringUtil::endsWith($name, '.so')) {
$multiple = ApkAlignmentExtraField::COMMON_PAGE_ALIGNMENT_BYTES;
}
$offset = ftell($outStream);
$dataMinStartOffset =
$offset +
ZipConstants::LFH_FILENAME_POS +
$extraLength +
$nameLength;
$padding =
($multiple - ($dataMinStartOffset % $multiple))
% $multiple;
if ($padding > 0) {
$dataMinStartOffset += ApkAlignmentExtraField::MIN_SIZE;
$padding =
($multiple - ($dataMinStartOffset % $multiple))
% $multiple;
$entry->getLocalExtraFields()->add(
new ApkAlignmentExtraField($multiple, $padding)
);
}
}
}
/**
* Merges the local file data fields of the given ZipExtraFields.
*
* @param ZipEntry $entry
* @param bool $local
*
* @throws ZipException
*
* @return string
*/
protected function getExtraFieldsContents(ZipEntry $entry, $local)
protected function getExtraFieldsContents(ZipEntry $entry, bool $local): string
{
$local = (bool) $local;
$collection = $local ?
$entry->getLocalExtraFields() :
$entry->getCdExtraFields();
$collection = $local
? $entry->getLocalExtraFields()
: $entry->getCdExtraFields();
$extraData = '';
foreach ($collection as $extraField) {
@@ -287,7 +221,7 @@ class ZipWriter
$size = \strlen($extraData);
if ($size > 0xffff) {
if ($size > 0xFFFF) {
throw new ZipException(
sprintf(
'Size extra out of range: %d. Extra data: %s',
@@ -302,11 +236,10 @@ class ZipWriter
/**
* @param resource $outStream
* @param ZipEntry $entry
*
* @throws ZipException
*/
protected function writeData($outStream, ZipEntry $entry)
protected function writeData($outStream, ZipEntry $entry): void
{
$zipData = $entry->getData();
@@ -447,11 +380,8 @@ class ZipWriter
/**
* @param resource $inStream
* @param resource $outStream
* @param int $size
*
* @return int
*/
private function writeAndCountChecksum($inStream, $outStream, $size)
private function writeAndCountChecksum($inStream, $outStream, int $size): int
{
$contextHash = hash_init('crc32b');
$offset = 0;
@@ -469,7 +399,6 @@ class ZipWriter
/**
* @param resource $outStream
* @param ZipEntry $entry
*
* @throws ZipUnsupportMethodException
*
@@ -521,12 +450,10 @@ class ZipWriter
/**
* @param resource $outStream
* @param ZipEntry $entry
* @param int $size
*
* @return resource|null
*/
protected function appendEncryptionFilter($outStream, ZipEntry $entry, $size)
protected function appendEncryptionFilter($outStream, ZipEntry $entry, int $size)
{
$encContextFilter = null;
@@ -558,9 +485,8 @@ class ZipWriter
/**
* @param resource $outStream
* @param ZipEntry $entry
*/
protected function writeDataDescriptor($outStream, ZipEntry $entry)
protected function writeDataDescriptor($outStream, ZipEntry $entry): void
{
$crc = $entry->getCrc();
@@ -583,14 +509,16 @@ class ZipWriter
);
if (
$entry->isZip64ExtensionsRequired() ||
$entry->getLocalExtraFields()->has(Zip64ExtraField::HEADER_ID)
$entry->isZip64ExtensionsRequired()
|| $entry->getLocalExtraFields()->has(Zip64ExtraField::HEADER_ID)
) {
$dd =
$dd = pack(
'PP',
// compressed size 8 bytes
PackUtil::packLongLE($entry->getCompressedSize()) .
$entry->getCompressedSize(),
// uncompressed size 8 bytes
PackUtil::packLongLE($entry->getUncompressedSize());
$entry->getUncompressedSize()
);
} else {
$dd = pack(
'VV',
@@ -609,7 +537,7 @@ class ZipWriter
*
* @throws ZipException
*/
protected function writeCentralDirectoryBlock($outStream)
protected function writeCentralDirectoryBlock($outStream): void
{
foreach ($this->zipContainer->getEntries() as $outputEntry) {
$this->writeCentralDirectoryHeader($outStream, $outputEntry);
@@ -620,11 +548,10 @@ class ZipWriter
* Writes a Central File Header record.
*
* @param resource $outStream
* @param ZipEntry $entry
*
* @throws ZipException
*/
protected function writeCentralDirectoryHeader($outStream, ZipEntry $entry)
protected function writeCentralDirectoryHeader($outStream, ZipEntry $entry): void
{
$compressedSize = $entry->getCompressedSize();
$uncompressedSize = $entry->getUncompressedSize();
@@ -633,9 +560,9 @@ class ZipWriter
$entry->getCdExtraFields()->remove(Zip64ExtraField::HEADER_ID);
if (
$localHeaderOffset > ZipConstants::ZIP64_MAGIC ||
$compressedSize > ZipConstants::ZIP64_MAGIC ||
$uncompressedSize > ZipConstants::ZIP64_MAGIC
$localHeaderOffset > ZipConstants::ZIP64_MAGIC
|| $compressedSize > ZipConstants::ZIP64_MAGIC
|| $uncompressedSize > ZipConstants::ZIP64_MAGIC
) {
$zip64ExtraField = new Zip64ExtraField();
@@ -743,17 +670,15 @@ class ZipWriter
/**
* @param resource $outStream
* @param int $centralDirectoryOffset
* @param int $centralDirectorySize
*/
protected function writeEndOfCentralDirectoryBlock(
$outStream,
$centralDirectoryOffset,
$centralDirectorySize
) {
int $centralDirectoryOffset,
int $centralDirectorySize
): void {
$cdEntriesCount = \count($this->zipContainer);
$cdEntriesZip64 = $cdEntriesCount > 0xffff;
$cdEntriesZip64 = $cdEntriesCount > 0xFFFF;
$cdSizeZip64 = $centralDirectorySize > ZipConstants::ZIP64_MAGIC;
$cdOffsetZip64 = $centralDirectoryOffset > ZipConstants::ZIP64_MAGIC;
@@ -765,7 +690,7 @@ class ZipWriter
$zip64EndOfCentralDirectoryOffset = ftell($outStream);
// find max software version, version needed to extract and most common platform
list($softwareVersion, $versionNeededToExtract) = array_reduce(
[$softwareVersion, $versionNeededToExtract] = array_reduce(
$this->zipContainer->getEntries(),
static function (array $carry, ZipEntry $entry) {
$carry[0] = max($carry[0], $entry->getSoftwareVersion() & 0xFF);
@@ -784,18 +709,12 @@ class ZipWriter
fwrite(
$outStream,
pack(
'V',
'VPvvVVPPPPVVPV',
// signature 4 bytes (0x06064b50)
ZipConstants::ZIP64_END_CD
)
);
// size of zip64 end of central
// directory record 8 bytes
fwrite($outStream, PackUtil::packLongLE(ZipConstants::ZIP64_END_OF_CD_LEN - 12));
fwrite(
$outStream,
pack(
'vvVV',
ZipConstants::ZIP64_END_CD,
// size of zip64 end of central
// directory record 8 bytes
ZipConstants::ZIP64_END_OF_CD_LEN - 12,
// version made by 2 bytes
$versionMadeBy & 0xFFFF,
// version needed to extract 2 bytes
@@ -804,44 +723,32 @@ class ZipWriter
0,
// number of the disk with the
// start of the central directory 4 bytes
0
)
);
fwrite(
$outStream,
// total number of entries in the
// central directory on this disk 8 bytes
PackUtil::packLongLE($cdEntriesCount) .
// total number of entries in the
// central directory 8 bytes
PackUtil::packLongLE($cdEntriesCount) .
// size of the central directory 8 bytes
PackUtil::packLongLE($centralDirectorySize) .
// offset of start of central
// directory with respect to
// the starting disk number 8 bytes
PackUtil::packLongLE($centralDirectoryOffset)
);
// write zip64 end of central directory locator
fwrite(
$outStream,
pack(
'VV',
0,
// total number of entries in the
// central directory on this disk 8 bytes
$cdEntriesCount,
// total number of entries in the
// central directory 8 bytes
$cdEntriesCount,
// size of the central directory 8 bytes
$centralDirectorySize,
// offset of start of central
// directory with respect to
// the starting disk number 8 bytes
$centralDirectoryOffset,
// zip64 end of central dir locator
// signature 4 bytes (0x07064b50)
ZipConstants::ZIP64_END_CD_LOC,
// number of the disk with the
// start of the zip64 end of
// central directory 4 bytes
0
) .
// relative offset of the zip64
// end of central directory record 8 bytes
PackUtil::packLongLE($zip64EndOfCentralDirectoryOffset) .
// total number of disks 4 bytes
pack('V', 1)
0,
// relative offset of the zip64
// end of central directory record 8 bytes
$zip64EndOfCentralDirectoryOffset,
// total number of disks 4 bytes
1
)
);
}
@@ -861,10 +768,10 @@ class ZipWriter
0,
// total number of entries in the
// central directory on this disk 2 bytes
$cdEntriesZip64 ? 0xffff : $cdEntriesCount,
$cdEntriesZip64 ? 0xFFFF : $cdEntriesCount,
// total number of entries in
// the central directory 2 bytes
$cdEntriesZip64 ? 0xffff : $cdEntriesCount,
$cdEntriesZip64 ? 0xFFFF : $cdEntriesCount,
// size of the central directory 4 bytes
$cdSizeZip64 ? ZipConstants::ZIP64_MAGIC : $centralDirectorySize,
// offset of start of central

View File

@@ -1,26 +1,28 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Data;
use PhpZip\Exception\ZipException;
use PhpZip\Model\ZipData;
use PhpZip\Model\ZipEntry;
/**
* Class ZipFileData.
*/
class ZipFileData implements ZipData
{
/** @var \SplFileInfo */
private $file;
private \SplFileInfo $file;
/**
* ZipStringData constructor.
*
* @param \SplFileInfo $fileInfo
*
* @throws ZipException
*/
public function __construct(\SplFileInfo $fileInfo)
public function __construct(ZipEntry $zipEntry, \SplFileInfo $fileInfo)
{
if (!$fileInfo->isFile()) {
throw new ZipException('$fileInfo is not a file.');
@@ -31,6 +33,7 @@ class ZipFileData implements ZipData
}
$this->file = $fileInfo;
$zipEntry->setUncompressedSize($fileInfo->getSize());
}
/**
@@ -52,7 +55,7 @@ class ZipFileData implements ZipData
*
* @return string returns data as string
*/
public function getDataAsString()
public function getDataAsString(): string
{
if (!$this->file->isReadable()) {
throw new ZipException(sprintf('The %s file is no longer readable.', $this->file->getPathname()));
@@ -66,13 +69,10 @@ class ZipFileData implements ZipData
*
* @throws ZipException
*/
public function copyDataToStream($outStream)
public function copyDataToStream($outStream): void
{
try {
$stream = $this->getDataAsStream();
stream_copy_to_stream($stream, $outStream);
} finally {
fclose($stream);
}
$stream = $this->getDataAsStream();
stream_copy_to_stream($stream, $outStream);
fclose($stream);
}
}

View File

@@ -1,26 +1,41 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Data;
use PhpZip\Model\ZipData;
use PhpZip\Model\ZipEntry;
use PhpZip\ZipFile;
/**
* Class ZipNewData.
* The class contains a streaming resource with new content added to the ZIP archive.
*/
class ZipNewData implements ZipData
{
/**
* A static variable allows closing the stream in the destructor
* only if it is its sole holder.
*
* @var array<int, int> array of resource ids and the number of class clones
*/
private static array $guardClonedStream = [];
private ZipEntry $zipEntry;
/** @var resource */
private $stream;
/** @var ZipEntry */
private $zipEntry;
/**
* ZipStringData constructor.
*
* @param ZipEntry $zipEntry
* @param string|resource $data
* @param string|resource $data Raw string data or resource
* @noinspection PhpMissingParamTypeInspection
*/
public function __construct(ZipEntry $zipEntry, $data)
{
@@ -30,7 +45,9 @@ class ZipNewData implements ZipData
$zipEntry->setUncompressedSize(\strlen($data));
if (!($handle = fopen('php://temp', 'w+b'))) {
throw new \RuntimeException('Temp resource can not open from write.');
// @codeCoverageIgnoreStart
throw new \RuntimeException('A temporary resource cannot be opened for writing.');
// @codeCoverageIgnoreEnd
}
fwrite($handle, $data);
rewind($handle);
@@ -38,6 +55,12 @@ class ZipNewData implements ZipData
} elseif (\is_resource($data)) {
$this->stream = $data;
}
$resourceId = (int) $this->stream;
self::$guardClonedStream[$resourceId]
= isset(self::$guardClonedStream[$resourceId])
? self::$guardClonedStream[$resourceId] + 1
: 0;
}
/**
@@ -46,7 +69,7 @@ class ZipNewData implements ZipData
public function getDataAsStream()
{
if (!\is_resource($this->stream)) {
throw new \LogicException(sprintf('Resource was closed (entry=%s).', $this->zipEntry->getName()));
throw new \LogicException(sprintf('Resource has been closed (entry=%s).', $this->zipEntry->getName()));
}
return $this->stream;
@@ -55,7 +78,7 @@ class ZipNewData implements ZipData
/**
* @return string returns data as string
*/
public function getDataAsString()
public function getDataAsString(): string
{
$stream = $this->getDataAsStream();
$pos = ftell($stream);
@@ -72,15 +95,42 @@ class ZipNewData implements ZipData
/**
* @param resource $outStream
*/
public function copyDataToStream($outStream)
public function copyDataToStream($outStream): void
{
$stream = $this->getDataAsStream();
rewind($stream);
stream_copy_to_stream($stream, $outStream);
}
/**
* @see https://php.net/manual/en/language.oop5.cloning.php
*/
public function __clone()
{
$resourceId = (int) $this->stream;
self::$guardClonedStream[$resourceId]
= isset(self::$guardClonedStream[$resourceId])
? self::$guardClonedStream[$resourceId] + 1
: 1;
}
/**
* The stream will be closed when closing the zip archive.
*
* The method implements protection against closing the stream of the cloned object.
*
* @see ZipFile::close()
*/
public function __destruct()
{
$resourceId = (int) $this->stream;
if (isset(self::$guardClonedStream[$resourceId]) && self::$guardClonedStream[$resourceId] > 0) {
self::$guardClonedStream[$resourceId]--;
return;
}
if (\is_resource($this->stream)) {
fclose($this->stream);
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Data;
use PhpZip\Exception\Crc32Exception;
@@ -8,37 +17,22 @@ use PhpZip\IO\ZipReader;
use PhpZip\Model\ZipData;
use PhpZip\Model\ZipEntry;
/**
* Class ZipFileData.
*/
class ZipSourceFileData implements ZipData
{
/** @var ZipReader */
private $zipReader;
private ZipReader $zipReader;
/** @var resource|null */
private $stream;
/** @var ZipEntry */
private $sourceEntry;
private ZipEntry $sourceEntry;
/** @var int */
private $offset;
private int $offset;
/** @var int */
private $uncompressedSize;
private int $uncompressedSize;
/** @var int */
private $compressedSize;
private int $compressedSize;
/**
* ZipFileData constructor.
*
* @param ZipReader $zipReader
* @param ZipEntry $zipEntry
* @param int $offsetData
*/
public function __construct(ZipReader $zipReader, ZipEntry $zipEntry, $offsetData)
public function __construct(ZipReader $zipReader, ZipEntry $zipEntry, int $offsetData)
{
$this->zipReader = $zipReader;
$this->offset = $offsetData;
@@ -47,21 +41,16 @@ class ZipSourceFileData implements ZipData
$this->uncompressedSize = $zipEntry->getUncompressedSize();
}
/**
* @param ZipEntry $entry
*
* @return bool
*/
public function hasRecompressData(ZipEntry $entry)
public function hasRecompressData(ZipEntry $entry): bool
{
return $this->sourceEntry->getCompressionLevel() !== $entry->getCompressionLevel() ||
$this->sourceEntry->getCompressionMethod() !== $entry->getCompressionMethod() ||
$this->sourceEntry->isEncrypted() !== $entry->isEncrypted() ||
$this->sourceEntry->getEncryptionMethod() !== $entry->getEncryptionMethod() ||
$this->sourceEntry->getPassword() !== $entry->getPassword() ||
$this->sourceEntry->getCompressedSize() !== $entry->getCompressedSize() ||
$this->sourceEntry->getUncompressedSize() !== $entry->getUncompressedSize() ||
$this->sourceEntry->getCrc() !== $entry->getCrc();
return $this->sourceEntry->getCompressionLevel() !== $entry->getCompressionLevel()
|| $this->sourceEntry->getCompressionMethod() !== $entry->getCompressionMethod()
|| $this->sourceEntry->isEncrypted() !== $entry->isEncrypted()
|| $this->sourceEntry->getEncryptionMethod() !== $entry->getEncryptionMethod()
|| $this->sourceEntry->getPassword() !== $entry->getPassword()
|| $this->sourceEntry->getCompressedSize() !== $entry->getCompressedSize()
|| $this->sourceEntry->getUncompressedSize() !== $entry->getUncompressedSize()
|| $this->sourceEntry->getCrc() !== $entry->getCrc();
}
/**
@@ -83,7 +72,7 @@ class ZipSourceFileData implements ZipData
*
* @return string returns data as string
*/
public function getDataAsString()
public function getDataAsString(): string
{
$autoClosable = $this->stream === null;
@@ -105,64 +94,49 @@ class ZipSourceFileData implements ZipData
}
/**
* @param resource $outputStream Output stream
* @param resource $outStream Output stream
*
* @throws ZipException
* @throws Crc32Exception
*/
public function copyDataToStream($outputStream)
public function copyDataToStream($outStream): void
{
if (\is_resource($this->stream)) {
rewind($this->stream);
stream_copy_to_stream($this->stream, $outputStream);
stream_copy_to_stream($this->stream, $outStream);
} else {
$this->zipReader->copyUncompressedDataToStream($this, $outputStream);
$this->zipReader->copyUncompressedDataToStream($this, $outStream);
}
}
/**
* @param resource $outputStream Output stream
*/
public function copyCompressedDataToStream($outputStream)
public function copyCompressedDataToStream($outputStream): void
{
$this->zipReader->copyCompressedDataToStream($this, $outputStream);
}
/**
* @return ZipEntry
*/
public function getSourceEntry()
public function getSourceEntry(): ZipEntry
{
return $this->sourceEntry;
}
/**
* @return int
*/
public function getCompressedSize()
public function getCompressedSize(): int
{
return $this->compressedSize;
}
/**
* @return int
*/
public function getUncompressedSize()
public function getUncompressedSize(): int
{
return $this->uncompressedSize;
}
/**
* @return int
*/
public function getOffset()
public function getOffset(): int
{
return $this->offset;
}
/**
* {@inheritdoc}
*/
public function __destruct()
{
if (\is_resource($this->stream)) {

View File

@@ -1,40 +1,36 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model;
/**
* End of Central Directory.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class EndOfCentralDirectory
{
/** @var int Count files. */
private $entryCount;
private int $entryCount;
/** @var int Central Directory Offset. */
private $cdOffset;
private int $cdOffset;
/** @var int */
private $cdSize;
private int $cdSize;
/** @var string|null The archive comment. */
private $comment;
private ?string $comment;
/** @var bool Zip64 extension */
private $zip64;
private bool $zip64;
/**
* EndOfCentralDirectory constructor.
*
* @param int $entryCount
* @param int $cdOffset
* @param int $cdSize
* @param bool $zip64
* @param string|null $comment
*/
public function __construct($entryCount, $cdOffset, $cdSize, $zip64, $comment = null)
public function __construct(int $entryCount, int $cdOffset, int $cdSize, bool $zip64, ?string $comment = null)
{
$this->entryCount = $entryCount;
$this->cdOffset = $cdOffset;
@@ -43,50 +39,32 @@ class EndOfCentralDirectory
$this->comment = $comment;
}
/**
* @param string|null $comment
*/
public function setComment($comment)
public function setComment(?string $comment): void
{
$this->comment = $comment;
}
/**
* @return int
*/
public function getEntryCount()
public function getEntryCount(): int
{
return $this->entryCount;
}
/**
* @return int
*/
public function getCdOffset()
public function getCdOffset(): int
{
return $this->cdOffset;
}
/**
* @return int
*/
public function getCdSize()
public function getCdSize(): int
{
return $this->cdSize;
}
/**
* @return string|null
*/
public function getComment()
public function getComment(): ?string
{
return $this->comment;
}
/**
* @return bool
*/
public function isZip64()
public function isZip64(): bool
{
return $this->zip64;
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra;
/**
@@ -16,14 +25,12 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @var ZipExtraField[]
*/
protected $collection = [];
protected array $collection = [];
/**
* Returns the number of Extra Fields in this collection.
*
* @return int
*/
public function count()
public function count(): int
{
return \count($this->collection);
}
@@ -37,19 +44,16 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
* @return ZipExtraField|null the Extra Field with the given Header ID or
* if no such Extra Field exists
*/
public function get($headerId)
public function get(int $headerId): ?ZipExtraField
{
$this->validateHeaderId($headerId);
return isset($this->collection[$headerId]) ? $this->collection[$headerId] : null;
return $this->collection[$headerId] ?? null;
}
/**
* @param int $headerId
*/
private function validateHeaderId($headerId)
private function validateHeaderId(int $headerId): void
{
if ($headerId < 0 || $headerId > 0xffff) {
if ($headerId < 0 || $headerId > 0xFFFF) {
throw new \InvalidArgumentException('$headerId out of range');
}
}
@@ -62,7 +66,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
* @return ZipExtraField the Extra Field previously associated with the Header ID of
* of the given Extra Field or null if no such Extra Field existed
*/
public function add(ZipExtraField $extraField)
public function add(ZipExtraField $extraField): ZipExtraField
{
$headerId = $extraField->getHeaderId();
@@ -75,7 +79,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
/**
* @param ZipExtraField[] $extraFields
*/
public function addAll(array $extraFields)
public function addAll(array $extraFields): void
{
foreach ($extraFields as $extraField) {
$this->add($extraField);
@@ -85,7 +89,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
/**
* @param ExtraFieldsCollection $collection
*/
public function addCollection(self $collection)
public function addCollection(self $collection): void
{
$this->addAll($collection->collection);
}
@@ -93,7 +97,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
/**
* @return ZipExtraField[]
*/
public function getAll()
public function getAll(): array
{
return $this->collection;
}
@@ -102,10 +106,8 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
* Returns Extra Field exists.
*
* @param int $headerId the requested Header ID
*
* @return bool
*/
public function has($headerId)
public function has(int $headerId): bool
{
return isset($this->collection[$headerId]);
}
@@ -118,7 +120,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
* @return ZipExtraField|null the Extra Field with the given Header ID or null
* if no such Extra Field exists
*/
public function remove($headerId)
public function remove(int $headerId): ?ZipExtraField
{
$this->validateHeaderId($headerId);
@@ -137,11 +139,11 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @see http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param int $offset an offset to check for
* @param mixed $offset an offset to check for
*
* @return bool true on success or false on failure
*/
public function offsetExists($offset)
public function offsetExists($offset): bool
{
return isset($this->collection[(int) $offset]);
}
@@ -151,13 +153,11 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @see http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param int $offset the offset to retrieve
*
* @return ZipExtraField|null
* @param mixed $offset the offset to retrieve
*/
public function offsetGet($offset)
public function offsetGet($offset): ?ZipExtraField
{
return isset($this->collection[$offset]) ? $this->collection[$offset] : null;
return $this->collection[(int) $offset] ?? null;
}
/**
@@ -165,10 +165,10 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @see http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset the offset to assign the value to
* @param ZipExtraField $value the value to set
* @param mixed $offset the offset to assign the value to
* @param mixed $value the value to set
*/
public function offsetSet($offset, $value)
public function offsetSet($offset, $value): void
{
if (!$value instanceof ZipExtraField) {
throw new \InvalidArgumentException('value is not instanceof ' . ZipExtraField::class);
@@ -183,7 +183,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @param mixed $offset the offset to unset
*/
public function offsetUnset($offset)
public function offsetUnset($offset): void
{
$this->remove($offset);
}
@@ -192,10 +192,8 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
* Return the current element.
*
* @see http://php.net/manual/en/iterator.current.php
*
* @return ZipExtraField
*/
public function current()
public function current(): ZipExtraField
{
return current($this->collection);
}
@@ -205,7 +203,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @see http://php.net/manual/en/iterator.next.php
*/
public function next()
public function next(): void
{
next($this->collection);
}
@@ -217,7 +215,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @return int scalar on success, or null on failure
*/
public function key()
public function key(): int
{
return key($this->collection);
}
@@ -230,7 +228,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
* @return bool The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid()
public function valid(): bool
{
return key($this->collection) !== null;
}
@@ -240,20 +238,17 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @see http://php.net/manual/en/iterator.rewind.php
*/
public function rewind()
public function rewind(): void
{
reset($this->collection);
}
public function clear()
public function clear(): void
{
$this->collection = [];
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
$formats = [];

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -11,47 +20,33 @@ use PhpZip\Model\ZipEntry;
*/
abstract class AbstractUnicodeExtraField implements ZipExtraField
{
const DEFAULT_VERSION = 0x01;
public const DEFAULT_VERSION = 0x01;
/** @var int */
private $crc32;
private int $crc32;
/** @var string */
private $unicodeValue;
private string $unicodeValue;
/**
* @param int $crc32
* @param string $unicodeValue
*/
public function __construct($crc32, $unicodeValue)
public function __construct(int $crc32, string $unicodeValue)
{
$this->crc32 = (int) $crc32;
$this->unicodeValue = (string) $unicodeValue;
}
/**
* @param string $unicodeValue
*
* @return static
*/
public static function create($unicodeValue)
{
return new static(crc32($unicodeValue), $unicodeValue);
$this->crc32 = $crc32;
$this->unicodeValue = $unicodeValue;
}
/**
* @return int the CRC32 checksum of the filename or comment as
* encoded in the central directory of the zip file
*/
public function getCrc32()
public function getCrc32(): int
{
return $this->crc32;
}
/**
* @return string
*/
public function getUnicodeValue()
public function setCrc32(int $crc32): void
{
$this->crc32 = $crc32;
}
public function getUnicodeValue(): string
{
return $this->unicodeValue;
}
@@ -59,35 +54,36 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
/**
* @param string $unicodeValue the UTF-8 encoded name to set
*/
public function setUnicodeValue($unicodeValue)
public function setUnicodeValue(string $unicodeValue): void
{
$this->unicodeValue = $unicodeValue;
$this->crc32 = crc32($unicodeValue);
}
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException on error
*
* @return static
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
if (\strlen($buffer) < 5) {
throw new ZipException('UniCode path extra data must have at least 5 bytes.');
throw new ZipException('Unicode path extra data must have at least 5 bytes.');
}
$version = unpack('C', $buffer)[1];
[
'version' => $version,
'crc32' => $crc32,
] = unpack('Cversion/Vcrc32', $buffer);
if ($version !== self::DEFAULT_VERSION) {
throw new ZipException(sprintf('Unsupported version [%d] for UniCode path extra data.', $version));
throw new ZipException(sprintf('Unsupported version [%d] for Unicode path extra data.', $version));
}
$crc32 = unpack('V', substr($buffer, 1))[1];
$unicodeValue = substr($buffer, 5);
return new static($crc32, $unicodeValue);
@@ -97,13 +93,13 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException on error
*
* @return static
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
@@ -114,14 +110,14 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
return pack(
'CV',
self::DEFAULT_VERSION,
$this->crc32
) .
$this->unicodeValue;
)
. $this->unicodeValue;
}
/**
@@ -130,7 +126,7 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return $this->packLocalFileData();
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -12,7 +21,7 @@ use PhpZip\Model\ZipEntry;
* @see https://android.googlesource.com/platform/tools/apksig/+/master/src/main/java/com/android/apksig/ApkSigner.java
* @see https://developer.android.com/studio/command-line/zipalign
*/
class ApkAlignmentExtraField implements ZipExtraField
final class ApkAlignmentExtraField implements ZipExtraField
{
/**
* @var int Extensible data block/field header ID used for storing
@@ -20,31 +29,19 @@ class ApkAlignmentExtraField implements ZipExtraField
* well as for aligning the entries's data. See ZIP
* appnote.txt section 4.5 Extensible data fields.
*/
const HEADER_ID = 0xd935;
/**
* @var int minimum size (in bytes) of the extensible data block/field used
* for alignment of uncompressed entries
*/
const MIN_SIZE = 6;
public const HEADER_ID = 0xD935;
/** @var int */
const ALIGNMENT_BYTES = 4;
public const ALIGNMENT_BYTES = 4;
/** @var int */
const COMMON_PAGE_ALIGNMENT_BYTES = 4096;
public const COMMON_PAGE_ALIGNMENT_BYTES = 4096;
/** @var int */
private $multiple;
private int $multiple;
/** @var int */
private $padding;
private int $padding;
/**
* @param int $multiple
* @param int $padding
*/
public function __construct($multiple, $padding)
public function __construct(int $multiple, int $padding)
{
$this->multiple = $multiple;
$this->padding = $padding;
@@ -54,57 +51,43 @@ class ApkAlignmentExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
/**
* @return int
*/
public function getMultiple()
public function getMultiple(): int
{
return $this->multiple;
}
/**
* @return int
*/
public function getPadding()
public function getPadding(): int
{
return $this->padding;
}
/**
* @param int $multiple
*/
public function setMultiple($multiple)
public function setMultiple(int $multiple): void
{
$this->multiple = (int) $multiple;
$this->multiple = $multiple;
}
/**
* @param int $padding
*/
public function setPadding($padding)
public function setPadding(int $padding): void
{
$this->padding = (int) $padding;
$this->padding = $padding;
}
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException
*
* @return ApkAlignmentExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$length = \strlen($buffer);
@@ -128,13 +111,13 @@ class ApkAlignmentExtraField implements ZipExtraField
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException on error
*
* @return ApkAlignmentExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
@@ -145,7 +128,7 @@ class ApkAlignmentExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
return pack('vx' . $this->padding, $this->multiple);
}
@@ -156,15 +139,12 @@ class ApkAlignmentExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return $this->packLocalFileData();
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return sprintf(
'0x%04x APK Alignment: Multiple=%d Padding=%d',

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Constants\UnixStat;
@@ -44,55 +53,45 @@ use PhpZip\Model\ZipEntry;
*
* @see ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip Info-ZIP version Specification
*/
class AsiExtraField implements ZipExtraField
final class AsiExtraField implements ZipExtraField
{
/** @var int Header id */
const HEADER_ID = 0x756e;
public const HEADER_ID = 0x756E;
const USER_GID_PID = 1000;
public const USER_GID_PID = 1000;
/** Bits used for permissions (and sticky bit). */
const PERM_MASK = 07777;
public const PERM_MASK = 07777;
/** @var int Standard Unix stat(2) file mode. */
private $mode;
private int $mode;
/** @var int User ID. */
private $uid;
private int $uid;
/** @var int Group ID. */
private $gid;
private int $gid;
/**
* @var string File this entry points to, if it is a symbolic link.
* Empty string - if entry is not a symbolic link.
*/
private $link;
private string $link;
/**
* AsiExtraField constructor.
*
* @param int $mode
* @param int $uid
* @param int $gid
* @param string $link
*/
public function __construct($mode, $uid = self::USER_GID_PID, $gid = self::USER_GID_PID, $link = '')
public function __construct(int $mode, int $uid = self::USER_GID_PID, int $gid = self::USER_GID_PID, string $link = '')
{
$this->mode = (int) $mode;
$this->uid = (int) $uid;
$this->gid = (int) $gid;
$this->link = (string) $link;
$this->mode = $mode;
$this->uid = $uid;
$this->gid = $gid;
$this->link = $link;
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -101,13 +100,13 @@ class AsiExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws Crc32Exception
*
* @return static
* @return AsiExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$givenChecksum = unpack('V', $buffer)[1];
$buffer = substr($buffer, 4);
@@ -117,27 +116,32 @@ class AsiExtraField implements ZipExtraField
throw new Crc32Exception('Asi Unix Extra Filed Data', $givenChecksum, $realChecksum);
}
$data = unpack('vmode/VlinkSize/vuid/vgid', $buffer);
[
'mode' => $mode,
'linkSize' => $linkSize,
'uid' => $uid,
'gid' => $gid,
] = unpack('vmode/VlinkSize/vuid/vgid', $buffer);
$link = '';
if ($data['linkSize'] > 0) {
$link = substr($buffer, 8);
if ($linkSize > 0) {
$link = substr($buffer, 10);
}
return new self($data['mode'], $data['uid'], $data['gid'], $link);
return new self($mode, $uid, $gid, $link);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws Crc32Exception
*
* @return AsiExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
@@ -148,7 +152,7 @@ class AsiExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
$data = pack(
'vVvv',
@@ -167,7 +171,7 @@ class AsiExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return $this->packLocalFileData();
}
@@ -178,7 +182,7 @@ class AsiExtraField implements ZipExtraField
* @return string name of the file this entry links to if it is a
* symbolic link, the empty string otherwise
*/
public function getLink()
public function getLink(): string
{
return $this->link;
}
@@ -189,7 +193,7 @@ class AsiExtraField implements ZipExtraField
* @param string $link name of the file this entry links to, empty
* string if it is not a symbolic link
*/
public function setLink($link)
public function setLink(string $link): void
{
$this->link = $link;
$this->mode = $this->getPermissionsMode($this->mode);
@@ -200,7 +204,7 @@ class AsiExtraField implements ZipExtraField
*
* @return bool true if this is a symbolic link
*/
public function isLink()
public function isLink(): bool
{
return !empty($this->link);
}
@@ -212,13 +216,15 @@ class AsiExtraField implements ZipExtraField
*
* @return int the type with the mode
*/
protected function getPermissionsMode($mode)
private function getPermissionsMode(int $mode): int
{
$type = UnixStat::UNX_IFMT;
$type = 0;
if ($this->isLink()) {
$type = UnixStat::UNX_IFLNK;
} elseif ($this->isDirectory()) {
} elseif (($mode & UnixStat::UNX_IFREG) !== 0) {
$type = UnixStat::UNX_IFREG;
} elseif (($mode & UnixStat::UNX_IFDIR) !== 0) {
$type = UnixStat::UNX_IFDIR;
}
@@ -230,63 +236,42 @@ class AsiExtraField implements ZipExtraField
*
* @return bool true if this entry is a directory
*/
public function isDirectory()
public function isDirectory(): bool
{
return ($this->mode & UnixStat::UNX_IFDIR) !== 0 && !$this->isLink();
}
/**
* @return int
*/
public function getMode()
public function getMode(): int
{
return $this->mode;
}
/**
* @param int $mode
*/
public function setMode($mode)
public function setMode(int $mode): void
{
$this->mode = $this->getPermissionsMode($mode);
}
/**
* @return int
*/
public function getUserId()
public function getUserId(): int
{
return $this->uid;
}
/**
* @param int $uid
*/
public function setUserId($uid)
public function setUserId(int $uid): void
{
$this->uid = $uid;
}
/**
* @return int
*/
public function getGroupId()
public function getGroupId(): int
{
return $this->gid;
}
/**
* @param int $gid
*/
public function setGroupId($gid)
public function setGroupId(int $gid): void
{
$this->gid = $gid;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return sprintf(
'0x%04x ASI: Mode=%o UID=%d GID=%d Link="%s',

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Model\Extra\ZipExtraField;
@@ -64,81 +73,72 @@ use PhpZip\Model\ZipEntry;
*
* @see ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip Info-ZIP version Specification
*/
class ExtendedTimestampExtraField implements ZipExtraField
final class ExtendedTimestampExtraField implements ZipExtraField
{
/** @var int Header id */
const HEADER_ID = 0x5455;
public const HEADER_ID = 0x5455;
/**
* @var int the bit set inside the flags by when the last modification time
* is present in this extra field
*/
const MODIFY_TIME_BIT = 1;
public const MODIFY_TIME_BIT = 1;
/**
* @var int the bit set inside the flags by when the last access time is
* present in this extra field
*/
const ACCESS_TIME_BIT = 2;
public const ACCESS_TIME_BIT = 2;
/**
* @var int the bit set inside the flags by when the original creation time
* is present in this extra field
*/
const CREATE_TIME_BIT = 4;
public const CREATE_TIME_BIT = 4;
/**
* @var int The 3 boolean fields (below) come from this flags byte. The remaining 5 bits
* are ignored according to the current version of the spec (December 2012).
*/
private $flags;
private int $flags;
/** @var int|null Modify time */
private $modifyTime;
private ?int $modifyTime;
/** @var int|null Access time */
private $accessTime;
private ?int $accessTime;
/** @var int|null Create time */
private $createTime;
private ?int $createTime;
/**
* @param int $flags
* @param int|null $modifyTime
* @param int|null $accessTime
* @param int|null $createTime
*/
public function __construct($flags, $modifyTime, $accessTime, $createTime)
public function __construct(int $flags, ?int $modifyTime, ?int $accessTime, ?int $createTime)
{
$this->flags = (int) $flags;
$this->flags = $flags;
$this->modifyTime = $modifyTime;
$this->accessTime = $accessTime;
$this->createTime = $createTime;
}
/**
* @param int|null $modifyTime
* @param int|null $accessTime
* @param int|null $createTime
* @param ?int $modifyTime
* @param ?int $accessTime
* @param ?int $createTime
*
* @return ExtendedTimestampExtraField
*/
public static function create($modifyTime, $accessTime, $createTime)
public static function create(?int $modifyTime, ?int $accessTime, ?int $createTime): self
{
$flags = 0;
if ($modifyTime !== null) {
$modifyTime = (int) $modifyTime;
$flags |= self::MODIFY_TIME_BIT;
}
if ($accessTime !== null) {
$accessTime = (int) $accessTime;
$flags |= self::ACCESS_TIME_BIT;
}
if ($createTime !== null) {
$createTime = (int) $createTime;
$flags |= self::CREATE_TIME_BIT;
}
@@ -149,10 +149,8 @@ class ExtendedTimestampExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -161,11 +159,11 @@ class ExtendedTimestampExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return ExtendedTimestampExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$length = \strlen($buffer);
$flags = unpack('C', $buffer)[1];
@@ -198,11 +196,11 @@ class ExtendedTimestampExtraField implements ZipExtraField
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return ExtendedTimestampExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
@@ -213,7 +211,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
$data = '';
@@ -241,7 +239,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
$cdLength = 1 + ($this->modifyTime !== null ? 4 : 0);
@@ -263,7 +261,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
* @return int flags byte indicating which of the
* three datestamp fields are present
*/
public function getFlags()
public function getFlags(): int
{
return $this->flags;
}
@@ -274,7 +272,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return int|null modify time (seconds since epoch) or null
*/
public function getModifyTime()
public function getModifyTime(): ?int
{
return $this->modifyTime;
}
@@ -285,7 +283,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return int|null access time (seconds since epoch) or null
*/
public function getAccessTime()
public function getAccessTime(): ?int
{
return $this->accessTime;
}
@@ -301,7 +299,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return int|null create time (seconds since epoch) or null
*/
public function getCreateTime()
public function getCreateTime(): ?int
{
return $this->createTime;
}
@@ -314,7 +312,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return \DateTimeInterface|null modify time as \DateTimeInterface or null
*/
public function getModifyDateTime()
public function getModifyDateTime(): ?\DateTimeInterface
{
return self::timestampToDateTime($this->modifyTime);
}
@@ -327,7 +325,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return \DateTimeInterface|null access time as \DateTimeInterface or null
*/
public function getAccessDateTime()
public function getAccessDateTime(): ?\DateTimeInterface
{
return self::timestampToDateTime($this->accessTime);
}
@@ -345,7 +343,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @return \DateTimeInterface|null create time as \DateTimeInterface or null
*/
public function getCreateDateTime()
public function getCreateDateTime(): ?\DateTimeInterface
{
return self::timestampToDateTime($this->createTime);
}
@@ -356,9 +354,28 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @param int|null $unixTime unix time of the modify time (seconds per epoch) or null
*/
public function setModifyTime($unixTime)
public function setModifyTime(?int $unixTime): void
{
$this->modifyTime = $unixTime;
$this->updateFlags();
}
private function updateFlags(): void
{
$flags = 0;
if ($this->modifyTime !== null) {
$flags |= self::MODIFY_TIME_BIT;
}
if ($this->accessTime !== null) {
$flags |= self::ACCESS_TIME_BIT;
}
if ($this->createTime !== null) {
$flags |= self::CREATE_TIME_BIT;
}
$this->flags = $flags;
}
/**
@@ -367,9 +384,10 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @param int|null $unixTime Unix time of the access time (seconds per epoch) or null
*/
public function setAccessTime($unixTime)
public function setAccessTime(?int $unixTime): void
{
$this->accessTime = $unixTime;
$this->updateFlags();
}
/**
@@ -378,17 +396,13 @@ class ExtendedTimestampExtraField implements ZipExtraField
*
* @param int|null $unixTime Unix time of the create time (seconds per epoch) or null
*/
public function setCreateTime($unixTime)
public function setCreateTime(?int $unixTime): void
{
$this->createTime = $unixTime;
$this->updateFlags();
}
/**
* @param int|null $timestamp
*
* @return \DateTimeInterface|null
*/
private static function timestampToDateTime($timestamp)
private static function timestampToDateTime(?int $timestamp): ?\DateTimeInterface
{
try {
return $timestamp !== null ? new \DateTimeImmutable('@' . $timestamp) : null;
@@ -397,10 +411,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
}
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
$args = [self::HEADER_ID];
$format = '0x%04x ExtendedTimestamp:';

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -14,18 +23,13 @@ use PhpZip\Model\ZipEntry;
* by the extra field in the first file, which is hexadecimal in the 0xCAFE bytes series.
* If this extra field is added as the very first extra field of
* the archive, Solaris will consider it an executable jar file.
*
* @license MIT
*/
class JarMarkerExtraField implements ZipExtraField
final class JarMarkerExtraField implements ZipExtraField
{
/** @var int Header id. */
const HEADER_ID = 0xCAFE;
public const HEADER_ID = 0xCAFE;
/**
* @param ZipContainer $container
*/
public static function setJarMarker(ZipContainer $container)
public static function setJarMarker(ZipContainer $container): void
{
$zipEntries = $container->getEntries();
@@ -44,10 +48,8 @@ class JarMarkerExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -58,7 +60,7 @@ class JarMarkerExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
return '';
}
@@ -69,7 +71,7 @@ class JarMarkerExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return '';
}
@@ -78,13 +80,13 @@ class JarMarkerExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException on error
*
* @return JarMarkerExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
if (!empty($buffer)) {
throw new ZipException("JarMarker doesn't expect any data");
@@ -97,21 +99,18 @@ class JarMarkerExtraField implements ZipExtraField
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException on error
*
* @return JarMarkerExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return sprintf('0x%04x Jar Marker', self::HEADER_ID);
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -42,45 +51,36 @@ use PhpZip\Model\ZipEntry;
* and this extra field are present, the values in this extra field
* supercede the values in that extra field.
*/
class NewUnixExtraField implements ZipExtraField
final class NewUnixExtraField implements ZipExtraField
{
/** @var int header id */
const HEADER_ID = 0x7875;
public const HEADER_ID = 0x7875;
/** ID of the first non-root user created on a unix system. */
const USER_GID_PID = 1000;
public const USER_GID_PID = 1000;
/** @var int version of this extra field, currently 1 */
private $version = 1;
private int $version;
/** @var int User id */
private $uid;
private int $uid;
/** @var int Group id */
private $gid;
private int $gid;
/**
* NewUnixExtraField constructor.
*
* @param int $version
* @param int $uid
* @param int $gid
*/
public function __construct($version = 1, $uid = self::USER_GID_PID, $gid = self::USER_GID_PID)
public function __construct(int $version = 1, int $uid = self::USER_GID_PID, int $gid = self::USER_GID_PID)
{
$this->version = (int) $version;
$this->uid = (int) $uid;
$this->gid = (int) $gid;
$this->version = $version;
$this->uid = $uid;
$this->gid = $gid;
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -89,13 +89,13 @@ class NewUnixExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException
*
* @return NewUnixExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$length = \strlen($buffer);
@@ -103,48 +103,31 @@ class NewUnixExtraField implements ZipExtraField
throw new ZipException(sprintf('X7875_NewUnix length is too short, only %s bytes', $length));
}
$offset = 0;
$data = unpack('Cversion/CuidSize', $buffer);
[
'version' => $version,
'uidSize' => $uidSize,
] = unpack('Cversion/CuidSize', $buffer);
$offset += 2;
$uidSize = $data['uidSize'];
$gid = self::readSizeIntegerLE(substr($buffer, $offset, $uidSize), $uidSize);
$offset += $uidSize;
$gidSize = unpack('C', $buffer[$offset])[1];
$offset++;
$uid = self::readSizeIntegerLE(substr($buffer, $offset, $gidSize), $gidSize);
return new self($data['version'], $gid, $uid);
}
/**
* Converts a signed byte into an unsigned integer representation
* (e.g., -1 becomes 255).
*
* @param int $b byte to convert to int
*
* @return int representation of the provided byte
*
* @since 1.5
*/
public static function signedByteToUnsignedInt($b)
{
if ($b >= 0) {
return $b;
}
return 256 + $b;
return new self($version, $gid, $uid);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException
*
* @return NewUnixExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
@@ -155,15 +138,15 @@ class NewUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
return pack(
'CCVCV',
$this->version,
4, // GIDSize
$this->gid,
4, // UIDSize
$this->uid
$this->uid,
4, // GIDSize
$this->gid
);
}
@@ -173,20 +156,15 @@ class NewUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return $this->packLocalFileData();
}
/**
* @param string $data
* @param int $size
*
* @throws ZipException
*
* @return int
*/
private static function readSizeIntegerLE($data, $size)
private static function readSizeIntegerLE(string $data, int $size): int
{
$format = [
1 => 'C', // unsigned byte
@@ -201,42 +179,32 @@ class NewUnixExtraField implements ZipExtraField
return unpack($format[$size], $data)[1];
}
/**
* @return int
*/
public function getUid()
public function getUid(): int
{
return $this->uid;
}
/**
* @param int $uid
*/
public function setUid($uid)
public function setUid(int $uid): void
{
$this->uid = $uid & 0xffffffff;
$this->uid = $uid & 0xFFFFFFFF;
}
/**
* @return int
*/
public function getGid()
public function getGid(): int
{
return $this->gid;
}
/**
* @param int $gid
*/
public function setGid($gid)
public function setGid(int $gid): void
{
$this->gid = $gid & 0xffffffff;
$this->gid = $gid & 0xFFFFFFFF;
}
/**
* @return string
*/
public function __toString()
public function getVersion(): int
{
return $this->version;
}
public function __toString(): string
{
return sprintf(
'0x%04x NewUnix: UID=%d GID=%d',

View File

@@ -1,29 +1,36 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/**
* NTFS Extra Field.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*
* @license MIT
*/
class NtfsExtraField implements ZipExtraField
final class NtfsExtraField implements ZipExtraField
{
/** @var int Header id */
const HEADER_ID = 0x000a;
public const HEADER_ID = 0x000A;
/** @var int Tag ID */
const TIME_ATTR_TAG = 0x0001;
public const TIME_ATTR_TAG = 0x0001;
/** @var int Attribute size */
const TIME_ATTR_SIZE = 24; // 3 * 8
public const TIME_ATTR_SIZE = 24; // 3 * 8
/**
* @var int A file time is a 64-bit value that represents the number of
@@ -31,42 +38,36 @@ class NtfsExtraField implements ZipExtraField
* A.M. January 1, 1601 Coordinated Universal Time (UTC).
* this is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
*/
const EPOCH_OFFSET = -11644473600;
public const EPOCH_OFFSET = -116444736000000000;
/** @var int Modify ntfs time */
private $modifyTime;
private int $modifyNtfsTime;
/** @var int Access ntfs time */
private $accessTime;
private int $accessNtfsTime;
/** @var int Create ntfs time */
private $createTime;
private int $createNtfsTime;
/**
* @param int $modifyTime
* @param int $accessTime
* @param int $createTime
*/
public function __construct($modifyTime, $accessTime, $createTime)
public function __construct(int $modifyNtfsTime, int $accessNtfsTime, int $createNtfsTime)
{
$this->modifyTime = (int) $modifyTime;
$this->accessTime = (int) $accessTime;
$this->createTime = (int) $createTime;
$this->modifyNtfsTime = $modifyNtfsTime;
$this->accessNtfsTime = $accessNtfsTime;
$this->createNtfsTime = $createNtfsTime;
}
/**
* @param \DateTimeInterface $mtime
* @param \DateTimeInterface $atime
* @param \DateTimeInterface $ctime
*
* @return NtfsExtraField
*/
public static function create(\DateTimeInterface $mtime, \DateTimeInterface $atime, \DateTimeInterface $ctime)
{
public static function create(
\DateTimeInterface $modifyDateTime,
\DateTimeInterface $accessDateTime,
\DateTimeInterface $createNtfsTime
): self {
return new self(
self::dateTimeToNtfsTime($mtime),
self::dateTimeToNtfsTime($atime),
self::dateTimeToNtfsTime($ctime)
self::dateTimeToNtfsTime($modifyDateTime),
self::dateTimeToNtfsTime($accessDateTime),
self::dateTimeToNtfsTime($createNtfsTime)
);
}
@@ -74,10 +75,8 @@ class NtfsExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -86,12 +85,18 @@ class NtfsExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
if (\PHP_INT_SIZE === 4) {
throw new ZipException('not supported for php-32bit');
}
$buffer = substr($buffer, 4);
$modifyTime = 0;
@@ -99,17 +104,21 @@ class NtfsExtraField implements ZipExtraField
$createTime = 0;
while ($buffer || $buffer !== '') {
$unpack = unpack('vtag/vsizeAttr', $buffer);
[
'tag' => $tag,
'sizeAttr' => $sizeAttr,
] = unpack('vtag/vsizeAttr', $buffer);
if ($unpack['tag'] === self::TIME_ATTR_TAG && $unpack['sizeAttr'] === self::TIME_ATTR_SIZE) {
// refactoring will be needed when php 5.5 support ends
$modifyTime = PackUtil::unpackLongLE(substr($buffer, 4, 8));
$accessTime = PackUtil::unpackLongLE(substr($buffer, 12, 8));
$createTime = PackUtil::unpackLongLE(substr($buffer, 20, 8));
if ($tag === self::TIME_ATTR_TAG && $sizeAttr === self::TIME_ATTR_SIZE) {
[
'modifyTime' => $modifyTime,
'accessTime' => $accessTime,
'createTime' => $createTime,
] = unpack('PmodifyTime/PaccessTime/PcreateTime', substr($buffer, 4, 24));
break;
}
$buffer = substr($buffer, 4 + $unpack['sizeAttr']);
$buffer = substr($buffer, 4 + $sizeAttr);
}
return new self($modifyTime, $accessTime, $createTime);
@@ -119,11 +128,13 @@ class NtfsExtraField implements ZipExtraField
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
@@ -134,15 +145,47 @@ class NtfsExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
$data = pack('Vvv', 0, self::TIME_ATTR_TAG, self::TIME_ATTR_SIZE);
// refactoring will be needed when php 5.5 support ends
$data .= PackUtil::packLongLE($this->modifyTime);
$data .= PackUtil::packLongLE($this->accessTime);
$data .= PackUtil::packLongLE($this->createTime);
return pack(
'VvvPPP',
0,
self::TIME_ATTR_TAG,
self::TIME_ATTR_SIZE,
$this->modifyNtfsTime,
$this->accessNtfsTime,
$this->createNtfsTime
);
}
return $data;
public function getModifyNtfsTime(): int
{
return $this->modifyNtfsTime;
}
public function setModifyNtfsTime(int $modifyNtfsTime): void
{
$this->modifyNtfsTime = $modifyNtfsTime;
}
public function getAccessNtfsTime(): int
{
return $this->accessNtfsTime;
}
public function setAccessNtfsTime(int $accessNtfsTime): void
{
$this->accessNtfsTime = $accessNtfsTime;
}
public function getCreateNtfsTime(): int
{
return $this->createNtfsTime;
}
public function setCreateNtfsTime(int $createNtfsTime): void
{
$this->createNtfsTime = $createNtfsTime;
}
/**
@@ -151,104 +194,90 @@ class NtfsExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return $this->packLocalFileData();
}
/**
* @return \DateTimeInterface
*/
public function getModifyDateTime()
public function getModifyDateTime(): \DateTimeInterface
{
return self::ntfsTimeToDateTime($this->modifyTime);
return self::ntfsTimeToDateTime($this->modifyNtfsTime);
}
public function setModifyDateTime(\DateTimeInterface $modifyTime): void
{
$this->modifyNtfsTime = self::dateTimeToNtfsTime($modifyTime);
}
public function getAccessDateTime(): \DateTimeInterface
{
return self::ntfsTimeToDateTime($this->accessNtfsTime);
}
public function setAccessDateTime(\DateTimeInterface $accessTime): void
{
$this->accessNtfsTime = self::dateTimeToNtfsTime($accessTime);
}
public function getCreateDateTime(): \DateTimeInterface
{
return self::ntfsTimeToDateTime($this->createNtfsTime);
}
public function setCreateDateTime(\DateTimeInterface $createTime): void
{
$this->createNtfsTime = self::dateTimeToNtfsTime($createTime);
}
/**
* @param \DateTimeInterface $modifyTime
* @param float $timestamp Float timestamp
*/
public function setModifyDateTime(\DateTimeInterface $modifyTime)
public static function timestampToNtfsTime(float $timestamp): int
{
$this->modifyTime = self::dateTimeToNtfsTime($modifyTime);
return (int) (($timestamp * 10000000) - self::EPOCH_OFFSET);
}
public static function dateTimeToNtfsTime(\DateTimeInterface $dateTime): int
{
return self::timestampToNtfsTime((float) $dateTime->format('U.u'));
}
/**
* @return \DateTimeInterface
* @return float Float unix timestamp
*/
public function getAccessDateTime()
public static function ntfsTimeToTimestamp(int $ntfsTime): float
{
return self::ntfsTimeToDateTime($this->accessTime);
return (float) (($ntfsTime + self::EPOCH_OFFSET) / 10000000);
}
/**
* @param \DateTimeInterface $accessTime
*/
public function setAccessDateTime(\DateTimeInterface $accessTime)
public static function ntfsTimeToDateTime(int $ntfsTime): \DateTimeInterface
{
$this->accessTime = self::dateTimeToNtfsTime($accessTime);
}
$timestamp = self::ntfsTimeToTimestamp($ntfsTime);
$dateTime = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6f', $timestamp));
/**
* @return \DateTimeInterface
*/
public function getCreateDateTime()
{
return self::ntfsTimeToDateTime($this->createTime);
}
/**
* @param \DateTimeInterface $createTime
*/
public function setCreateDateTime(\DateTimeInterface $createTime)
{
$this->createTime = self::dateTimeToNtfsTime($createTime);
}
/**
* @param \DateTimeInterface $dateTime
*
* @return int
*/
public static function dateTimeToNtfsTime(\DateTimeInterface $dateTime)
{
return $dateTime->getTimestamp() * 10000000 + self::EPOCH_OFFSET;
}
/**
* @param int $time
*
* @return \DateTimeInterface
*/
public static function ntfsTimeToDateTime($time)
{
$timestamp = (int) ($time / 10000000 + self::EPOCH_OFFSET);
try {
return new \DateTimeImmutable('@' . $timestamp);
} catch (\Exception $e) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp, 1, $e);
if ($dateTime === false) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp);
}
return $dateTime;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
$args = [self::HEADER_ID];
$format = '0x%04x NtfsExtra:';
if ($this->modifyTime !== 0) {
if ($this->modifyNtfsTime !== 0) {
$format .= ' Modify:[%s]';
$args[] = $this->getModifyDateTime()->format(\DATE_ATOM);
}
if ($this->accessTime !== 0) {
if ($this->accessNtfsTime !== 0) {
$format .= ' Access:[%s]';
$args[] = $this->getAccessDateTime()->format(\DATE_ATOM);
}
if ($this->createTime !== 0) {
if ($this->createNtfsTime !== 0) {
$format .= ' Create:[%s]';
$args[] = $this->getCreateDateTime()->format(\DATE_ATOM);
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Model\Extra\ZipExtraField;
@@ -53,30 +62,24 @@ use PhpZip\Model\ZipEntry;
* mid-1994. Therefore future archiving software should continue to
* support it.
*/
class OldUnixExtraField implements ZipExtraField
final class OldUnixExtraField implements ZipExtraField
{
/** @var int Header id */
const HEADER_ID = 0x5855;
public const HEADER_ID = 0x5855;
/** @var int|null Access timestamp */
private $accessTime;
private ?int $accessTime;
/** @var int|null Modify timestamp */
private $modifyTime;
private ?int $modifyTime;
/** @var int|null User id */
private $uid;
private ?int $uid;
/** @var int|null Group id */
private $gid;
private ?int $gid;
/**
* @param int|null $accessTime
* @param int|null $modifyTime
* @param int|null $uid
* @param int|null $gid
*/
public function __construct($accessTime, $modifyTime, $uid, $gid)
public function __construct(?int $accessTime, ?int $modifyTime, ?int $uid, ?int $gid)
{
$this->accessTime = $accessTime;
$this->modifyTime = $modifyTime;
@@ -88,10 +91,8 @@ class OldUnixExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -100,11 +101,11 @@ class OldUnixExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return OldUnixExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$length = \strlen($buffer);
@@ -133,11 +134,11 @@ class OldUnixExtraField implements ZipExtraField
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return OldUnixExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
$length = \strlen($buffer);
@@ -160,7 +161,7 @@ class OldUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
$data = '';
@@ -189,7 +190,7 @@ class OldUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
$data = '';
@@ -204,100 +205,67 @@ class OldUnixExtraField implements ZipExtraField
return $data;
}
/**
* @return int|null
*/
public function getAccessTime()
public function getAccessTime(): ?int
{
return $this->accessTime;
}
/**
* @param int|null $accessTime
*/
public function setAccessTime($accessTime)
public function setAccessTime(?int $accessTime): void
{
$this->accessTime = $accessTime;
}
/**
* @return \DateTimeInterface|null
*/
public function getAccessDateTime()
public function getAccessDateTime(): ?\DateTimeInterface
{
try {
return $this->accessTime === null ? null :
new \DateTimeImmutable('@' . $this->accessTime);
return $this->accessTime === null ? null
: new \DateTimeImmutable('@' . $this->accessTime);
} catch (\Exception $e) {
return null;
}
}
/**
* @return int|null
*/
public function getModifyTime()
public function getModifyTime(): ?int
{
return $this->modifyTime;
}
/**
* @param int|null $modifyTime
*/
public function setModifyTime($modifyTime)
public function setModifyTime(?int $modifyTime): void
{
$this->modifyTime = $modifyTime;
}
/**
* @return \DateTimeInterface|null
*/
public function getModifyDateTime()
public function getModifyDateTime(): ?\DateTimeInterface
{
try {
return $this->modifyTime === null ? null :
new \DateTimeImmutable('@' . $this->modifyTime);
return $this->modifyTime === null ? null
: new \DateTimeImmutable('@' . $this->modifyTime);
} catch (\Exception $e) {
return null;
}
}
/**
* @return int|null
*/
public function getUid()
public function getUid(): ?int
{
return $this->uid;
}
/**
* @param int|null $uid
*/
public function setUid($uid)
public function setUid(?int $uid): void
{
$this->uid = $uid;
}
/**
* @return int|null
*/
public function getGid()
public function getGid(): ?int
{
return $this->gid;
}
/**
* @param int|null $gid
*/
public function setGid($gid)
public function setGid(?int $gid): void
{
$this->gid = $gid;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
$args = [self::HEADER_ID];
$format = '0x%04x OldUnix:';

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
/**
@@ -46,26 +55,21 @@ namespace PhpZip\Model\Extra\Fields;
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT section 4.6.8
*/
class UnicodeCommentExtraField extends AbstractUnicodeExtraField
final class UnicodeCommentExtraField extends AbstractUnicodeExtraField
{
const HEADER_ID = 0x6375;
public const HEADER_ID = 0x6375;
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return sprintf(
'0x%04x UnicodeComment: "%s"',

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
/**
@@ -47,26 +56,21 @@ namespace PhpZip\Model\Extra\Fields;
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT section 4.6.9
*/
class UnicodePathExtraField extends AbstractUnicodeExtraField
final class UnicodePathExtraField extends AbstractUnicodeExtraField
{
const HEADER_ID = 0x7075;
public const HEADER_ID = 0x7075;
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return sprintf(
'0x%04x UnicodePath: "%s"',

View File

@@ -1,37 +1,37 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\RuntimeException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
/**
* Simple placeholder for all those extra fields we don't want to deal with.
*/
class UnrecognizedExtraField implements ZipExtraField
final class UnrecognizedExtraField implements ZipExtraField
{
/** @var int */
private $headerId;
private int $headerId;
/** @var string extra field data without Header-ID or length specifier */
private $data;
private string $data;
/**
* UnrecognizedExtraField constructor.
*
* @param int $headerId
* @param string $data
*/
public function __construct($headerId, $data)
public function __construct(int $headerId, string $data)
{
$this->headerId = (int) $headerId;
$this->data = (string) $data;
$this->headerId = $headerId;
$this->data = $data;
}
/**
* @param int $headerId
*/
public function setHeaderId($headerId)
public function setHeaderId(int $headerId): void
{
$this->headerId = $headerId;
}
@@ -40,10 +40,8 @@ class UnrecognizedExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return $this->headerId;
}
@@ -52,60 +50,55 @@ class UnrecognizedExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return UnrecognizedExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
throw new \RuntimeException('Unsupport parse');
throw new RuntimeException('Unsupport parse');
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return UnrecognizedExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
throw new \RuntimeException('Unsupport parse');
throw new RuntimeException('Unsupport parse');
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
return $this->data;
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return $this->data;
}
/**
* @return string
*/
public function getData()
public function getData(): string
{
return $this->data;
}
/**
* @param string $data
*/
public function setData($data)
public function setData(string $data): void
{
$this->data = (string) $data;
$this->data = $data;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
$args = [$this->headerId, $this->data];
$format = '0x%04x Unrecognized Extra Field: "%s"';

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Constants\ZipCompressionMethod;
@@ -15,72 +24,72 @@ use PhpZip\Model\ZipEntry;
*
* @see http://www.winzip.com/win/en/aes_tips.htm AES Coding Tips for Developers
*/
class WinZipAesExtraField implements ZipExtraField
final class WinZipAesExtraField implements ZipExtraField
{
/** @var int Header id */
const HEADER_ID = 0x9901;
public const HEADER_ID = 0x9901;
/**
* @var int Data size (currently 7, but subject to possible increase
* in the future)
*/
const DATA_SIZE = 7;
public const DATA_SIZE = 7;
/**
* @var int The vendor ID field should always be set to the two ASCII
* characters "AE"
*/
const VENDOR_ID = 0x4541; // 'A' | ('E' << 8)
public const VENDOR_ID = 0x4541; // 'A' | ('E' << 8)
/**
* @var int Entries of this type do include the standard ZIP CRC-32 value.
* For use with {@see WinZipAesExtraField::setVendorVersion()}.
*/
const VERSION_AE1 = 1;
public const VERSION_AE1 = 1;
/**
* @var int Entries of this type do not include the standard ZIP CRC-32 value.
* For use with {@see WinZipAesExtraField::setVendorVersion().
*/
const VERSION_AE2 = 2;
public const VERSION_AE2 = 2;
/** @var int integer mode value indicating AES encryption 128-bit strength */
const KEY_STRENGTH_128BIT = 0x01;
public const KEY_STRENGTH_128BIT = 0x01;
/** @var int integer mode value indicating AES encryption 192-bit strength */
const KEY_STRENGTH_192BIT = 0x02;
public const KEY_STRENGTH_192BIT = 0x02;
/** @var int integer mode value indicating AES encryption 256-bit strength */
const KEY_STRENGTH_256BIT = 0x03;
public const KEY_STRENGTH_256BIT = 0x03;
/** @var int[] */
private static $allowVendorVersions = [
private const ALLOW_VENDOR_VERSIONS = [
self::VERSION_AE1,
self::VERSION_AE2,
];
/** @var array<int, int> */
private static $encryptionStrengths = [
private const ENCRYPTION_STRENGTHS = [
self::KEY_STRENGTH_128BIT => 128,
self::KEY_STRENGTH_192BIT => 192,
self::KEY_STRENGTH_256BIT => 256,
];
/** @var array<int, int> */
private static $MAP_KEY_STRENGTH_METHODS = [
private const MAP_KEY_STRENGTH_METHODS = [
self::KEY_STRENGTH_128BIT => ZipEncryptionMethod::WINZIP_AES_128,
self::KEY_STRENGTH_192BIT => ZipEncryptionMethod::WINZIP_AES_192,
self::KEY_STRENGTH_256BIT => ZipEncryptionMethod::WINZIP_AES_256,
];
/** @var int Integer version number specific to the zip vendor */
private $vendorVersion = self::VERSION_AE1;
private int $vendorVersion = self::VERSION_AE1;
/** @var int Integer mode value indicating AES encryption strength */
private $keyStrength = self::KEY_STRENGTH_256BIT;
private int $keyStrength = self::KEY_STRENGTH_256BIT;
/** @var int The actual compression method used to compress the file */
private $compressionMethod;
private int $compressionMethod;
/**
* @param int $vendorVersion Integer version number specific to the zip vendor
@@ -89,7 +98,7 @@ class WinZipAesExtraField implements ZipExtraField
*
* @throws ZipUnsupportMethodException
*/
public function __construct($vendorVersion, $keyStrength, $compressionMethod)
public function __construct(int $vendorVersion, int $keyStrength, int $compressionMethod)
{
$this->setVendorVersion($vendorVersion);
$this->setKeyStrength($keyStrength);
@@ -97,15 +106,13 @@ class WinZipAesExtraField implements ZipExtraField
}
/**
* @param ZipEntry $entry
*
* @throws ZipUnsupportMethodException
*
* @return WinZipAesExtraField
*/
public static function create(ZipEntry $entry)
public static function create(ZipEntry $entry): self
{
$keyStrength = array_search($entry->getEncryptionMethod(), self::$MAP_KEY_STRENGTH_METHODS, true);
$keyStrength = array_search($entry->getEncryptionMethod(), self::MAP_KEY_STRENGTH_METHODS, true);
if ($keyStrength === false) {
throw new InvalidArgumentException('Not support encryption method ' . $entry->getEncryptionMethod());
@@ -118,11 +125,11 @@ class WinZipAesExtraField implements ZipExtraField
//
// https://www.winzip.com/win/en/aes_info.html
$vendorVersion = (
$entry->getUncompressedSize() < 20 ||
$entry->getCompressionMethod() === ZipCompressionMethod::BZIP2
) ?
self::VERSION_AE2 :
self::VERSION_AE1;
$entry->getUncompressedSize() < 20
|| $entry->getCompressionMethod() === ZipCompressionMethod::BZIP2
)
? self::VERSION_AE2
: self::VERSION_AE1;
$field = new self($vendorVersion, $keyStrength, $entry->getCompressionMethod());
@@ -136,10 +143,8 @@ class WinZipAesExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -147,14 +152,14 @@ class WinZipAesExtraField implements ZipExtraField
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
*
* @throws ZipException on error
*
* @return WinZipAesExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$size = \strlen($buffer);
@@ -168,36 +173,37 @@ class WinZipAesExtraField implements ZipExtraField
);
}
$data = unpack('vvendorVersion/vvendorId/ckeyStrength/vcompressionMethod', $buffer);
[
'vendorVersion' => $vendorVersion,
'vendorId' => $vendorId,
'keyStrength' => $keyStrength,
'compressionMethod' => $compressionMethod,
] = unpack('vvendorVersion/vvendorId/ckeyStrength/vcompressionMethod', $buffer);
if ($data['vendorId'] !== self::VENDOR_ID) {
if ($vendorId !== self::VENDOR_ID) {
throw new ZipException(
sprintf(
'Vendor id invalid: %d. Must be %d',
$data['vendorId'],
$vendorId,
self::VENDOR_ID
)
);
}
return new self(
$data['vendorVersion'],
$data['keyStrength'],
$data['compressionMethod']
);
return new self($vendorVersion, $keyStrength, $compressionMethod);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
*
* @throws ZipException
*
* @return WinZipAesExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
@@ -208,7 +214,7 @@ class WinZipAesExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
return pack(
'vvcv',
@@ -225,7 +231,7 @@ class WinZipAesExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
return $this->packLocalFileData();
}
@@ -233,12 +239,10 @@ class WinZipAesExtraField implements ZipExtraField
/**
* Returns the vendor version.
*
* @return int
*
* @see WinZipAesExtraField::VERSION_AE2
* @see WinZipAesExtraField::VERSION_AE1
*/
public function getVendorVersion()
public function getVendorVersion(): int
{
return $this->vendorVersion;
}
@@ -251,12 +255,10 @@ class WinZipAesExtraField implements ZipExtraField
* @see WinZipAesExtraField::VERSION_AE2
* @see WinZipAesExtraField::VERSION_AE1
*/
public function setVendorVersion($vendorVersion)
public function setVendorVersion(int $vendorVersion): void
{
$vendorVersion = (int) $vendorVersion;
if (!\in_array($vendorVersion, self::$allowVendorVersions, true)) {
throw new \InvalidArgumentException(
if (!\in_array($vendorVersion, self::ALLOW_VENDOR_VERSIONS, true)) {
throw new InvalidArgumentException(
sprintf(
'Unsupport WinZip AES vendor version: %d',
$vendorVersion
@@ -268,113 +270,80 @@ class WinZipAesExtraField implements ZipExtraField
/**
* Returns vendor id.
*
* @return int
*/
public function getVendorId()
public function getVendorId(): int
{
return self::VENDOR_ID;
}
/**
* @return int
*/
public function getKeyStrength()
public function getKeyStrength(): int
{
return $this->keyStrength;
}
/**
* Set key strength.
*
* @param int $keyStrength
*/
public function setKeyStrength($keyStrength)
public function setKeyStrength(int $keyStrength): void
{
$keyStrength = (int) $keyStrength;
if (!isset(self::$encryptionStrengths[$keyStrength])) {
throw new \InvalidArgumentException(
if (!isset(self::ENCRYPTION_STRENGTHS[$keyStrength])) {
throw new InvalidArgumentException(
sprintf(
'Key strength %d not support value. Allow values: %s',
$keyStrength,
implode(', ', array_keys(self::$encryptionStrengths))
implode(', ', array_keys(self::ENCRYPTION_STRENGTHS))
)
);
}
$this->keyStrength = $keyStrength;
}
/**
* @return int
*/
public function getCompressionMethod()
public function getCompressionMethod(): int
{
return $this->compressionMethod;
}
/**
* @param int $compressionMethod
*
* @throws ZipUnsupportMethodException
*/
public function setCompressionMethod($compressionMethod)
public function setCompressionMethod(int $compressionMethod): void
{
$compressionMethod = (int) $compressionMethod;
ZipCompressionMethod::checkSupport($compressionMethod);
$this->compressionMethod = $compressionMethod;
}
/**
* @return int
*/
public function getEncryptionStrength()
public function getEncryptionStrength(): int
{
return self::$encryptionStrengths[$this->keyStrength];
return self::ENCRYPTION_STRENGTHS[$this->keyStrength];
}
/**
* @return int
*/
public function getEncryptionMethod()
public function getEncryptionMethod(): int
{
$keyStrength = $this->getKeyStrength();
if (!isset(self::$MAP_KEY_STRENGTH_METHODS[$keyStrength])) {
if (!isset(self::MAP_KEY_STRENGTH_METHODS[$keyStrength])) {
throw new InvalidArgumentException('Invalid encryption method');
}
return self::$MAP_KEY_STRENGTH_METHODS[$keyStrength];
return self::MAP_KEY_STRENGTH_METHODS[$keyStrength];
}
/**
* @return bool
*/
public function isV1()
public function isV1(): bool
{
return $this->vendorVersion === self::VERSION_AE1;
}
/**
* @return bool
*/
public function isV2()
public function isV2(): bool
{
return $this->vendorVersion === self::VERSION_AE2;
}
/**
* @return int
*/
public function getSaltSize()
public function getSaltSize(): int
{
return (int) ($this->getEncryptionStrength() / 8 / 2);
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return sprintf(
'0x%04x WINZIP AES: VendorVersion=%d KeyStrength=0x%02x CompressionMethod=%s',

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Constants\ZipConstants;
@@ -7,43 +16,30 @@ use PhpZip\Exception\RuntimeException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/**
* ZIP64 Extra Field.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*/
class Zip64ExtraField implements ZipExtraField
final class Zip64ExtraField implements ZipExtraField
{
/** @var int The Header ID for a ZIP64 Extended Information Extra Field. */
const HEADER_ID = 0x0001;
public const HEADER_ID = 0x0001;
/** @var int|null */
private $uncompressedSize;
private ?int $uncompressedSize;
/** @var int|null */
private $compressedSize;
private ?int $compressedSize;
/** @var int|null */
private $localHeaderOffset;
private ?int $localHeaderOffset;
/** @var int|null */
private $diskStart;
private ?int $diskStart;
/**
* Zip64ExtraField constructor.
*
* @param int|null $uncompressedSize
* @param int|null $compressedSize
* @param int|null $localHeaderOffset
* @param int|null $diskStart
*/
public function __construct(
$uncompressedSize = null,
$compressedSize = null,
$localHeaderOffset = null,
$diskStart = null
?int $uncompressedSize = null,
?int $compressedSize = null,
?int $localHeaderOffset = null,
?int $diskStart = null
) {
$this->uncompressedSize = $uncompressedSize;
$this->compressedSize = $compressedSize;
@@ -55,10 +51,8 @@ class Zip64ExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
public function getHeaderId(): int
{
return self::HEADER_ID;
}
@@ -66,14 +60,14 @@ class Zip64ExtraField implements ZipExtraField
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
*
* @throws ZipException on error
*
* @return Zip64ExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$length = \strlen($buffer);
@@ -91,8 +85,10 @@ class Zip64ExtraField implements ZipExtraField
);
}
$uncompressedSize = PackUtil::unpackLongLE(substr($buffer, 0, 8));
$compressedSize = PackUtil::unpackLongLE(substr($buffer, 8, 8));
[
'uncompressedSize' => $uncompressedSize,
'compressedSize' => $compressedSize,
] = unpack('PuncompressedSize/PcompressedSize', substr($buffer, 0, 16));
return new self($uncompressedSize, $compressedSize);
}
@@ -100,14 +96,14 @@ class Zip64ExtraField implements ZipExtraField
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
*
* @throws ZipException
*
* @return Zip64ExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
if ($entry === null) {
throw new RuntimeException('zipEntry is null');
@@ -125,7 +121,7 @@ class Zip64ExtraField implements ZipExtraField
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no uncompressed size).');
}
$uncompressedSize = PackUtil::unpackLongLE(substr($buffer, $length - $remaining, 8));
$uncompressedSize = unpack('P', substr($buffer, $length - $remaining, 8))[1];
$remaining -= 8;
}
@@ -133,7 +129,7 @@ class Zip64ExtraField implements ZipExtraField
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no compressed size).');
}
$compressedSize = PackUtil::unpackLongLE(substr($buffer, $length - $remaining, 8));
$compressedSize = unpack('P', substr($buffer, $length - $remaining, 8))[1];
$remaining -= 8;
}
@@ -141,7 +137,7 @@ class Zip64ExtraField implements ZipExtraField
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no relative local header offset).');
}
$localHeaderOffset = PackUtil::unpackLongLE(substr($buffer, $length - $remaining, 8));
$localHeaderOffset = unpack('P', substr($buffer, $length - $remaining, 8))[1];
$remaining -= 8;
}
@@ -158,7 +154,7 @@ class Zip64ExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData()
public function packLocalFileData(): string
{
if ($this->uncompressedSize !== null || $this->compressedSize !== null) {
if ($this->uncompressedSize === null || $this->compressedSize === null) {
@@ -173,19 +169,16 @@ class Zip64ExtraField implements ZipExtraField
return '';
}
/**
* @return string
*/
private function packSizes()
private function packSizes(): string
{
$data = '';
if ($this->uncompressedSize !== null) {
$data .= PackUtil::packLongLE($this->uncompressedSize);
$data .= pack('P', $this->uncompressedSize);
}
if ($this->compressedSize !== null) {
$data .= PackUtil::packLongLE($this->compressedSize);
$data .= pack('P', $this->compressedSize);
}
return $data;
@@ -197,12 +190,12 @@ class Zip64ExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData()
public function packCentralDirData(): string
{
$data = $this->packSizes();
if ($this->localHeaderOffset !== null) {
$data .= PackUtil::packLongLE($this->localHeaderOffset);
$data .= pack('P', $this->localHeaderOffset);
}
if ($this->diskStart !== null) {
@@ -212,74 +205,47 @@ class Zip64ExtraField implements ZipExtraField
return $data;
}
/**
* @return int|null
*/
public function getUncompressedSize()
public function getUncompressedSize(): ?int
{
return $this->uncompressedSize;
}
/**
* @param int|null $uncompressedSize
*/
public function setUncompressedSize($uncompressedSize)
public function setUncompressedSize(?int $uncompressedSize): void
{
$this->uncompressedSize = $uncompressedSize;
}
/**
* @return int|null
*/
public function getCompressedSize()
public function getCompressedSize(): ?int
{
return $this->compressedSize;
}
/**
* @param int|null $compressedSize
*/
public function setCompressedSize($compressedSize)
public function setCompressedSize(?int $compressedSize): void
{
$this->compressedSize = $compressedSize;
}
/**
* @return int|null
*/
public function getLocalHeaderOffset()
public function getLocalHeaderOffset(): ?int
{
return $this->localHeaderOffset;
}
/**
* @param int|null $localHeaderOffset
*/
public function setLocalHeaderOffset($localHeaderOffset)
public function setLocalHeaderOffset(?int $localHeaderOffset): void
{
$this->localHeaderOffset = $localHeaderOffset;
}
/**
* @return int|null
*/
public function getDiskStart()
public function getDiskStart(): ?int
{
return $this->diskStart;
}
/**
* @param int|null $diskStart
*/
public function setDiskStart($diskStart)
public function setDiskStart(?int $diskStart): void
{
$this->diskStart = $diskStart;
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
$args = [self::HEADER_ID];
$format = '0x%04x ZIP64: ';

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra;
use PhpZip\Exception\InvalidArgumentException;
@@ -22,9 +31,9 @@ final class ZipExtraDriver
{
/**
* @var array<int, string>
* @psalm-var array<int, class-string<\PhpZip\Model\Extra\ZipExtraField>>
* @psalm-var array<int, class-string<ZipExtraField>>
*/
private static $implementations = [
private static array $implementations = [
ApkAlignmentExtraField::HEADER_ID => ApkAlignmentExtraField::class,
AsiExtraField::HEADER_ID => AsiExtraField::class,
ExtendedTimestampExtraField::HEADER_ID => ExtendedTimestampExtraField::class,
@@ -45,7 +54,7 @@ final class ZipExtraDriver
/**
* @param string|ZipExtraField $extraField ZipExtraField object or class name
*/
public static function register($extraField)
public static function register($extraField): void
{
if (!is_a($extraField, ZipExtraField::class, true)) {
throw new InvalidArgumentException(
@@ -61,10 +70,8 @@ final class ZipExtraDriver
/**
* @param int|string|ZipExtraField $extraType ZipExtraField object or class name or extra header id
*
* @return bool
*/
public static function unregister($extraType)
public static function unregister($extraType): bool
{
$headerId = null;
@@ -85,23 +92,12 @@ final class ZipExtraDriver
return false;
}
/**
* @param int $headerId
*
* @return string|null
*/
public static function getClassNameOrNull($headerId)
public static function getClassNameOrNull(int $headerId): ?string
{
$headerId = (int) $headerId;
if ($headerId < 0 || $headerId > 0xffff) {
if ($headerId < 0 || $headerId > 0xFFFF) {
throw new \InvalidArgumentException('$headerId out of range: ' . $headerId);
}
if (isset(self::$implementations[$headerId])) {
return self::$implementations[$headerId];
}
return null;
return self::$implementations[$headerId] ?? null;
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra;
use PhpZip\Model\ZipEntry;
@@ -15,30 +24,28 @@ interface ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId();
public function getHeaderId(): int;
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return static
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null);
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self;
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
* @param ZipEntry|null $entry optional zip entry
*
* @return static
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null);
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self;
/**
* The actual data to put into local file data - without Header-ID
@@ -46,7 +53,7 @@ interface ZipExtraField
*
* @return string the data
*/
public function packLocalFileData();
public function packLocalFileData(): string;
/**
* The actual data to put into central directory - without Header-ID or
@@ -54,10 +61,7 @@ interface ZipExtraField
*
* @return string the data
*/
public function packCentralDirData();
public function packCentralDirData(): string;
/**
* @return string
*/
public function __toString();
public function __toString(): string;
}

View File

@@ -1,25 +1,29 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model;
/**
* Class ImmutableZipContainer.
*/
class ImmutableZipContainer implements \Countable
{
/** @var ZipEntry[] */
protected $entries;
protected array $entries;
/** @var string|null Archive comment */
protected $archiveComment;
protected ?string $archiveComment;
/**
* ZipContainer constructor.
*
* @param ZipEntry[] $entries
* @param string|null $archiveComment
* @param ZipEntry[] $entries
* @param ?string $archiveComment
*/
public function __construct(array $entries, $archiveComment)
public function __construct(array $entries, ?string $archiveComment = null)
{
$this->entries = $entries;
$this->archiveComment = $archiveComment;
@@ -28,15 +32,12 @@ class ImmutableZipContainer implements \Countable
/**
* @return ZipEntry[]
*/
public function &getEntries()
public function &getEntries(): array
{
return $this->entries;
}
/**
* @return string|null
*/
public function getArchiveComment()
public function getArchiveComment(): ?string
{
return $this->archiveComment;
}
@@ -49,8 +50,24 @@ class ImmutableZipContainer implements \Countable
* @return int The custom count as an integer.
* The return value is cast to an integer.
*/
public function count()
public function count(): int
{
return \count($this->entries);
}
/**
* When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties.
* Any properties that are references to other variables, will remain references.
* Once the cloning is complete, if a __clone() method is defined,
* then the newly created object's __clone() method will be called, to allow any necessary properties that need to
* be changed. NOT CALLABLE DIRECTLY.
*
* @see https://php.net/manual/en/language.oop5.cloning.php
*/
public function __clone()
{
foreach ($this->entries as $key => $value) {
$this->entries[$key] = clone $value;
}
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model;
use PhpZip\Constants\ZipEncryptionMethod;
@@ -8,26 +17,19 @@ use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
/**
* Class ZipContainer.
* Zip Container.
*/
class ZipContainer extends ImmutableZipContainer
{
/** @var ImmutableZipContainer|null */
private $sourceContainer;
/**
* @var int|null Apk zipalign value
*
* @todo remove and use in ApkFileWriter
* @var ImmutableZipContainer|null The source container contains zip entries from
* an open zip archive. The source container makes
* it possible to undo changes in the archive.
* When cloning, this container is not cloned.
*/
private $zipAlign;
private ?ImmutableZipContainer $sourceContainer;
/**
* MutableZipContainer constructor.
*
* @param ImmutableZipContainer|null $sourceContainer
*/
public function __construct(ImmutableZipContainer $sourceContainer = null)
public function __construct(?ImmutableZipContainer $sourceContainer = null)
{
$entries = [];
$archiveComment = null;
@@ -42,28 +44,20 @@ class ZipContainer extends ImmutableZipContainer
$this->sourceContainer = $sourceContainer;
}
/**
* @return ImmutableZipContainer|null
*/
public function getSourceContainer()
public function getSourceContainer(): ?ImmutableZipContainer
{
return $this->sourceContainer;
}
/**
* @param ZipEntry $entry
*/
public function addEntry(ZipEntry $entry)
public function addEntry(ZipEntry $entry): void
{
$this->entries[$entry->getName()] = $entry;
}
/**
* @param string|ZipEntry $entry
*
* @return bool
*/
public function deleteEntry($entry)
public function deleteEntry($entry): bool
{
$entry = $entry instanceof ZipEntry ? $entry->getName() : (string) $entry;
@@ -84,7 +78,7 @@ class ZipContainer extends ImmutableZipContainer
*
* @return ZipEntry New zip entry
*/
public function renameEntry($old, $new)
public function renameEntry($old, $new): ZipEntry
{
$old = $old instanceof ZipEntry ? $old->getName() : (string) $old;
$new = $new instanceof ZipEntry ? $new->getName() : (string) $new;
@@ -106,10 +100,8 @@ class ZipContainer extends ImmutableZipContainer
* @param string|ZipEntry $entryName
*
* @throws ZipEntryNotFoundException
*
* @return ZipEntry
*/
public function getEntry($entryName)
public function getEntry($entryName): ZipEntry
{
$entry = $this->getEntryOrNull($entryName);
@@ -122,22 +114,18 @@ class ZipContainer extends ImmutableZipContainer
/**
* @param string|ZipEntry $entryName
*
* @return ZipEntry|null
*/
public function getEntryOrNull($entryName)
public function getEntryOrNull($entryName): ?ZipEntry
{
$entryName = $entryName instanceof ZipEntry ? $entryName->getName() : (string) $entryName;
return isset($this->entries[$entryName]) ? $this->entries[$entryName] : null;
return $this->entries[$entryName] ?? null;
}
/**
* @param string|ZipEntry $entryName
*
* @return bool
*/
public function hasEntry($entryName)
public function hasEntry($entryName): bool
{
$entryName = $entryName instanceof ZipEntry ? $entryName->getName() : (string) $entryName;
@@ -147,7 +135,7 @@ class ZipContainer extends ImmutableZipContainer
/**
* Delete all entries.
*/
public function deleteAll()
public function deleteAll(): void
{
$this->entries = [];
}
@@ -159,7 +147,7 @@ class ZipContainer extends ImmutableZipContainer
*
* @return ZipEntry[] Deleted entries
*/
public function deleteByRegex($regexPattern)
public function deleteByRegex(string $regexPattern): array
{
if (empty($regexPattern)) {
throw new InvalidArgumentException('The regex pattern is not specified');
@@ -184,7 +172,7 @@ class ZipContainer extends ImmutableZipContainer
/**
* Undo all changes done in the archive.
*/
public function unchangeAll()
public function unchangeAll(): void
{
$this->entries = [];
@@ -199,7 +187,7 @@ class ZipContainer extends ImmutableZipContainer
/**
* Undo change archive comment.
*/
public function unchangeArchiveComment()
public function unchangeArchiveComment(): void
{
$this->archiveComment = null;
@@ -212,16 +200,14 @@ class ZipContainer extends ImmutableZipContainer
* Revert all changes done to an entry with the given name.
*
* @param string|ZipEntry $entry Entry name or ZipEntry
*
* @return bool
*/
public function unchangeEntry($entry)
public function unchangeEntry($entry): bool
{
$entry = $entry instanceof ZipEntry ? $entry->getName() : (string) $entry;
if (
$this->sourceContainer !== null &&
isset($this->entries[$entry], $this->sourceContainer->entries[$entry])
$this->sourceContainer !== null
&& isset($this->entries[$entry], $this->sourceContainer->entries[$entry])
) {
$this->entries[$entry] = clone $this->sourceContainer->entries[$entry];
@@ -240,10 +226,8 @@ class ZipContainer extends ImmutableZipContainer
* return strcmp($nameA, $nameB);
* });
* ```
*
* @param callable $cmp
*/
public function sortByName(callable $cmp)
public function sortByName(callable $cmp): void
{
uksort($this->entries, $cmp);
}
@@ -257,34 +241,25 @@ class ZipContainer extends ImmutableZipContainer
* return strcmp($a->getName(), $b->getName());
* });
* ```
*
* @param callable $cmp
*/
public function sortByEntry(callable $cmp)
public function sortByEntry(callable $cmp): void
{
uasort($this->entries, $cmp);
}
/**
* @param string|null $archiveComment
*/
public function setArchiveComment($archiveComment)
public function setArchiveComment(?string $archiveComment): void
{
if ($archiveComment !== null && $archiveComment !== '') {
$archiveComment = (string) $archiveComment;
$length = \strlen($archiveComment);
if ($length > 0xffff) {
if ($length > 0xFFFF) {
throw new InvalidArgumentException('Length comment out of range');
}
}
$this->archiveComment = $archiveComment;
}
/**
* @return ZipEntryMatcher
*/
public function matcher()
public function matcher(): ZipEntryMatcher
{
return new ZipEntryMatcher($this);
}
@@ -292,9 +267,9 @@ class ZipContainer extends ImmutableZipContainer
/**
* Specify a password for extracting files.
*
* @param string|null $password
* @param ?string $password
*/
public function setReadPassword($password)
public function setReadPassword(?string $password): void
{
if ($this->sourceContainer !== null) {
foreach ($this->sourceContainer->entries as $entry) {
@@ -306,13 +281,10 @@ class ZipContainer extends ImmutableZipContainer
}
/**
* @param string $entryName
* @param string $password
*
* @throws ZipEntryNotFoundException
* @throws ZipException
*/
public function setReadPasswordEntry($entryName, $password)
public function setReadPasswordEntry(string $entryName, string $password): void
{
if (!isset($this->sourceContainer->entries[$entryName])) {
throw new ZipEntryNotFoundException($entryName);
@@ -324,57 +296,39 @@ class ZipContainer extends ImmutableZipContainer
}
/**
* @return int|null
* @param ?string $writePassword
*
* @throws ZipEntryNotFoundException
*/
public function getZipAlign()
{
return $this->zipAlign;
}
/**
* @param int|null $zipAlign
*/
public function setZipAlign($zipAlign)
{
$this->zipAlign = $zipAlign === null ? null : (int) $zipAlign;
}
/**
* @return bool
*/
public function isZipAlign()
{
return $this->zipAlign !== null;
}
/**
* @param string|null $writePassword
*/
public function setWritePassword($writePassword)
public function setWritePassword(?string $writePassword): void
{
$this->matcher()->all()->setPassword($writePassword);
}
/**
* Remove password.
*
* @throws ZipEntryNotFoundException
*/
public function removePassword()
public function removePassword(): void
{
$this->matcher()->all()->setPassword(null);
}
/**
* @param string|ZipEntry $entryName
*
* @throws ZipEntryNotFoundException
*/
public function removePasswordEntry($entryName)
public function removePasswordEntry($entryName): void
{
$this->matcher()->add($entryName)->setPassword(null);
}
/**
* @param int $encryptionMethod
* @throws ZipEntryNotFoundException
*/
public function setEncryptionMethod($encryptionMethod = ZipEncryptionMethod::WINZIP_AES_256)
public function setEncryptionMethod(int $encryptionMethod = ZipEncryptionMethod::WINZIP_AES_256): void
{
$this->matcher()->all()->setEncryptionMethod($encryptionMethod);
}

View File

@@ -1,18 +1,24 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model;
use PhpZip\Exception\ZipException;
/**
* Interface ZipData.
*/
interface ZipData
{
/**
* @return string returns data as string
*/
public function getDataAsString();
public function getDataAsString(): string;
/**
* @return resource returns stream data

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,24 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model;
/**
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
use PhpZip\Exception\ZipEntryNotFoundException;
class ZipEntryMatcher implements \Countable
{
/** @var ZipContainer */
protected $zipContainer;
protected ZipContainer $zipContainer;
/** @var array */
protected $matches = [];
protected array $matches = [];
/**
* ZipEntryMatcher constructor.
*
* @param ZipContainer $zipContainer
*/
public function __construct(ZipContainer $zipContainer)
{
$this->zipContainer = $zipContainer;
@@ -29,13 +29,11 @@ class ZipEntryMatcher implements \Countable
*
* @return ZipEntryMatcher
*/
public function add($entries)
public function add($entries): self
{
$entries = (array) $entries;
$entries = array_map(
static function ($entry) {
return $entry instanceof ZipEntry ? $entry->getName() : (string) $entry;
},
static fn ($entry) => $entry instanceof ZipEntry ? $entry->getName() : (string) $entry,
$entries
);
$this->matches = array_values(
@@ -59,23 +57,16 @@ class ZipEntryMatcher implements \Countable
}
/**
* @param string $regexp
*
* @return ZipEntryMatcher
*
* @noinspection PhpUnusedParameterInspection
*/
public function match($regexp)
public function match(string $regexp): self
{
array_walk(
$this->zipContainer->getEntries(),
/**
* @param ZipEntry $entry
* @param string $entryName
*/
function (ZipEntry $entry, $entryName) use ($regexp) {
function (ZipEntry $entry, string $entryName) use ($regexp): void {
if (preg_match($regexp, $entryName)) {
$this->matches[] = (string) $entryName;
$this->matches[] = $entryName;
}
}
);
@@ -87,9 +78,12 @@ class ZipEntryMatcher implements \Countable
/**
* @return ZipEntryMatcher
*/
public function all()
public function all(): self
{
$this->matches = array_keys($this->zipContainer->getEntries());
$this->matches = array_map(
'strval',
array_keys($this->zipContainer->getEntries())
);
return $this;
}
@@ -99,36 +93,31 @@ class ZipEntryMatcher implements \Countable
*
* Callable function signature:
* function(string $entryName){}
*
* @param callable $callable
*/
public function invoke(callable $callable)
public function invoke(callable $callable): void
{
if (!empty($this->matches)) {
array_walk(
$this->matches,
/** @param string $entryName */
static function ($entryName) use ($callable) {
static function (string $entryName) use ($callable): void {
$callable($entryName);
}
);
}
}
/**
* @return array
*/
public function getMatches()
public function getMatches(): array
{
return $this->matches;
}
public function delete()
public function delete(): void
{
array_walk(
$this->matches,
/** @param string $entryName */
function ($entryName) {
function (string $entryName): void {
$this->zipContainer->deleteEntry($entryName);
}
);
@@ -136,15 +125,17 @@ class ZipEntryMatcher implements \Countable
}
/**
* @param string|null $password
* @param int|null $encryptionMethod
* @param ?string $password
* @param ?int $encryptionMethod
*
* @throws ZipEntryNotFoundException
*/
public function setPassword($password, $encryptionMethod = null)
public function setPassword(?string $password, ?int $encryptionMethod = null): void
{
array_walk(
$this->matches,
/** @param string $entryName */
function ($entryName) use ($password, $encryptionMethod) {
function (string $entryName) use ($password, $encryptionMethod): void {
$entry = $this->zipContainer->getEntry($entryName);
if (!$entry->isDirectory()) {
@@ -155,14 +146,14 @@ class ZipEntryMatcher implements \Countable
}
/**
* @param int $encryptionMethod
* @throws ZipEntryNotFoundException
*/
public function setEncryptionMethod($encryptionMethod)
public function setEncryptionMethod(int $encryptionMethod): void
{
array_walk(
$this->matches,
/** @param string $entryName */
function ($entryName) use ($encryptionMethod) {
function (string $entryName) use ($encryptionMethod): void {
$entry = $this->zipContainer->getEntry($entryName);
if (!$entry->isDirectory()) {
@@ -172,12 +163,14 @@ class ZipEntryMatcher implements \Countable
);
}
public function disableEncryption()
/**
* @throws ZipEntryNotFoundException
*/
public function disableEncryption(): void
{
array_walk(
$this->matches,
/** @param string $entryName */
function ($entryName) {
function (string $entryName): void {
$entry = $this->zipContainer->getEntry($entryName);
if (!$entry->isDirectory()) {
@@ -192,14 +185,9 @@ class ZipEntryMatcher implements \Countable
*
* @see http://php.net/manual/en/countable.count.php
*
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
*
* @since 5.1.0
* @return int the custom count as an integer
*/
public function count()
public function count(): int
{
return \count($this->matches);
}

View File

@@ -1,266 +0,0 @@
<?php
namespace PhpZip\Model;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipEncryptionMethod;
use PhpZip\Constants\ZipPlatform;
use PhpZip\Util\FileAttribUtil;
use PhpZip\Util\FilesUtil;
/**
* Zip info.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @deprecated Use ZipEntry
*/
class ZipInfo
{
/** @var ZipEntry */
private $entry;
/**
* ZipInfo constructor.
*
* @param ZipEntry $entry
*/
public function __construct(ZipEntry $entry)
{
$this->entry = $entry;
}
/**
* @param ZipEntry $entry
*
* @return string
*
* @deprecated Use {@see ZipPlatform::getPlatformName()}
*/
public static function getPlatformName(ZipEntry $entry)
{
return ZipPlatform::getPlatformName($entry->getExtractedOS());
}
/**
* @return string
*/
public function getName()
{
return $this->entry->getName();
}
/**
* @return bool
*/
public function isFolder()
{
return $this->entry->isDirectory();
}
/**
* @return int
*/
public function getSize()
{
return $this->entry->getUncompressedSize();
}
/**
* @return int
*/
public function getCompressedSize()
{
return $this->entry->getCompressedSize();
}
/**
* @return int
*/
public function getMtime()
{
return $this->entry->getMTime()->getTimestamp();
}
/**
* @return int|null
*/
public function getCtime()
{
$ctime = $this->entry->getCTime();
return $ctime === null ? null : $ctime->getTimestamp();
}
/**
* @return int|null
*/
public function getAtime()
{
$atime = $this->entry->getATime();
return $atime === null ? null : $atime->getTimestamp();
}
/**
* @return string
*/
public function getAttributes()
{
$externalAttributes = $this->entry->getExternalAttributes();
if ($this->entry->getCreatedOS() === ZipPlatform::OS_UNIX) {
$permission = (($externalAttributes >> 16) & 0xFFFF);
return FileAttribUtil::getUnixMode($permission);
}
return FileAttribUtil::getDosMode($externalAttributes);
}
/**
* @return bool
*/
public function isEncrypted()
{
return $this->entry->isEncrypted();
}
/**
* @return string|null
*/
public function getComment()
{
return $this->entry->getComment();
}
/**
* @return int
*/
public function getCrc()
{
return $this->entry->getCrc();
}
/**
* @return string
*
* @deprecated use \PhpZip\Model\ZipInfo::getMethodName()
*/
public function getMethod()
{
return $this->getMethodName();
}
/**
* @return string
*/
public function getMethodName()
{
return ZipCompressionMethod::getCompressionMethodName($this->entry->getCompressionMethod());
}
/**
* @return string
*/
public function getEncryptionMethodName()
{
return ZipEncryptionMethod::getEncryptionMethodName($this->entry->getEncryptionMethod());
}
/**
* @return string
*/
public function getPlatform()
{
return ZipPlatform::getPlatformName($this->entry->getExtractedOS());
}
/**
* @return int
*/
public function getVersion()
{
return $this->entry->getExtractVersion();
}
/**
* @return int|null
*/
public function getEncryptionMethod()
{
$encryptionMethod = $this->entry->getEncryptionMethod();
return $encryptionMethod === ZipEncryptionMethod::NONE ? null : $encryptionMethod;
}
/**
* @return int|null
*/
public function getCompressionLevel()
{
return $this->entry->getCompressionLevel();
}
/**
* @return int
*/
public function getCompressionMethod()
{
return $this->entry->getCompressionMethod();
}
/**
* @return array
*/
public function toArray()
{
return [
'name' => $this->getName(),
'folder' => $this->isFolder(),
'size' => $this->getSize(),
'compressed_size' => $this->getCompressedSize(),
'modified' => $this->getMtime(),
'created' => $this->getCtime(),
'accessed' => $this->getAtime(),
'attributes' => $this->getAttributes(),
'encrypted' => $this->isEncrypted(),
'encryption_method' => $this->getEncryptionMethod(),
'encryption_method_name' => $this->getEncryptionMethodName(),
'comment' => $this->getComment(),
'crc' => $this->getCrc(),
'method_name' => $this->getMethodName(),
'compression_method' => $this->getCompressionMethod(),
'platform' => $this->getPlatform(),
'version' => $this->getVersion(),
];
}
/**
* @return string
*/
public function __toString()
{
$ctime = $this->entry->getCTime();
$atime = $this->entry->getATime();
$comment = $this->getComment();
return __CLASS__ . ' {'
. 'Name="' . $this->getName() . '", '
. ($this->isFolder() ? 'Folder, ' : '')
. 'Size="' . FilesUtil::humanSize($this->getSize()) . '"'
. ', Compressed size="' . FilesUtil::humanSize($this->getCompressedSize()) . '"'
. ', Modified time="' . $this->entry->getMTime()->format(\DATE_W3C) . '", '
. ($ctime !== null ? 'Created time="' . $ctime->format(\DATE_W3C) . '", ' : '')
. ($atime !== null ? 'Accessed time="' . $atime->format(\DATE_W3C) . '", ' : '')
. ($this->isEncrypted() ? 'Encrypted, ' : '')
. ($comment !== null ? 'Comment="' . $comment . '", ' : '')
. (!empty($this->crc) ? 'Crc=0x' . dechex($this->crc) . ', ' : '')
. 'Method name="' . $this->getMethodName() . '", '
. 'Attributes="' . $this->getAttributes() . '", '
. 'Platform="' . $this->getPlatform() . '", '
. 'Version=' . $this->getVersion()
. '}';
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
use PhpZip\Exception\RuntimeException;
@@ -9,24 +18,8 @@ use PhpZip\Exception\RuntimeException;
*
* @internal
*/
class CryptoUtil
final class CryptoUtil
{
/**
* Returns random bytes.
*
* @param int $length
*
* @throws \Exception
*
* @return string
*
* @deprecated Use random_bytes()
*/
final public static function randomBytes($length)
{
return random_bytes($length);
}
/**
* Decrypt AES-CTR.
*
@@ -36,7 +29,7 @@ class CryptoUtil
*
* @return string Raw data
*/
public static function decryptAesCtr($data, $key, $iv)
public static function decryptAesCtr(string $data, string $key, string $iv): string
{
if (\extension_loaded('openssl')) {
$numBits = \strlen($key) * 8;
@@ -44,11 +37,7 @@ class CryptoUtil
return openssl_decrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
}
if (\extension_loaded('mcrypt')) {
return mcrypt_decrypt(\MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv);
}
throw new RuntimeException('Extension openssl or mcrypt not loaded');
throw new RuntimeException('Openssl extension not loaded');
}
/**
@@ -60,7 +49,7 @@ class CryptoUtil
*
* @return string Encrypted data
*/
public static function encryptAesCtr($data, $key, $iv)
public static function encryptAesCtr(string $data, string $key, string $iv): string
{
if (\extension_loaded('openssl')) {
$numBits = \strlen($key) * 8;
@@ -68,10 +57,6 @@ class CryptoUtil
return openssl_encrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
}
if (\extension_loaded('mcrypt')) {
return mcrypt_encrypt(\MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv);
}
throw new RuntimeException('Extension openssl or mcrypt not loaded');
throw new RuntimeException('Openssl extension not loaded');
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
/**
@@ -20,9 +29,6 @@ namespace PhpZip\Util;
*
* @see https://docs.microsoft.com/ru-ru/windows/win32/api/winbase/nf-winbase-filetimetodosdatetime?redirectedfrom=MSDN
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @internal
*/
class DateTimeConverter
@@ -30,14 +36,18 @@ class DateTimeConverter
/**
* Smallest supported DOS date/time value in a ZIP file,
* which is January 1st, 1980 AD 00:00:00 local time.
*
* @var int
*/
const MIN_DOS_TIME = 0x210000; // (1 << 21) | (1 << 16)
public const MIN_DOS_TIME = (1 << 21) | (1 << 16);
/**
* Largest supported DOS date/time value in a ZIP file,
* which is December 31st, 2107 AD 23:59:58 local time.
*
* @var int
*/
const MAX_DOS_TIME = 0xff9fbf7d; // ((2107 - 1980) << 25) | (12 << 21) | (31 << 16) | (23 << 11) | (59 << 5) | (58 >> 1);
public const MAX_DOS_TIME = ((2107 - 1980) << 25) | (12 << 21) | (31 << 16) | (23 << 11) | (59 << 5) | (58 >> 1);
/**
* Convert a 32 bit integer DOS date/time value to a UNIX timestamp value.
@@ -46,7 +56,7 @@ class DateTimeConverter
*
* @return int Unix timestamp
*/
public static function msDosToUnix($dosTime)
public static function msDosToUnix(int $dosTime): int
{
if ($dosTime <= self::MIN_DOS_TIME) {
$dosTime = 0;
@@ -55,12 +65,12 @@ class DateTimeConverter
}
// date_default_timezone_set('UTC');
return mktime(
(($dosTime >> 11) & 0x1f), // hours
(($dosTime >> 5) & 0x3f), // minutes
(($dosTime << 1) & 0x3e), // seconds
(($dosTime >> 21) & 0x0f), // month
(($dosTime >> 16) & 0x1f), // day
((($dosTime >> 25) & 0x7f) + 1980) // year
(($dosTime >> 11) & 0x1F), // hours
(($dosTime >> 5) & 0x3F), // minutes
(($dosTime << 1) & 0x3E), // seconds
(($dosTime >> 21) & 0x0F), // month
(($dosTime >> 16) & 0x1F), // day
((($dosTime >> 25) & 0x7F) + 1980) // year
);
}
@@ -74,7 +84,7 @@ class DateTimeConverter
* rounded down to even seconds
* and is in between DateTimeConverter::MIN_DOS_TIME and DateTimeConverter::MAX_DOS_TIME
*/
public static function unixToMsDos($unixTimestamp)
public static function unixToMsDos(int $unixTimestamp): int
{
if ($unixTimestamp < 0) {
throw new \InvalidArgumentException('Negative unix timestamp: ' . $unixTimestamp);
@@ -82,12 +92,12 @@ class DateTimeConverter
$date = getdate($unixTimestamp);
$dosTime = (
(($date['year'] - 1980) << 25) |
($date['mon'] << 21) |
($date['mday'] << 16) |
($date['hours'] << 11) |
($date['minutes'] << 5) |
($date['seconds'] >> 1)
(($date['year'] - 1980) << 25)
| ($date['mon'] << 21)
| ($date['mday'] << 16)
| ($date['hours'] << 11)
| ($date['minutes'] << 5)
| ($date['seconds'] >> 1)
);
if ($dosTime <= self::MIN_DOS_TIME) {

View File

@@ -1,28 +1,29 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
use PhpZip\Constants\DosAttrs;
use PhpZip\Constants\UnixStat;
/**
* Class FileAttribUtil.
*
* @internal
*/
class FileAttribUtil implements DosAttrs, UnixStat
{
/**
* Get DOS mode,.
*
* @param int $xattr
*
* @return string
* Get DOS mode.
*/
public static function getDosMode($xattr)
public static function getDosMode(int $xattr): string
{
$xattr = (int) $xattr;
$mode = (($xattr & self::DOS_DIRECTORY) === self::DOS_DIRECTORY) ? 'd' : '-';
$mode .= (($xattr & self::DOS_ARCHIVE) === self::DOS_ARCHIVE) ? 'a' : '-';
$mode .= (($xattr & self::DOS_READ_ONLY) === self::DOS_READ_ONLY) ? 'r' : '-';
@@ -34,14 +35,11 @@ class FileAttribUtil implements DosAttrs, UnixStat
}
/**
* @param int $permission
*
* @return string
* @noinspection DuplicatedCode
*/
public static function getUnixMode($permission)
public static function getUnixMode(int $permission): string
{
$mode = '';
$permission = (int) $permission;
switch ($permission & self::UNX_IFMT) {
case self::UNX_IFDIR:
$mode .= 'd';

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
use PhpZip\Util\Iterator\IgnoreFilesFilterIterator;
@@ -8,9 +17,6 @@ use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
/**
* Files util.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @internal
*/
final class FilesUtil
@@ -19,10 +25,8 @@ final class FilesUtil
* Is empty directory.
*
* @param string $dir Directory
*
* @return bool
*/
public static function isEmptyDir($dir)
public static function isEmptyDir(string $dir): bool
{
if (!is_readable($dir)) {
return false;
@@ -36,28 +40,25 @@ final class FilesUtil
*
* @param string $dir directory path
*/
public static function removeDir($dir)
public static function removeDir(string $dir): void
{
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
/** @var \SplFileInfo $fileInfo */
foreach ($files as $fileInfo) {
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
$function($fileInfo->getRealPath());
$function($fileInfo->getPathname());
}
rmdir($dir);
@rmdir($dir);
}
/**
* Convert glob pattern to regex pattern.
*
* @param string $globPattern
*
* @return string
*/
public static function convertGlobToRegEx($globPattern)
public static function convertGlobToRegEx(string $globPattern): string
{
// Remove beginning and ending * globs because they're useless
$globPattern = trim($globPattern, '*');
@@ -143,13 +144,9 @@ final class FilesUtil
/**
* Search files.
*
* @param string $inputDir
* @param bool $recursive
* @param array $ignoreFiles
*
* @return array Searched file list
*/
public static function fileSearchWithIgnore($inputDir, $recursive = true, array $ignoreFiles = [])
public static function fileSearchWithIgnore(string $inputDir, bool $recursive = true, array $ignoreFiles = []): array
{
if ($recursive) {
$directoryIterator = new \RecursiveDirectoryIterator($inputDir);
@@ -181,26 +178,20 @@ final class FilesUtil
/**
* Search files from glob pattern.
*
* @param string $globPattern
* @param int $flags
* @param bool $recursive
*
* @return array Searched file list
*/
public static function globFileSearch($globPattern, $flags = 0, $recursive = true)
public static function globFileSearch(string $globPattern, int $flags = 0, bool $recursive = true): array
{
$flags = (int) $flags;
$recursive = (bool) $recursive;
$files = glob($globPattern, $flags);
if (!$recursive) {
return $files;
}
foreach (glob(\dirname($globPattern) . '/*', \GLOB_ONLYDIR | \GLOB_NOSORT) as $dir) {
foreach (glob(\dirname($globPattern) . \DIRECTORY_SEPARATOR . '*', \GLOB_ONLYDIR | \GLOB_NOSORT) as $dir) {
// Unpacking the argument via ... is supported starting from php 5.6 only
/** @noinspection SlowArrayOperationsInLoopInspection */
$files = array_merge($files, self::globFileSearch($dir . '/' . basename($globPattern), $flags, $recursive));
$files = array_merge($files, self::globFileSearch($dir . \DIRECTORY_SEPARATOR . basename($globPattern), $flags, $recursive));
}
return $files;
@@ -209,13 +200,9 @@ final class FilesUtil
/**
* Search files from regex pattern.
*
* @param string $folder
* @param string $pattern
* @param bool $recursive
*
* @return array Searched file list
*/
public static function regexFileSearch($folder, $pattern, $recursive = true)
public static function regexFileSearch(string $folder, string $pattern, bool $recursive = true): array
{
if ($recursive) {
$directoryIterator = new \RecursiveDirectoryIterator($folder);
@@ -242,10 +229,8 @@ final class FilesUtil
*
* @param int $size Size bytes
* @param string|null $unit Unit support 'GB', 'MB', 'KB'
*
* @return string
*/
public static function humanSize($size, $unit = null)
public static function humanSize(int $size, ?string $unit = null): string
{
if (($unit === null && $size >= 1 << 30) || $unit === 'GB') {
return number_format($size / (1 << 30), 2) . 'GB';
@@ -266,18 +251,14 @@ final class FilesUtil
* Normalizes zip path.
*
* @param string $path Zip path
*
* @return string
*/
public static function normalizeZipPath($path)
public static function normalizeZipPath(string $path): string
{
return implode(
'/',
\DIRECTORY_SEPARATOR,
array_filter(
explode('/', (string) $path),
static function ($part) {
return $part !== '.' && $part !== '..';
}
explode('/', $path),
static fn ($part) => $part !== '.' && $part !== '..'
)
);
}
@@ -287,11 +268,9 @@ final class FilesUtil
*
* @param string $file A file path
*
* @return bool
*
* @see source symfony filesystem component
*/
public static function isAbsolutePath($file)
public static function isAbsolutePath(string $file): bool
{
return strspn($file, '/\\', 0, 1)
|| (
@@ -302,45 +281,16 @@ final class FilesUtil
|| parse_url($file, \PHP_URL_SCHEME) !== null;
}
/**
* @param string $linkPath
* @param string $target
*
* @return bool
*/
public static function symlink($target, $linkPath)
public static function symlink(string $target, string $path, bool $allowSymlink): bool
{
if (\DIRECTORY_SEPARATOR === '\\') {
$linkPath = str_replace('/', '\\', $linkPath);
$target = str_replace('/', '\\', $target);
$abs = null;
if (!self::isAbsolutePath($target)) {
$abs = realpath(\dirname($linkPath) . \DIRECTORY_SEPARATOR . $target);
if (\is_string($abs)) {
$target = $abs;
}
}
if (\DIRECTORY_SEPARATOR === '\\' || !$allowSymlink) {
return file_put_contents($path, $target) !== false;
}
if (!symlink($target, $linkPath)) {
if (\DIRECTORY_SEPARATOR === '\\' && is_file($target)) {
return copy($target, $linkPath);
}
return false;
}
return true;
return symlink($target, $path);
}
/**
* @param string $file
*
* @return bool
*/
public static function isBadCompressionFile($file)
public static function isBadCompressionFile(string $file): bool
{
$badCompressFileExt = [
'dic',
@@ -366,12 +316,7 @@ final class FilesUtil
return self::isBadCompressionMimeType($mimeType);
}
/**
* @param string $mimeType
*
* @return bool
*/
public static function isBadCompressionMimeType($mimeType)
public static function isBadCompressionMimeType(string $mimeType): bool
{
static $badDeflateCompMimeTypes = [
'application/epub+zip',
@@ -422,21 +367,13 @@ final class FilesUtil
'x-epoc/x-sisx-app',
];
if (\in_array($mimeType, $badDeflateCompMimeTypes, true)) {
return true;
}
return false;
return \in_array($mimeType, $badDeflateCompMimeTypes, true);
}
/**
* @param string $file
*
* @return string
*
* @noinspection PhpComposerExtensionStubsInspection
*/
public static function getMimeTypeFromFile($file)
public static function getMimeTypeFromFile(string $file): string
{
if (\function_exists('mime_content_type')) {
return mime_content_type($file);
@@ -446,14 +383,10 @@ final class FilesUtil
}
/**
* @param string $contents
*
* @return string
* @noinspection PhpComposerExtensionStubsInspection
*/
public static function getMimeTypeFromString($contents)
public static function getMimeTypeFromString(string $contents): string
{
$contents = (string) $contents;
$finfo = new \finfo(\FILEINFO_MIME);
$mimeType = $finfo->buffer($contents);

View File

@@ -1,28 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util\Iterator;
use PhpZip\Util\StringUtil;
/**
* Iterator for ignore files.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class IgnoreFilesFilterIterator extends \FilterIterator
{
/**
* Ignore list files.
*
* @var array
*/
private $ignoreFiles = ['..'];
/** Ignore list files. */
private array $ignoreFiles = ['..'];
/**
* @param \Iterator $iterator
* @param array $ignoreFiles
*/
public function __construct(\Iterator $iterator, array $ignoreFiles)
{
parent::__construct($iterator);
@@ -35,10 +33,8 @@ class IgnoreFilesFilterIterator extends \FilterIterator
* @see http://php.net/manual/en/filteriterator.accept.php
*
* @return bool true if the current element is acceptable, otherwise false
*
* @since 5.1.0
*/
public function accept()
public function accept(): bool
{
/**
* @var \SplFileInfo $fileInfo

View File

@@ -1,28 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util\Iterator;
use PhpZip\Util\StringUtil;
/**
* Recursive iterator for ignore files.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
{
/**
* Ignore list files.
*
* @var array
*/
private $ignoreFiles = ['..'];
/** Ignore list files. */
private array $ignoreFiles = ['..'];
/**
* @param \RecursiveIterator $iterator
* @param array $ignoreFiles
*/
public function __construct(\RecursiveIterator $iterator, array $ignoreFiles)
{
parent::__construct($iterator);
@@ -35,10 +33,8 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
* @see http://php.net/manual/en/filteriterator.accept.php
*
* @return bool true if the current element is acceptable, otherwise false
*
* @since 5.1.0
*/
public function accept()
public function accept(): bool
{
/**
* @var \SplFileInfo $fileInfo
@@ -66,8 +62,10 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
/**
* @return IgnoreFilesRecursiveFilterIterator
* @psalm-suppress UndefinedInterfaceMethod
* @noinspection PhpPossiblePolymorphicInvocationInspection
*/
public function getChildren()
public function getChildren(): self
{
return new self($this->getInnerIterator()->getChildren(), $this->ignoreFiles);
}

36
src/Util/MathUtil.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
/**
* Math util.
*
* @internal
*/
final class MathUtil
{
/**
* Cast to signed int 32-bit.
*/
public static function toSignedInt32(int $int): int
{
if (\PHP_INT_SIZE === 8) {
$int &= 0xFFFFFFFF;
if ($int & 0x80000000) {
return $int - 0x100000000;
}
}
return $int;
}
}

View File

@@ -1,69 +0,0 @@
<?php
namespace PhpZip\Util;
/**
* Pack util.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @internal
*/
final class PackUtil
{
/**
* @param int $longValue
*
* @return string
*/
public static function packLongLE($longValue)
{
if (\PHP_VERSION_ID >= 506030) {
return pack('P', $longValue);
}
$left = 0xffffffff00000000;
$right = 0x00000000ffffffff;
$r = ($longValue & $left) >> 32;
$l = $longValue & $right;
return pack('VV', $l, $r);
}
/**
* @param string $value
*
* @return int
*/
public static function unpackLongLE($value)
{
if (\PHP_VERSION_ID >= 506030) {
return unpack('P', $value)[1];
}
$unpack = unpack('Va/Vb', $value);
return $unpack['a'] + ($unpack['b'] << 32);
}
/**
* Cast to signed int 32-bit.
*
* @param int $int
*
* @return int
*/
public static function toSignedInt32($int)
{
if (\PHP_INT_SIZE === 8) {
$int &= 0xffffffff;
if ($int & 0x80000000) {
return $int - 0x100000000;
}
}
return $int;
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
/**
@@ -9,40 +18,18 @@ namespace PhpZip\Util;
*/
final class StringUtil
{
/**
* @param string $haystack
* @param string $needle
*
* @return bool
*/
public static function endsWith($haystack, $needle)
public static function endsWith(string $haystack, string $needle): bool
{
$length = \strlen($needle);
if ($length === 0) {
return true;
}
return substr($haystack, -$length) === $needle;
return $needle === '' || ($haystack !== '' && substr_compare($haystack, $needle, -\strlen($needle)) === 0);
}
/**
* @param string $string
*
* @return bool
*/
public static function isBinary($string)
public static function isBinary(string $string): bool
{
return strpos($string, "\0") !== false;
}
/**
* @param string $name
*
* @return bool
*/
public static function isASCII($name)
public static function isASCII(string $name): bool
{
return preg_match('~[^\x20-\x7e]~', (string) $name) === 0;
return preg_match('~[^\x20-\x7e]~', $name) === 0;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,897 +0,0 @@
<?php
namespace PhpZip;
use PhpZip\Constants\ZipCompressionLevel;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipEncryptionMethod;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\ZipEntry;
use PhpZip\Model\ZipEntryMatcher;
use PhpZip\Model\ZipInfo;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Finder\Finder;
/**
* Create, open .ZIP files, modify, get info and extract files.
*
* Implemented support traditional PKWARE encryption and WinZip AES encryption.
* Implemented support ZIP64.
* Support ZipAlign functional.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @deprecated will be removed in version 4.0. Use the {@see ZipFile} class.
*/
interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
{
/**
* Method for Stored (uncompressed) entries.
*
* @see ZipEntry::setCompressionMethod()
* @deprecated Use {@see ZipCompressionMethod::STORED}
*/
const METHOD_STORED = ZipCompressionMethod::STORED;
/**
* Method for Deflated compressed entries.
*
* @see ZipEntry::setCompressionMethod()
* @deprecated Use {@see ZipCompressionMethod::DEFLATED}
*/
const METHOD_DEFLATED = ZipCompressionMethod::DEFLATED;
/**
* Method for BZIP2 compressed entries.
* Require php extension bz2.
*
* @see ZipEntry::setCompressionMethod()
* @deprecated Use {@see ZipCompressionMethod::BZIP2}
*/
const METHOD_BZIP2 = ZipCompressionMethod::BZIP2;
/**
* @var int default compression level
*
* @deprecated Use {@see ZipCompressionLevel::NORMAL}
*/
const LEVEL_DEFAULT_COMPRESSION = ZipCompressionLevel::NORMAL;
/**
* Compression level for fastest compression.
*
* @deprecated Use {@see ZipCompressionLevel::FAST}
*/
const LEVEL_FAST = ZipCompressionLevel::FAST;
/**
* Compression level for fastest compression.
*
* @deprecated Use {@see ZipCompressionLevel::SUPER_FAST}
*/
const LEVEL_BEST_SPEED = ZipCompressionLevel::SUPER_FAST;
/** @deprecated Use {@see ZipCompressionLevel::SUPER_FAST} */
const LEVEL_SUPER_FAST = ZipCompressionLevel::SUPER_FAST;
/**
* Compression level for best compression.
*
* @deprecated Use {@see ZipCompressionLevel::MAXIMUM}
*/
const LEVEL_BEST_COMPRESSION = ZipCompressionLevel::MAXIMUM;
/**
* No specified method for set encryption method to Traditional PKWARE encryption.
*
* @deprecated Use {@see ZipEncryptionMethod::PKWARE}
*/
const ENCRYPTION_METHOD_TRADITIONAL = ZipEncryptionMethod::PKWARE;
/**
* No specified method for set encryption method to WinZip AES encryption.
* Default value 256 bit.
*
* @deprecated Use {@see ZipEncryptionMethod::WINZIP_AES_256}
*/
const ENCRYPTION_METHOD_WINZIP_AES = ZipEncryptionMethod::WINZIP_AES_256;
/**
* No specified method for set encryption method to WinZip AES encryption 128 bit.
*
* @deprecated Use {@see ZipEncryptionMethod::WINZIP_AES_128}
*/
const ENCRYPTION_METHOD_WINZIP_AES_128 = ZipEncryptionMethod::WINZIP_AES_128;
/**
* No specified method for set encryption method to WinZip AES encryption 194 bit.
*
* @deprecated Use {@see ZipEncryptionMethod::WINZIP_AES_192}
*/
const ENCRYPTION_METHOD_WINZIP_AES_192 = ZipEncryptionMethod::WINZIP_AES_192;
/**
* No specified method for set encryption method to WinZip AES encryption 256 bit.
*
* @deprecated Use {@see ZipEncryptionMethod::WINZIP_AES_256}
*/
const ENCRYPTION_METHOD_WINZIP_AES_256 = ZipEncryptionMethod::WINZIP_AES_256;
/**
* Open zip archive from file.
*
* @param string $filename
* @param array $options
*
* @throws ZipException if can't open file
*
* @return ZipFile
*/
public function openFile($filename, array $options = []);
/**
* Open zip archive from raw string data.
*
* @param string $data
* @param array $options
*
* @throws ZipException if can't open temp stream
*
* @return ZipFile
*/
public function openFromString($data, array $options = []);
/**
* Open zip archive from stream resource.
*
* @param resource $handle
* @param array $options
*
* @throws ZipException
*
* @return ZipFile
*/
public function openFromStream($handle, array $options = []);
/**
* @return string[] returns the list files
*/
public function getListFiles();
/**
* @return int returns the number of entries in this ZIP file
*/
public function count();
/**
* Returns the file comment.
*
* @return string|null the file comment
*/
public function getArchiveComment();
/**
* Set archive comment.
*
* @param string|null $comment
*
* @return ZipFile
*/
public function setArchiveComment($comment = null);
/**
* Checks if there is an entry in the archive.
*
* @param string $entryName
*
* @return bool
*/
public function hasEntry($entryName);
/**
* Returns ZipEntry object.
*
* @param string $entryName
*
* @throws ZipEntryNotFoundException
*
* @return ZipEntry
*/
public function getEntry($entryName);
/**
* Checks that the entry in the archive is a directory.
* Returns true if and only if this ZIP entry represents a directory entry
* (i.e. end with '/').
*
* @param string $entryName
*
* @throws ZipEntryNotFoundException
*
* @return bool
*/
public function isDirectory($entryName);
/**
* Returns entry comment.
*
* @param string $entryName
*
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return string
*/
public function getEntryComment($entryName);
/**
* Set entry comment.
*
* @param string $entryName
* @param string|null $comment
*
* @throws ZipEntryNotFoundException
* @throws ZipException
*
* @return ZipFile
*/
public function setEntryComment($entryName, $comment = null);
/**
* Returns the entry contents.
*
* @param string $entryName
*
* @throws ZipEntryNotFoundException
* @throws ZipException
*
* @return string
*/
public function getEntryContents($entryName);
/**
* @param string $entryName
*
* @throws ZipEntryNotFoundException
* @throws ZipException
*
* @return resource
*/
public function getEntryStream($entryName);
/**
* Get info by entry.
*
* @param string|ZipEntry $entryName
*
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return ZipInfo
*/
public function getEntryInfo($entryName);
/**
* Get info by all entries.
*
* @return ZipInfo[]
*/
public function getAllInfo();
/**
* @return ZipEntryMatcher
*/
public function matcher();
/**
* Extract the archive contents (unzip).
*
* Extract the complete archive or the given files to the specified destination.
*
* @param string $destDir location where to extract the files
* @param array|string|null $entries The entries to extract. It accepts either
* a single entry name or an array of names.
*
* @throws ZipException
*
* @return ZipFile
*/
public function extractTo($destDir, $entries = null);
/**
* Add entry from the string.
*
* @param string $entryName zip entry name
* @param string $contents string contents
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED},
* {@see ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}.
* If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*/
public function addFromString($entryName, $contents, $compressionMethod = null);
/**
* @param Finder $finder
* @param array $options
*
* @throws ZipException
*
* @return ZipEntry[]
*/
public function addFromFinder(Finder $finder, array $options = []);
/**
* @param \SplFileInfo $file
* @param string|null $entryName
* @param array $options
*
* @throws ZipException
*
* @return ZipEntry
*/
public function addSplFile(\SplFileInfo $file, $entryName = null, array $options = []);
/**
* Add entry from the file.
*
* @param string $filename destination file
* @param string|null $entryName zip Entry name
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED},
* {@see ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}.
* If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*/
public function addFile($filename, $entryName = null, $compressionMethod = null);
/**
* Add entry from the stream.
*
* @param resource $stream stream resource
* @param string $entryName zip Entry name
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED},
* {@see ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}.
* If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*/
public function addFromStream($stream, $entryName, $compressionMethod = null);
/**
* Add an empty directory in the zip archive.
*
* @param string $dirName
*
* @throws ZipException
*
* @return ZipFile
*/
public function addEmptyDir($dirName);
/**
* Add directory not recursively to the zip archive.
*
* @param string $inputDir Input directory
* @param string $localPath add files to this directory, or the root
* @param int|null $compressionMethod Compression method.
*
* Use {@see ZipCompressionMethod::STORED}, {@see
* ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*/
public function addDir($inputDir, $localPath = '/', $compressionMethod = null);
/**
* Add recursive directory to the zip archive.
*
* @param string $inputDir Input directory
* @param string $localPath add files to this directory, or the root
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED}, {@see
* ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*
* @see ZipCompressionMethod::STORED
* @see ZipCompressionMethod::DEFLATED
* @see ZipCompressionMethod::BZIP2
*/
public function addDirRecursive($inputDir, $localPath = '/', $compressionMethod = null);
/**
* Add directories from directory iterator.
*
* @param \Iterator $iterator directory iterator
* @param string $localPath add files to this directory, or the root
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED}, {@see
* ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*
* @see ZipCompressionMethod::STORED
* @see ZipCompressionMethod::DEFLATED
* @see ZipCompressionMethod::BZIP2
*/
public function addFilesFromIterator(\Iterator $iterator, $localPath = '/', $compressionMethod = null);
/**
* Add files from glob pattern.
*
* @param string $inputDir Input directory
* @param string $globPattern glob pattern
* @param string $localPath add files to this directory, or the root
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED},
* {@see ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
*/
public function addFilesFromGlob($inputDir, $globPattern, $localPath = '/', $compressionMethod = null);
/**
* Add files recursively from glob pattern.
*
* @param string $inputDir Input directory
* @param string $globPattern glob pattern
* @param string $localPath add files to this directory, or the root
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED},
* {@see ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
*/
public function addFilesFromGlobRecursive($inputDir, $globPattern, $localPath = '/', $compressionMethod = null);
/**
* Add files from regex pattern.
*
* @param string $inputDir search files in this directory
* @param string $regexPattern regex pattern
* @param string $localPath add files to this directory, or the root
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED},
* {@see ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*
* @internal param bool $recursive Recursive search
*/
public function addFilesFromRegex($inputDir, $regexPattern, $localPath = '/', $compressionMethod = null);
/**
* Add files recursively from regex pattern.
*
* @param string $inputDir search files in this directory
* @param string $regexPattern regex pattern
* @param string $localPath add files to this directory, or the root
* @param int|null $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED},
* {@see ZipCompressionMethod::DEFLATED} or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*
* @internal param bool $recursive Recursive search
*/
public function addFilesFromRegexRecursive($inputDir, $regexPattern, $localPath = '/', $compressionMethod = null);
/**
* Add array data to archive.
* Keys is local names.
* Values is contents.
*
* @param array $mapData associative array for added to zip
*/
public function addAll(array $mapData);
/**
* Rename the entry.
*
* @param string $oldName old entry name
* @param string $newName new entry name
*
* @throws ZipException
*
* @return ZipFile
*/
public function rename($oldName, $newName);
/**
* Delete entry by name.
*
* @param string $entryName zip Entry name
*
* @throws ZipEntryNotFoundException if entry not found
*
* @return ZipFile
*/
public function deleteFromName($entryName);
/**
* Delete entries by glob pattern.
*
* @param string $globPattern Glob pattern
*
* @return ZipFile
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
*/
public function deleteFromGlob($globPattern);
/**
* Delete entries by regex pattern.
*
* @param string $regexPattern Regex pattern
*
* @return ZipFile
*/
public function deleteFromRegex($regexPattern);
/**
* Delete all entries.
*
* @return ZipFile
*/
public function deleteAll();
/**
* Set compression level for new entries.
*
* @param int $compressionLevel
*
* @return ZipFile
*
* @see ZipCompressionLevel::NORMAL
* @see ZipCompressionLevel::SUPER_FAST
* @see ZipCompressionLevel::FAST
* @see ZipCompressionLevel::MAXIMUM
*/
public function setCompressionLevel($compressionLevel = ZipCompressionLevel::NORMAL);
/**
* @param string $entryName
* @param int $compressionLevel
*
* @throws ZipException
*
* @return ZipFile
*
* @see ZipCompressionLevel::NORMAL
* @see ZipCompressionLevel::SUPER_FAST
* @see ZipCompressionLevel::FAST
* @see ZipCompressionLevel::MAXIMUM
*/
public function setCompressionLevelEntry($entryName, $compressionLevel);
/**
* @param string $entryName
* @param int $compressionMethod Compression method.
* Use {@see ZipCompressionMethod::STORED}, {@see ZipCompressionMethod::DEFLATED}
* or
* {@see ZipCompressionMethod::BZIP2}. If null, then auto choosing method.
*
* @throws ZipException
*
* @return ZipFile
*
* @see ZipCompressionMethod::STORED
* @see ZipCompressionMethod::DEFLATED
* @see ZipCompressionMethod::BZIP2
*/
public function setCompressionMethodEntry($entryName, $compressionMethod);
/**
* zipalign is optimization to Android application (APK) files.
*
* @param int|null $align
*
* @return ZipFile
*
* @see https://developer.android.com/studio/command-line/zipalign.html
*/
public function setZipAlign($align = null);
/**
* Set password to all input encrypted entries.
*
* @param string $password Password
*
* @return ZipFile
*/
public function setReadPassword($password);
/**
* Set password to concrete input entry.
*
* @param string $entryName
* @param string $password Password
*
* @throws ZipException
*
* @return ZipFile
*/
public function setReadPasswordEntry($entryName, $password);
/**
* Sets a new password for all files in the archive.
*
* @param string $password Password
* @param int|null $encryptionMethod Encryption method
*
* @return ZipFile
*/
public function setPassword($password, $encryptionMethod = ZipEncryptionMethod::WINZIP_AES_256);
/**
* Sets a new password of an entry defined by its name.
*
* @param string $entryName
* @param string $password
* @param int|null $encryptionMethod
*
* @throws ZipException
*
* @return ZipFile
*/
public function setPasswordEntry($entryName, $password, $encryptionMethod = null);
/**
* Disable encryption for all entries that are already in the archive.
*
* @return ZipFile
*/
public function disableEncryption();
/**
* Disable encryption of an entry defined by its name.
*
* @param string $entryName
*
* @return ZipFile
*/
public function disableEncryptionEntry($entryName);
/**
* Undo all changes done in the archive.
*
* @return ZipFile
*/
public function unchangeAll();
/**
* Undo change archive comment.
*
* @return ZipFile
*/
public function unchangeArchiveComment();
/**
* Revert all changes done to an entry with the given name.
*
* @param string|ZipEntry $entry Entry name or ZipEntry
*
* @return ZipFile
*/
public function unchangeEntry($entry);
/**
* Save as file.
*
* @param string $filename Output filename
*
* @throws ZipException
*
* @return ZipFile
*/
public function saveAsFile($filename);
/**
* Save as stream.
*
* @param resource $handle Output stream resource
*
* @throws ZipException
*
* @return ZipFile
*/
public function saveAsStream($handle);
/**
* Output .ZIP archive as attachment.
* Die after output.
*
* @param string $outputFilename Output filename
* @param string|null $mimeType Mime-Type
* @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline
*
* @throws ZipException
*/
public function outputAsAttachment($outputFilename, $mimeType = null, $attachment = true);
/**
* Output .ZIP archive as PSR-7 Response.
*
* @param ResponseInterface $response Instance PSR-7 Response
* @param string $outputFilename Output filename
* @param string|null $mimeType Mime-Type
* @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline
*
* @throws ZipException
*
* @return ResponseInterface
*/
public function outputAsResponse(
ResponseInterface $response,
$outputFilename,
$mimeType = null,
$attachment = true
);
/**
* Returns the zip archive as a string.
*
* @throws ZipException
*
* @return string
*/
public function outputAsString();
/**
* Close zip archive and release input stream.
*/
public function close();
/**
* Save and reopen zip archive.
*
* @throws ZipException
*
* @return ZipFile
*/
public function rewrite();
/**
* Offset to set.
*
* @see http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param string $entryName the offset to assign the value to
* @param string|\DirectoryIterator|\SplFileInfo|resource $contents the value to set
*
* @throws ZipException
*
* @see ZipFile::addFromString
* @see ZipFile::addEmptyDir
* @see ZipFile::addFile
* @see ZipFile::addFilesFromIterator
*/
public function offsetSet($entryName, $contents);
/**
* Offset to unset.
*
* @see http://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param string $entryName the offset to unset
*
* @throws ZipEntryNotFoundException
*/
public function offsetUnset($entryName);
/**
* Return the current element.
*
* @see http://php.net/manual/en/iterator.current.php
*
* @throws ZipException
*
* @return mixed can return any type
*
* @since 5.0.0
*/
public function current();
/**
* Offset to retrieve.
*
* @see http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param string $entryName the offset to retrieve
*
* @throws ZipException
*
* @return string|null
*/
public function offsetGet($entryName);
/**
* Return the key of the current element.
*
* @see http://php.net/manual/en/iterator.key.php
*
* @return mixed scalar on success, or null on failure
*
* @since 5.0.0
*/
public function key();
/**
* Move forward to next element.
*
* @see http://php.net/manual/en/iterator.next.php
* @since 5.0.0
*/
public function next();
/**
* Checks if current position is valid.
*
* @see http://php.net/manual/en/iterator.valid.php
*
* @return bool The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*
* @since 5.0.0
*/
public function valid();
/**
* Whether a offset exists.
*
* @see http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param string $entryName an offset to check for
*
* @return bool true on success or false on failure.
* The return value will be casted to boolean if non-boolean was returned.
*/
public function offsetExists($entryName);
/**
* Rewind the Iterator to the first element.
*
* @see http://php.net/manual/en/iterator.rewind.php
* @since 5.0.0
*/
public function rewind();
}

View File

@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\Tests\Internal\CustomZip\ZipFileCustomWriter;
use PhpZip\Tests\Internal\CustomZip\ZipFileWithBeforeSave;
use PhpZip\Tests\Internal\Epub\EpubFile;
use PhpZip\ZipFile;
/**
* Checks the ability to create own file-type class, reader, writer and container.
**.
*
* @internal
*
* @small
*/
final class CustomZipFormatTest extends ZipTestCase
{
/**
* @throws ZipException
*
* @see http://www.epubtest.org/test-books source epub files
*/
public function testEpub(): void
{
$epubFile = new EpubFile();
$epubFile->openFile(__DIR__ . '/resources/Advanced-v1.0.0.epub');
self::assertSame($epubFile->getRootFile(), 'EPUB/package.opf');
self::assertSame($epubFile->getMimeType(), 'application/epub+zip');
$epubInfo = $epubFile->getEpubInfo();
self::assertSame($epubInfo->toArray(), [
'title' => 'Advanced Accessibility Tests: Extended Descriptions',
'creator' => 'DAISY Consortium Transition to EPUB 3 and DIAGRAM Standards WG',
'language' => 'en-US',
'publisher' => 'DAISY Consortium and DIAGRAM Center',
'description' => 'Tests for accessible extended descriptions of images in EPUBs',
'rights' => 'This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike (CC BY-NC-SA) license.',
'date' => '2019-01-03',
'subject' => 'extended-descriptions',
]);
$epubFile->deleteFromName('mimetype');
self::assertFalse($epubFile->hasEntry('mimetype'));
try {
$epubFile->getMimeType();
self::fail('deleted mimetype');
} catch (ZipEntryNotFoundException $e) {
self::assertSame('Zip Entry "mimetype" was not found in the archive.', $e->getMessage());
}
$epubFile->saveAsFile($this->outputFilename);
self::assertFalse($epubFile->hasEntry('mimetype'));
$epubFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$epubFile->openFile($this->outputFilename);
// file appended in EpubWriter before write
self::assertTrue($epubFile->hasEntry('mimetype'));
$epubFile->close();
}
/**
* @throws \Exception
*/
public function testBeforeSaveInZipWriter(): void
{
$zipFile = new ZipFileCustomWriter();
for ($i = 0; $i < 10; $i++) {
$zipFile['file ' . $i] = 'contents file ' . $i;
}
$this->existsExtraFields($zipFile, false);
$zipFile->saveAsFile($this->outputFilename);
$this->existsExtraFields($zipFile, false);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
}
/**
* @throws \Exception
*/
public function testBeforeSaveInZipFile(): void
{
$zipFile = new ZipFileWithBeforeSave();
for ($i = 0; $i < 10; $i++) {
$zipFile['file ' . $i] = 'contents file ' . $i;
}
$this->existsExtraFields($zipFile, false);
$zipFile->saveAsFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
}
private function existsExtraFields(ZipFile $zipFile, bool $exists): void
{
foreach ($zipFile->getEntries() as $entry) {
$localExtras = $entry->getLocalExtraFields();
$cdExtras = $entry->getCdExtraFields();
self::assertSame(isset($localExtras[NtfsExtraField::HEADER_ID]), $exists);
self::assertSame(isset($cdExtras[NtfsExtraField::HEADER_ID]), $exists);
self::assertSame(isset($localExtras[NewUnixExtraField::HEADER_ID]), $exists);
self::assertSame(isset($cdExtras[NewUnixExtraField::HEADER_ID]), $exists);
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\AbstractUnicodeExtraField;
abstract class AbstractUnicodeExtraFieldTest extends TestCase
{
/**
* @return string|AbstractUnicodeExtraField
*
* @psalm-var class-string<AbstractUnicodeExtraField>
*/
abstract protected function getUnicodeExtraFieldClassName();
/**
* @dataProvider provideExtraField
*
* @throws ZipException
*/
public function testExtraField(int $crc32, string $unicodePath, string $originalPath, string $binaryData): void
{
$className = $this->getUnicodeExtraFieldClassName();
/** @var AbstractUnicodeExtraField $extraField */
$extraField = new $className($crc32, $unicodePath);
static::assertSame($extraField->getCrc32(), $crc32);
static::assertSame($extraField->getUnicodeValue(), $unicodePath);
static::assertSame(crc32($originalPath), $crc32);
static::assertSame($binaryData, $extraField->packLocalFileData());
static::assertSame($binaryData, $extraField->packCentralDirData());
static::assertEquals($className::unpackLocalFileData($binaryData), $extraField);
static::assertEquals($className::unpackCentralDirData($binaryData), $extraField);
}
abstract public function provideExtraField(): array;
public function testSetter(): void
{
$className = $this->getUnicodeExtraFieldClassName();
$entryName = '11111';
/** @var AbstractUnicodeExtraField $extraField */
$extraField = new $className(crc32($entryName), '22222');
static::assertSame($extraField->getHeaderId(), $className::HEADER_ID);
static::assertSame($extraField->getCrc32(), crc32($entryName));
static::assertSame($extraField->getUnicodeValue(), '22222');
$crc32 = 1234567;
$extraField->setCrc32($crc32);
static::assertSame($extraField->getCrc32(), $crc32);
$extraField->setUnicodeValue('44444');
static::assertSame($extraField->getUnicodeValue(), '44444');
}
/**
* @throws ZipException
*/
public function testUnicodeErrorParse(): void
{
$this->expectException(ZipException::class);
$this->expectExceptionMessage('Unicode path extra data must have at least 5 bytes.');
$className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData('');
}
/**
* @throws ZipException
*/
public function testUnknownVersionParse(): void
{
$this->expectException(ZipException::class);
$this->expectExceptionMessage('Unsupported version [2] for Unicode path extra data.');
$className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData("\x02\x04a\xD28\xC3\xA4\\\xC3\xBC.txt");
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\Crc32Exception;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\AsiExtraField;
/**
* @internal
*
* @small
*/
final class AsiExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @throws ZipException
*/
public function testExtraField(int $mode, int $uid, int $gid, string $link, string $binaryData): void
{
$asiExtraField = new AsiExtraField($mode, $uid, $gid, $link);
self::assertSame($asiExtraField->getHeaderId(), AsiExtraField::HEADER_ID);
self::assertSame($asiExtraField->getMode(), $mode);
self::assertSame($asiExtraField->getUserId(), $uid);
self::assertSame($asiExtraField->getGroupId(), $uid);
self::assertSame($asiExtraField->getLink(), $link);
self::assertSame($asiExtraField->packLocalFileData(), $binaryData);
self::assertSame($asiExtraField->packCentralDirData(), $binaryData);
self::assertEquals(AsiExtraField::unpackLocalFileData($binaryData), $asiExtraField);
self::assertEquals(AsiExtraField::unpackCentralDirData($binaryData), $asiExtraField);
}
public function provideExtraField(): array
{
return [
[
040755,
AsiExtraField::USER_GID_PID,
AsiExtraField::USER_GID_PID,
'',
"#\x06\\\xF6\xEDA\x00\x00\x00\x00\xE8\x03\xE8\x03",
],
[
0100644,
0,
0,
'sites-enabled/example.conf',
"_\xB8\xC7b\xA4\x81\x1A\x00\x00\x00\x00\x00\x00\x00sites-enabled/example.conf",
],
];
}
public function testSetter(): void
{
$extraField = new AsiExtraField(0777);
$extraField->setMode(0100666);
self::assertSame(0100666, $extraField->getMode());
$extraField->setUserId(700);
self::assertSame(700, $extraField->getUserId());
$extraField->setGroupId(500);
self::assertSame(500, $extraField->getGroupId());
$extraField->setLink('link.txt');
self::assertSame($extraField->getLink(), 'link.txt');
self::assertSame(0120666, $extraField->getMode());
// dir mode
$extraField->setMode(0755);
self::assertSame(0120755, $extraField->getMode());
$extraField->setLink('');
self::assertSame($extraField->getLink(), '');
self::assertSame(0100755, $extraField->getMode());
}
/**
* @throws Crc32Exception
*/
public function testInvalidParse(): void
{
$this->expectException(Crc32Exception::class);
$this->expectExceptionMessage('Asi Unix Extra Filed Data (expected CRC32 value');
AsiExtraField::unpackLocalFileData("\x01\x06\\\xF6\xEDA\x00\x00\x00\x00\xE8\x03\xE8\x03");
}
}

View File

@@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Model\Extra\Fields\ExtendedTimestampExtraField;
/**
* Class ExtendedTimestampExtraFieldTest.
*
* @internal
*
* @small
*/
final class ExtendedTimestampExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @noinspection PhpTooManyParametersInspection
*
* @param ?int $modifyTime
* @param ?int $accessTime
* @param ?int $createTime
*/
public function testExtraField(
int $flags,
?int $modifyTime,
?int $accessTime,
?int $createTime,
string $localData,
string $cdData
): void {
$localExtraField = new ExtendedTimestampExtraField($flags, $modifyTime, $accessTime, $createTime);
self::assertSame($localExtraField->getHeaderId(), ExtendedTimestampExtraField::HEADER_ID);
self::assertSame($localExtraField->getFlags(), $flags);
self::assertSame($localExtraField->getModifyTime(), $modifyTime);
self::assertSame($localExtraField->getAccessTime(), $accessTime);
self::assertSame($localExtraField->getCreateTime(), $createTime);
self::assertSame($localExtraField->packLocalFileData(), $localData);
self::assertEquals(ExtendedTimestampExtraField::unpackLocalFileData($localData), $localExtraField);
$extTimeField = ExtendedTimestampExtraField::create($modifyTime, $accessTime, $createTime);
self::assertEquals($extTimeField, $localExtraField);
$accessTime = null;
$createTime = null;
$cdExtraField = new ExtendedTimestampExtraField($flags, $modifyTime, $accessTime, $createTime);
self::assertSame($cdExtraField->getHeaderId(), ExtendedTimestampExtraField::HEADER_ID);
self::assertSame($cdExtraField->getFlags(), $flags);
self::assertSame($cdExtraField->getModifyTime(), $modifyTime);
self::assertSame($cdExtraField->getAccessTime(), $accessTime);
self::assertSame($cdExtraField->getCreateTime(), $createTime);
self::assertSame($cdExtraField->packCentralDirData(), $cdData);
self::assertEquals(ExtendedTimestampExtraField::unpackCentralDirData($cdData), $cdExtraField);
self::assertSame($localExtraField->packCentralDirData(), $cdData);
}
public function provideExtraField(): array
{
return [
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
| ExtendedTimestampExtraField::CREATE_TIME_BIT,
911512006,
911430000,
893709400,
"\x07\xC6\x91T6pQS6X\xECD5",
"\x07\xC6\x91T6",
],
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT,
1492955702,
1492955638,
null,
"\x036\xB2\xFCX\xF6\xB1\xFCX",
"\x036\xB2\xFCX",
],
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT,
1470494391,
null,
null,
"\x01\xB7\xF6\xA5W",
"\x01\xB7\xF6\xA5W",
],
];
}
/**
* @throws \Exception
*/
public function testSetter(): void
{
$mtime = time();
$atime = null;
$ctime = null;
$field = ExtendedTimestampExtraField::create($mtime, $atime, $ctime);
self::assertSame($field->getFlags(), ExtendedTimestampExtraField::MODIFY_TIME_BIT);
self::assertSame($field->getModifyTime(), $mtime);
self::assertEquals($field->getModifyDateTime(), new \DateTimeImmutable('@' . $mtime));
self::assertSame($field->getAccessTime(), $atime);
self::assertSame($field->getCreateTime(), $ctime);
$atime = strtotime('-1 min');
$field->setAccessTime($atime);
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
);
self::assertSame($field->getModifyTime(), $mtime);
self::assertSame($field->getAccessTime(), $atime);
self::assertEquals($field->getAccessDateTime(), new \DateTimeImmutable('@' . $atime));
self::assertSame($field->getCreateTime(), $ctime);
$ctime = strtotime('-1 hour');
$field->setCreateTime($ctime);
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
| ExtendedTimestampExtraField::CREATE_TIME_BIT
);
self::assertSame($field->getModifyTime(), $mtime);
self::assertSame($field->getAccessTime(), $atime);
self::assertSame($field->getCreateTime(), $ctime);
self::assertEquals($field->getCreateDateTime(), new \DateTimeImmutable('@' . $ctime));
$field->setCreateTime(null);
self::assertNull($field->getCreateTime());
self::assertNull($field->getCreateDateTime());
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
);
$field->setAccessTime(null);
self::assertNull($field->getAccessTime());
self::assertNull($field->getAccessDateTime());
self::assertSame($field->getFlags(), ExtendedTimestampExtraField::MODIFY_TIME_BIT);
$field->setModifyTime(null);
self::assertNull($field->getModifyTime());
self::assertNull($field->getModifyDateTime());
self::assertSame($field->getFlags(), 0);
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\JarMarkerExtraField;
/**
* Class JarMarkerExtraFieldTest.
*
* @internal
*
* @small
*/
final class JarMarkerExtraFieldTest extends TestCase
{
/**
* @throws ZipException
*/
public function testExtraField(): void
{
$jarField = new JarMarkerExtraField();
self::assertSame('', $jarField->packLocalFileData());
self::assertSame('', $jarField->packCentralDirData());
self::assertEquals(JarMarkerExtraField::unpackLocalFileData(''), $jarField);
self::assertEquals(JarMarkerExtraField::unpackCentralDirData(''), $jarField);
}
/**
* @throws ZipException
*/
public function testInvalidUnpackLocalData(): void
{
$this->expectException(ZipException::class);
$this->expectExceptionMessage("JarMarker doesn't expect any data");
JarMarkerExtraField::unpackLocalFileData("\x02\x00\00");
}
/**
* @throws ZipException
*/
public function testInvalidUnpackCdData(): void
{
$this->expectException(ZipException::class);
$this->expectExceptionMessage("JarMarker doesn't expect any data");
JarMarkerExtraField::unpackCentralDirData("\x02\x00\00");
}
}

View File

@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
/**
* Class NewUnixExtraFieldTest.
*
* @internal
*
* @small
*/
final class NewUnixExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @throws ZipException
*/
public function testExtraField(int $version, int $uid, int $gid, string $binaryData): void
{
$extraField = new NewUnixExtraField($version, $uid, $gid);
self::assertSame($extraField->getHeaderId(), NewUnixExtraField::HEADER_ID);
self::assertSame($extraField->getVersion(), $version);
self::assertSame($extraField->getGid(), $gid);
self::assertSame($extraField->getUid(), $uid);
self::assertEquals(NewUnixExtraField::unpackLocalFileData($binaryData), $extraField);
self::assertEquals(NewUnixExtraField::unpackCentralDirData($binaryData), $extraField);
self::assertSame($extraField->packLocalFileData(), $binaryData);
self::assertSame($extraField->packCentralDirData(), $binaryData);
}
public function provideExtraField(): array
{
return [
[
1,
NewUnixExtraField::USER_GID_PID,
NewUnixExtraField::USER_GID_PID,
"\x01\x04\xE8\x03\x00\x00\x04\xE8\x03\x00\x00",
],
[
1,
501,
20,
"\x01\x04\xF5\x01\x00\x00\x04\x14\x00\x00\x00",
],
[
1,
500,
495,
"\x01\x04\xF4\x01\x00\x00\x04\xEF\x01\x00\x00",
],
[
1,
11252,
10545,
"\x01\x04\xF4+\x00\x00\x041)\x00\x00",
],
[
1,
1721,
1721,
"\x01\x04\xB9\x06\x00\x00\x04\xB9\x06\x00\x00",
],
];
}
public function testSetter(): void
{
$extraField = new NewUnixExtraField(1, 1000, 1000);
self::assertSame(1, $extraField->getVersion());
self::assertSame(1000, $extraField->getUid());
self::assertSame(1000, $extraField->getGid());
$extraField->setUid(0);
self::assertSame(0, $extraField->getUid());
self::assertSame(1000, $extraField->getGid());
$extraField->setGid(0);
self::assertSame(0, $extraField->getUid());
self::assertSame(0, $extraField->getGid());
}
}

View File

@@ -0,0 +1,233 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
/**
* @internal
*
* @small
*/
final class NtfsExtraFieldTest extends TestCase
{
protected function setUp(): void
{
if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test');
}
}
/**
* @dataProvider provideExtraField
*
* @throws \Exception
*
* @noinspection PhpTooManyParametersInspection
*/
public function testExtraField(
int $modifyNtfsTime,
int $accessNtfsTime,
int $createNtfsTime,
float $modifyTimestamp,
float $accessTimestamp,
float $createTimestamp,
string $binaryData
): void {
$extraField = new NtfsExtraField($modifyNtfsTime, $accessNtfsTime, $createNtfsTime);
self::assertSame($extraField->getHeaderId(), NtfsExtraField::HEADER_ID);
self::assertEquals($extraField->getModifyDateTime()->getTimestamp(), (int) $modifyTimestamp);
self::assertEquals($extraField->getAccessDateTime()->getTimestamp(), (int) $accessTimestamp);
self::assertEquals($extraField->getCreateDateTime()->getTimestamp(), (int) $createTimestamp);
self::assertEquals(NtfsExtraField::unpackLocalFileData($binaryData), $extraField);
self::assertEquals(NtfsExtraField::unpackCentralDirData($binaryData), $extraField);
self::assertSame($extraField->packLocalFileData(), $binaryData);
self::assertSame($extraField->packCentralDirData(), $binaryData);
$extraFieldFromDateTime = NtfsExtraField::create(
$extraField->getModifyDateTime(),
$extraField->getAccessDateTime(),
$extraField->getCreateDateTime()
);
self::assertEqualsIntegerWithDelta(
$extraFieldFromDateTime->getModifyNtfsTime(),
$extraField->getModifyNtfsTime(),
100
);
self::assertEqualsIntegerWithDelta(
$extraFieldFromDateTime->getAccessNtfsTime(),
$extraField->getAccessNtfsTime(),
100
);
self::assertEqualsIntegerWithDelta(
$extraFieldFromDateTime->getCreateNtfsTime(),
$extraField->getCreateNtfsTime(),
100
);
}
public function provideExtraField(): array
{
return [
[
129853553114795379,
129853553114795379,
129853552641022547,
1340881711.4795379,
1340881711.4795379,
1340881664.1022547,
"\x00\x00\x00\x00\x01\x00\x18\x00s\xCD:Z\x1EU\xCD\x01s\xCD:Z\x1EU\xCD\x01S\x9A\xFD=\x1EU\xCD\x01",
],
[
131301570250000000,
131865940850000000,
131840940680000000,
1485683425.000000,
1542120485.000000,
1539620468.000000,
"\x00\x00\x00\x00\x01\x00\x18\x00\x80\xC63\x1D\x15z\xD2\x01\x80@V\xE2_{\xD4\x01\x00\xB2\x15\x14\xA3d\xD4\x01",
],
[
132181086710000000,
132181086710000000,
132181086710000000,
1573635071.000000,
1573635071.000000,
1573635071.000000,
"\x00\x00\x00\x00\x01\x00\x18\x00\x80\xE9_\x7F\xFF\x99\xD5\x01\x80\xE9_\x7F\xFF\x99\xD5\x01\x80\xE9_\x7F\xFF\x99\xD5\x01",
],
];
}
private static function assertEqualsIntegerWithDelta(
int $expected,
int $actual,
int $delta,
string $message = ''
): void {
self::assertSame(
self::roundInt($expected, $delta),
self::roundInt($actual, $delta),
$message
);
}
private static function roundInt(int $number, int $delta): int
{
return (int) (floor($number / $delta) * $delta);
}
/**
* @dataProvider provideExtraField
*
* @noinspection PhpTooManyParametersInspection
*/
public function testConverter(
int $mtimeNtfs,
int $atimeNtfs,
int $ctimeNtfs,
float $mtimeTimestamp,
float $atimeTimestamp,
float $ctimeTimestamp
): void {
self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($mtimeNtfs), $mtimeTimestamp, 0.00001);
self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($atimeNtfs), $atimeTimestamp, 0.00001);
self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($ctimeNtfs), $ctimeTimestamp, 0.00001);
self::assertEqualsIntegerWithDelta(NtfsExtraField::timestampToNtfsTime($mtimeTimestamp), $mtimeNtfs, 10);
self::assertEqualsIntegerWithDelta(NtfsExtraField::timestampToNtfsTime($atimeTimestamp), $atimeNtfs, 10);
self::assertEqualsIntegerWithDelta(NtfsExtraField::timestampToNtfsTime($ctimeTimestamp), $ctimeNtfs, 10);
}
/**
* @throws \Exception
*/
public function testSetter(): void
{
$timeZone = new \DateTimeZone('UTC');
$initDateTime = new \DateTimeImmutable('-1 min', $timeZone);
$mtimeDateTime = new \DateTimeImmutable('-1 hour', $timeZone);
$atimeDateTime = new \DateTimeImmutable('-1 day', $timeZone);
$ctimeDateTime = new \DateTimeImmutable('-1 year', $timeZone);
$extraField = NtfsExtraField::create($initDateTime, $initDateTime, $initDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
$extraField->setModifyDateTime($mtimeDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$mtimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
$extraField->setAccessDateTime($atimeDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$mtimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$atimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
$extraField->setCreateDateTime($ctimeDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$mtimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$atimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$ctimeDateTime->getTimestamp()
);
$newModifyNtfsTime = $extraField->getCreateNtfsTime();
$newAccessNtfsTime = $extraField->getModifyNtfsTime();
$newCreateNtfsTime = $extraField->getAccessNtfsTime();
$extraField->setModifyNtfsTime($newModifyNtfsTime);
$extraField->setAccessNtfsTime($newAccessNtfsTime);
$extraField->setCreateNtfsTime($newCreateNtfsTime);
self::assertSame($extraField->getModifyNtfsTime(), $newModifyNtfsTime);
self::assertSame($extraField->getAccessNtfsTime(), $newAccessNtfsTime);
self::assertSame($extraField->getCreateNtfsTime(), $newCreateNtfsTime);
}
}

View File

@@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Model\Extra\Fields\OldUnixExtraField;
/**
* Class OldUnixExtraFieldTest.
*
* @internal
*
* @small
*/
final class OldUnixExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @noinspection PhpTooManyParametersInspection
*
* @param ?int $accessTime
* @param ?int $modifyTime
* @param ?int $uid
* @param ?int $gid
*
* @throws \Exception
*/
public function testExtraField(
?int $accessTime,
?int $modifyTime,
?int $uid,
?int $gid,
string $localBinaryData,
string $cdBinaryData
): void {
$extraField = new OldUnixExtraField($accessTime, $modifyTime, $uid, $gid);
self::assertSame($extraField->getHeaderId(), OldUnixExtraField::HEADER_ID);
self::assertSame($extraField->getAccessTime(), $accessTime);
self::assertSame($extraField->getModifyTime(), $modifyTime);
self::assertSame($extraField->getUid(), $uid);
self::assertSame($extraField->getGid(), $gid);
if ($extraField->getModifyTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getModifyTime()),
$extraField->getModifyDateTime()
);
}
if ($extraField->getAccessTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getAccessTime()),
$extraField->getAccessDateTime()
);
}
self::assertEquals(OldUnixExtraField::unpackLocalFileData($localBinaryData), $extraField);
self::assertSame($extraField->packLocalFileData(), $localBinaryData);
$uid = null;
$gid = null;
$extraField = new OldUnixExtraField($accessTime, $modifyTime, $uid, $gid);
self::assertSame($extraField->getHeaderId(), OldUnixExtraField::HEADER_ID);
self::assertSame($extraField->getAccessTime(), $accessTime);
self::assertSame($extraField->getModifyTime(), $modifyTime);
self::assertNull($extraField->getUid());
self::assertNull($extraField->getGid());
if ($extraField->getModifyTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getModifyTime()),
$extraField->getModifyDateTime()
);
}
if ($extraField->getAccessTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getAccessTime()),
$extraField->getAccessDateTime()
);
}
self::assertEquals(OldUnixExtraField::unpackCentralDirData($cdBinaryData), $extraField);
self::assertSame($extraField->packCentralDirData(), $cdBinaryData);
}
public function provideExtraField(): array
{
return [
[
1213373265,
1213365834,
502,
502,
"Q\x9BRHJ~RH\xF6\x01\xF6\x01",
"Q\x9BRHJ~RH",
],
[
935520420,
935520401,
501,
100,
"\xA4\xE8\xC27\x91\xE8\xC27\xF5\x01d\x00",
"\xA4\xE8\xC27\x91\xE8\xC27",
],
[
1402666135,
1402666135,
501,
20,
"\x97\xFC\x9AS\x97\xFC\x9AS\xF5\x01\x14\x00",
"\x97\xFC\x9AS\x97\xFC\x9AS",
],
[
null,
null,
null,
null,
'',
'',
],
];
}
public function testSetter(): void
{
$extraField = new OldUnixExtraField(null, null, null, null);
self::assertNull($extraField->getAccessTime());
self::assertNull($extraField->getAccessDateTime());
self::assertNull($extraField->getModifyTime());
self::assertNull($extraField->getModifyDateTime());
self::assertNull($extraField->getUid());
self::assertNull($extraField->getGid());
$extraField->setModifyTime(1402666135);
self::assertSame($extraField->getModifyTime(), 1402666135);
$extraField->setAccessTime(1213365834);
self::assertSame($extraField->getAccessTime(), 1213365834);
$extraField->setUid(500);
self::assertSame($extraField->getUid(), 500);
$extraField->setGid(100);
self::assertSame($extraField->getGid(), 100);
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PhpZip\Model\Extra\Fields\UnicodeCommentExtraField;
/**
* @internal
*
* @small
*/
final class UnicodeCommentExtraFieldTest extends AbstractUnicodeExtraFieldTest
{
/**
* {@inheritDoc}
*/
protected function getUnicodeExtraFieldClassName()
{
return UnicodeCommentExtraField::class;
}
public function provideExtraField(): array
{
return [
[
4293813303,
'комментарий',
"\xAA\xAE\xAC\xAC\xA5\xAD\xE2\xA0\xE0\xA8\xA9",
"\x017d\xEE\xFF\xD0\xBA\xD0\xBE\xD0\xBC\xD0\xBC\xD0\xB5\xD0\xBD\xD1\x82\xD0\xB0\xD1\x80\xD0\xB8\xD0\xB9",
],
[
897024324,
'תגובה',
"\x9A\x82\x85\x81\x84",
"\x01D\x81w5\xD7\xAA\xD7\x92\xD7\x95\xD7\x91\xD7\x94",
],
];
}
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PhpZip\Model\Extra\Fields\UnicodePathExtraField;
/**
* Class UnicodePathExtraFieldTest.
*
* @internal
*
* @small
*/
final class UnicodePathExtraFieldTest extends AbstractUnicodeExtraFieldTest
{
/**
* {@inheritDoc}
*/
protected function getUnicodeExtraFieldClassName()
{
return UnicodePathExtraField::class;
}
public function provideExtraField(): array
{
return [
[
2728523760,
'txt\מבחן עברי.txt',
"txt/\x8E\x81\x87\x8F \x92\x81\x98\x89.txt",
"\x01\xF0\xF7\xA1\xA2txt\\\xD7\x9E\xD7\x91\xD7\x97\xD7\x9F \xD7\xA2\xD7\x91\xD7\xA8\xD7\x99.txt",
],
[
953311492,
'ä\ü.txt',
"\x84/\x81.txt",
"\x01\x04a\xD28\xC3\xA4\\\xC3\xBC.txt",
],
[
2965532848,
'Ölfässer.txt',
"\x99lf\x84sser.txt",
"\x01\xB0p\xC2\xB0\xC3\x96lf\xC3\xA4sser.txt",
],
[
3434671236,
'Как заработать в интернете.mp4',
"\x8A\xA0\xAA \xA7\xA0\xE0\xA0\xA1\xAE\xE2\xA0\xE2\xEC \xA2 \xA8\xAD\xE2\xA5\xE0\xAD\xA5\xE2\xA5.mp4",
"\x01\x84\xEC\xB8\xCC\xD0\x9A\xD0\xB0\xD0\xBA \xD0\xB7\xD0\xB0\xD1\x80\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x82\xD0\xB0\xD1\x82\xD1\x8C \xD0\xB2 \xD0\xB8\xD0\xBD\xD1\x82\xD0\xB5\xD1\x80\xD0\xBD\xD0\xB5\xD1\x82\xD0\xB5.mp4",
],
];
}
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\RuntimeException;
use PhpZip\Model\Extra\Fields\UnrecognizedExtraField;
/**
* Class UnrecognizedExtraFieldTest.
*
* @internal
*
* @small
*/
final class UnrecognizedExtraFieldTest extends TestCase
{
public function testExtraField(): void
{
$headerId = 0xF00D;
$binaryData = "\x01\x02\x03\x04\x05";
$unrecognizedExtraField = new UnrecognizedExtraField($headerId, $binaryData);
self::assertSame($unrecognizedExtraField->getHeaderId(), $headerId);
self::assertSame($unrecognizedExtraField->getData(), $binaryData);
$newHeaderId = 0xDADA;
$newBinaryData = "\x05\x00";
$unrecognizedExtraField->setHeaderId($newHeaderId);
self::assertSame($unrecognizedExtraField->getHeaderId(), $newHeaderId);
$unrecognizedExtraField->setData($newBinaryData);
self::assertSame($unrecognizedExtraField->getData(), $newBinaryData);
self::assertSame($unrecognizedExtraField->packLocalFileData(), $newBinaryData);
self::assertSame($unrecognizedExtraField->packCentralDirData(), $newBinaryData);
}
public function testUnpackLocalData(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Unsupport parse');
UnrecognizedExtraField::unpackLocalFileData("\x01\x02");
}
public function testUnpackCentralDirData(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Unsupport parse');
UnrecognizedExtraField::unpackCentralDirData("\x01\x02");
}
}

View File

@@ -0,0 +1,237 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipEncryptionMethod;
use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\ZipException;
use PhpZip\Exception\ZipUnsupportMethodException;
use PhpZip\Model\Extra\Fields\WinZipAesExtraField;
/**
* @internal
*
* @small
*/
final class WinZipAesExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @throws ZipException
* @throws ZipUnsupportMethodException
*/
public function testExtraField(
int $vendorVersion,
int $keyStrength,
int $compressionMethod,
int $saltSize,
string $binaryData
): void {
$extraField = new WinZipAesExtraField($vendorVersion, $keyStrength, $compressionMethod);
self::assertSame($extraField->getHeaderId(), WinZipAesExtraField::HEADER_ID);
self::assertSame($extraField->getVendorVersion(), $vendorVersion);
self::assertSame($extraField->getKeyStrength(), $keyStrength);
self::assertSame($extraField->getCompressionMethod(), $compressionMethod);
self::assertSame($extraField->getVendorId(), WinZipAesExtraField::VENDOR_ID);
self::assertSame($extraField->getSaltSize(), $saltSize);
self::assertSame($binaryData, $extraField->packLocalFileData());
self::assertSame($binaryData, $extraField->packCentralDirData());
self::assertEquals(WinZipAesExtraField::unpackLocalFileData($binaryData), $extraField);
self::assertEquals(WinZipAesExtraField::unpackCentralDirData($binaryData), $extraField);
}
public function provideExtraField(): array
{
return [
[
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_128BIT,
ZipCompressionMethod::STORED,
8,
"\x01\x00AE\x01\x00\x00",
],
[
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::DEFLATED,
12,
"\x01\x00AE\x02\x08\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_128BIT,
ZipCompressionMethod::DEFLATED,
8,
"\x02\x00AE\x01\x08\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_256BIT,
ZipCompressionMethod::STORED,
16,
"\x02\x00AE\x03\x00\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::DEFLATED,
12,
"\x02\x00AE\x02\x08\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_256BIT,
ZipCompressionMethod::STORED,
16,
"\x02\x00AE\x03\x00\x00",
],
];
}
/**
* @throws ZipUnsupportMethodException
*/
public function testSetter(): void
{
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_256BIT,
ZipCompressionMethod::DEFLATED
);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE1);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_256BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 16);
self::assertSame($extraField->getEncryptionStrength(), 256);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_256);
$extraField->setVendorVersion(WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_256BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 16);
self::assertSame($extraField->getEncryptionStrength(), 256);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_256);
$extraField->setKeyStrength(WinZipAesExtraField::KEY_STRENGTH_128BIT);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_128BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 8);
self::assertSame($extraField->getEncryptionStrength(), 128);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_128);
$extraField->setKeyStrength(WinZipAesExtraField::KEY_STRENGTH_192BIT);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_192BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 12);
self::assertSame($extraField->getEncryptionStrength(), 192);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_192);
$extraField->setCompressionMethod(ZipCompressionMethod::STORED);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_192BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::STORED);
self::assertSame($extraField->getSaltSize(), 12);
self::assertSame($extraField->getEncryptionStrength(), 192);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_192);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testConstructUnsupportVendorVersion(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3');
new WinZipAesExtraField(
3,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::STORED
);
}
public function testSetterUnsupportVendorVersion(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3');
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::STORED
);
$extraField->setVendorVersion(3);
}
public function testConstructUnsupportCompressionMethod(): void
{
$this->expectException(ZipUnsupportMethodException::class);
$this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
3
);
}
public function testSetterUnsupportCompressionMethod(): void
{
$this->expectException(ZipUnsupportMethodException::class);
$this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.');
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::STORED
);
$extraField->setCompressionMethod(3);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testConstructUnsupportKeyStrength(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
0x10,
ZipCompressionMethod::STORED
);
}
/**
* @throws ZipException
*/
public function testSetterUnsupportKeyStrength(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
0x10,
ZipCompressionMethod::STORED
);
}
}

View File

@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Constants\ZipConstants;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\Zip64ExtraField;
use PhpZip\Model\ZipEntry;
/**
* @internal
*
* @small
*/
final class Zip64ExtraFieldTest extends TestCase
{
protected function setUp(): void
{
if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test');
}
}
/**
* @dataProvider provideExtraField
*
* @noinspection PhpTooManyParametersInspection
*
* @param ?int $uncompressedSize
* @param ?int $compressedSize
* @param ?int $localHeaderOffset
* @param ?int $diskStart
* @param ?string $localBinaryData
* @param ?string $cdBinaryData
*
* @throws ZipException
*/
public function testExtraField(
?int $uncompressedSize,
?int $compressedSize,
?int $localHeaderOffset,
?int $diskStart,
?string $localBinaryData,
?string $cdBinaryData
): void {
$extraField = new Zip64ExtraField(
$uncompressedSize,
$compressedSize,
$localHeaderOffset,
$diskStart
);
self::assertSame($extraField->getHeaderId(), Zip64ExtraField::HEADER_ID);
self::assertSame($extraField->getUncompressedSize(), $uncompressedSize);
self::assertSame($extraField->getCompressedSize(), $compressedSize);
self::assertSame($extraField->getLocalHeaderOffset(), $localHeaderOffset);
self::assertSame($extraField->getDiskStart(), $diskStart);
$zipEntry = new ZipEntry('entry');
$zipEntry->setUncompressedSize($uncompressedSize !== null ? ZipConstants::ZIP64_MAGIC : 0xFFFFF);
$zipEntry->setCompressedSize($compressedSize !== null ? ZipConstants::ZIP64_MAGIC : 0xFFFF);
$zipEntry->setLocalHeaderOffset($localHeaderOffset !== null ? ZipConstants::ZIP64_MAGIC : 0xFFF);
if ($localBinaryData !== null) {
self::assertSame($localBinaryData, $extraField->packLocalFileData());
self::assertEquals(Zip64ExtraField::unpackLocalFileData($localBinaryData, $zipEntry), $extraField);
}
if ($cdBinaryData !== null) {
self::assertSame($cdBinaryData, $extraField->packCentralDirData());
self::assertEquals(Zip64ExtraField::unpackCentralDirData($cdBinaryData, $zipEntry), $extraField);
}
}
public function provideExtraField(): array
{
return [
[
0,
2,
null,
null,
"\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00",
null,
],
[
5368709120,
5369580144,
null,
null,
null,
"\x00\x00\x00@\x01\x00\x00\x00pJ\x0D@\x01\x00\x00\x00",
],
[
null,
null,
4945378839,
null,
null,
"\x17~\xC4&\x01\x00\x00\x00",
],
];
}
public function testSetter(): void
{
$extraField = new Zip64ExtraField();
self::assertNull($extraField->getUncompressedSize());
self::assertNull($extraField->getCompressedSize());
self::assertNull($extraField->getLocalHeaderOffset());
self::assertNull($extraField->getDiskStart());
$uncompressedSize = 12222;
$extraField->setUncompressedSize($uncompressedSize);
self::assertSame($extraField->getUncompressedSize(), $uncompressedSize);
$compressedSize = 12222;
$extraField->setCompressedSize($uncompressedSize);
self::assertSame($extraField->getCompressedSize(), $compressedSize);
$localHeaderOffset = 12222;
$extraField->setLocalHeaderOffset($localHeaderOffset);
self::assertSame($extraField->getLocalHeaderOffset(), $localHeaderOffset);
$diskStart = 2;
$extraField->setDiskStart($diskStart);
self::assertSame($extraField->getDiskStart(), $diskStart);
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\IO\ZipWriter;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
class CustomZipWriter extends ZipWriter
{
protected function beforeWrite(): void
{
parent::beforeWrite();
$now = new \DateTimeImmutable();
$ntfsTimeExtra = NtfsExtraField::create($now, $now->modify('-1 day'), $now->modify('-10 day'));
$unixExtra = new NewUnixExtraField();
foreach ($this->zipContainer->getEntries() as $entry) {
$entry->addExtraField($ntfsTimeExtra);
$entry->addExtraField($unixExtra);
}
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\IO\ZipWriter;
use PhpZip\ZipFile;
class ZipFileCustomWriter extends ZipFile
{
protected function createZipWriter(): ZipWriter
{
return new CustomZipWriter($this->zipContainer);
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\ZipFile;
class ZipFileWithBeforeSave extends ZipFile
{
/**
* Event before save or output.
*/
protected function onBeforeSave(): void
{
$now = new \DateTimeImmutable();
$ntfsTimeExtra = NtfsExtraField::create($now, $now->modify('-1 day'), $now->modify('-10 day'));
$unixExtra = new NewUnixExtraField();
foreach ($this->zipContainer->getEntries() as $entry) {
$entry->addExtraField($ntfsTimeExtra);
$entry->addExtraField($unixExtra);
}
}
}

View File

@@ -1,5 +1,14 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal;
/**
@@ -18,29 +27,27 @@ class DummyFileSystemStream
* This method is called immediately after the wrapper is
* initialized (f.e. by {@see fopen()} and {@see file_get_contents()}).
*
* @param string $path specifies the URL that was passed to
* the original function
* @param string $mode the mode used to open the file, as detailed
* for {@see fopen()}
* @param int $options Holds additional flags set by the streams
* API. It can hold one or more of the
* following values OR'd together.
* @param string $opened_path if the path is opened successfully, and
* STREAM_USE_PATH is set in options,
* opened_path should be set to the
* full path of the file/resource that
* was actually opened
*
* @return bool
* @param string $path specifies the URL that was passed to
* the original function
* @param string $mode the mode used to open the file, as detailed
* for {@see fopen()}
* @param int $options Holds additional flags set by the streams
* API. It can hold one or more of the
* following values OR'd together.
* @param string|null $opened_path if the path is opened successfully, and
* STREAM_USE_PATH is set in options,
* opened_path should be set to the
* full path of the file/resource that
* was actually opened
*
* @see https://www.php.net/streamwrapper.stream-open
*
* @noinspection PhpUsageOfSilenceOperatorInspection
*/
public function stream_open($path, $mode, $options, &$opened_path)
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
{
$parsedUrl = parse_url($path);
$uri = str_replace('//', '/', $parsedUrl['path']);
$uri = substr($parsedUrl['path'], 1);
$this->fp = @fopen($uri, $mode);
return $this->fp !== false;
@@ -64,7 +71,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-read
*/
public function stream_read($count)
public function stream_read(int $count)
{
return fread($this->fp, $count);
}
@@ -86,7 +93,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-seek
*/
public function stream_seek($offset, $whence = \SEEK_SET)
public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
{
return fseek($this->fp, $offset, $whence) === 0;
}
@@ -101,7 +108,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-tell
*/
public function stream_tell()
public function stream_tell(): int
{
$pos = ftell($this->fp);
@@ -123,7 +130,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-eof
*/
public function stream_eof()
public function stream_eof(): bool
{
return feof($this->fp);
}
@@ -133,13 +140,11 @@ class DummyFileSystemStream
*
* This method is called in response to {@see fstat()}.
*
* @return array
*
* @see https://www.php.net/streamwrapper.stream-stat
* @see https://www.php.net/stat
* @see https://www.php.net/fstat
*/
public function stream_stat()
public function stream_stat(): array
{
return fstat($this->fp);
}
@@ -159,7 +164,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-flush
*/
public function stream_flush()
public function stream_flush(): bool
{
return fflush($this->fp);
}
@@ -175,9 +180,9 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-truncate
*/
public function stream_truncate($new_size)
public function stream_truncate(int $new_size): bool
{
return ftruncate($this->fp, (int) $new_size);
return ftruncate($this->fp, $new_size);
}
/**
@@ -194,7 +199,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-write
*/
public function stream_write($data)
public function stream_write(string $data): int
{
$bytes = fwrite($this->fp, $data);
@@ -209,7 +214,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-close
*/
public function stream_close()
public function stream_close(): void
{
fclose($this->fp);
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\Epub;
use PhpZip\Constants\ZipPlatform;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\IO\ZipReader;
use PhpZip\IO\ZipWriter;
use PhpZip\Model\ImmutableZipContainer;
use PhpZip\Model\ZipContainer;
use PhpZip\Model\ZipEntry;
use PhpZip\ZipFile;
/**
* Class EpubFile.
*
* @property EpubZipContainer $zipContainer
*/
class EpubFile extends ZipFile
{
protected function createZipWriter(): ZipWriter
{
return new EpubWriter($this->zipContainer);
}
/**
* @param resource $inputStream
*/
protected function createZipReader($inputStream, array $options = []): ZipReader
{
return new EpubReader($inputStream, $options);
}
protected function createZipContainer(?ImmutableZipContainer $sourceContainer = null): ZipContainer
{
return new EpubZipContainer($sourceContainer);
}
protected function addZipEntry(ZipEntry $zipEntry): void
{
$zipEntry->setCreatedOS(ZipPlatform::OS_DOS);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
parent::addZipEntry($zipEntry);
}
/**
* @throws ZipEntryNotFoundException
*/
public function getMimeType(): string
{
return $this->zipContainer->getMimeType();
}
/**
* @throws ZipException
* @throws ZipEntryNotFoundException
*/
public function getEpubInfo(): EpubInfo
{
return new EpubInfo($this->getEntryContents($this->getRootFile()));
}
/**
* @throws ZipException
*/
public function getRootFile(): string
{
$entryName = 'META-INF/container.xml';
$contents = $this->getEntryContents($entryName);
$doc = new \DOMDocument();
$doc->loadXML($contents);
$xpath = new \DOMXPath($doc);
$rootFile = $xpath->evaluate('string(//@full-path)');
if ($rootFile === '') {
throw new ZipException('Incorrect ' . $entryName . ' file format');
}
return $rootFile;
}
}

View File

@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\Epub;
use PhpZip\Exception\ZipException;
/**
* Class EpubInfo.
*
* @see http://idpf.org/epub/30/spec/epub30-publications.html
*/
class EpubInfo
{
private ?string $title;
private ?string $creator;
private ?string $language;
private ?string $publisher;
private ?string $description;
private ?string $rights;
private ?string $date;
private ?string $subject;
/**
* EpubInfo constructor.
*
* @param $xmlContents
*
* @throws ZipException
*/
public function __construct($xmlContents)
{
$doc = new \DOMDocument();
$doc->loadXML($xmlContents);
$xpath = new \DOMXpath($doc);
$xpath->registerNamespace('root', 'http://www.idpf.org/2007/opf');
$metaDataNodeList = $xpath->query('//root:metadata');
if (\count($metaDataNodeList) !== 1) {
throw new ZipException('Invalid .opf file format');
}
$metaDataNode = $metaDataNodeList->item(0);
$title = $xpath->evaluate('string(//dc:title)', $metaDataNode);
$creator = $xpath->evaluate('string(//dc:creator)', $metaDataNode);
$language = $xpath->evaluate('string(//dc:language)', $metaDataNode);
$publisher = $xpath->evaluate('string(//dc:publisher)', $metaDataNode);
$description = $xpath->evaluate('string(//dc:description)', $metaDataNode);
$rights = $xpath->evaluate('string(//dc:rights)', $metaDataNode);
$date = $xpath->evaluate('string(//dc:date)', $metaDataNode);
$subject = $xpath->evaluate('string(//dc:subject)', $metaDataNode);
$this->title = empty($title) ? null : $title;
$this->creator = empty($creator) ? null : $creator;
$this->language = empty($language) ? null : $language;
$this->publisher = empty($publisher) ? null : $publisher;
$this->description = empty($description) ? null : $description;
$this->rights = empty($rights) ? null : $rights;
$this->date = empty($date) ? null : $date;
$this->subject = empty($subject) ? null : $subject;
}
public function getTitle(): ?string
{
return $this->title;
}
public function getCreator(): ?string
{
return $this->creator;
}
public function getLanguage(): ?string
{
return $this->language;
}
public function getPublisher(): ?string
{
return $this->publisher;
}
public function getDescription(): ?string
{
return $this->description;
}
public function getRights(): ?string
{
return $this->rights;
}
public function getDate(): ?string
{
return $this->date;
}
public function getSubject(): ?string
{
return $this->subject;
}
public function toArray(): array
{
return [
'title' => $this->title,
'creator' => $this->creator,
'language' => $this->language,
'publisher' => $this->publisher,
'description' => $this->description,
'rights' => $this->rights,
'date' => $this->date,
'subject' => $this->subject,
];
}
}

Some files were not shown because too many files have changed in this diff Show More