1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-08-17 12:43:05 +02:00

31 Commits

Author SHA1 Message Date
Ne-Lexa
c9130665cd fix test 2021-05-21 09:02:22 +03:00
Ne-Lexa
ac3533dece fix test 2021-05-16 23:40:27 +03:00
Ne-Lexa
bb1a607d0d fix test 2021-05-16 23:28:33 +03:00
Ne-Lexa
8954da9e32 fix test for Windows
issue #78
2021-05-16 23:20:34 +03:00
Ne-Lexa
c3523992ed fix test
issue #78
2021-05-16 23:09:44 +03:00
Ne-Lexa
495dd47df4 updated phpunit config
issue #78
2021-05-16 22:56:05 +03:00
Ne-Lexa
b126252266 removed platform config from composer
issue #78
2021-05-16 22:41:26 +03:00
Ne-Lexa
584784c119 php 7.1 - 8.0 compatible version
issue #78
2021-05-16 12:18:23 +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
501b52f6fc Merge branch 'release/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
78 changed files with 1978 additions and 1184 deletions

3
.gitattributes vendored
View File

@@ -1,8 +1,7 @@
.gitattributes export-ignore
.github export-ignore
.gitignore export-ignore
.php_cs export-ignore
.travis.yml export-ignore
.php-cs-fixer.php export-ignore
bootstrap.php export-ignore
phpunit.xml export-ignore
tests export-ignore

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

@@ -0,0 +1,130 @@
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, gd
key: cache-v2
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.1'
- '7.2'
- '7.3'
- '7.4'
- '8.0'
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 zipalign
-
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 == '8.0'
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 == '8.0'
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: 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

View File

@@ -1,16 +1,18 @@
<?php
declare(strict_types=1);
/**
* PHP Code Style Fixer (config created for version 2.16.1 (Yellow Bird)).
* PHP Code Style Fixer (config created for version 3.0.0 (Constitution)).
*
* Use one of the following console commands to just see the
* changes that will be made.
* - `php-cs-fixer fix --config='.php_cs' --diff-format udiff --dry-run`
* - `php '.php_cs'`
* - `php-cs-fixer fix --config='.php-cs-fixer.php' --dry-run`
* - `php '.php-cs-fixer.php'`
*
* Use one of the following console commands to fix PHP code:
* - `php-cs-fixer fix --config='.php_cs' --diff-format udiff`
* - `php '.php_cs' --force`
* - `php-cs-fixer fix --config='.php-cs-fixer.php'
* - `php '.php-cs-fixer.php' --force`
*
* @see https://cs.symfony.com/
*/
@@ -24,6 +26,14 @@ $rules = [
// Each element of an array must be indented exactly once.
'array_indentation' => true,
/*
* Converts simple usages of `array_push($x, $y);` to `$x[] = $y;`.
*
* Risky!
* Risky when the function `array_push` is overridden.
*/
'array_push' => true,
// PHP arrays should be declared using the configured syntax.
'array_syntax' => [
'syntax' => 'short',
@@ -51,23 +61,7 @@ $rules = [
'blank_line_after_opening_tag' => true,
// An empty line feed must precede any configured statement.
'blank_line_before_statement' => [
'statements' => [
'continue',
'declare',
'return',
'throw',
'try',
'case',
'die',
'exit',
'do',
'foreach',
'goto',
'if',
'while',
],
],
'blank_line_before_statement' => true,
/*
* The body of each structure MUST be enclosed by braces. Braces
@@ -82,8 +76,8 @@ $rules = [
'cast_spaces' => true,
/*
* Class, trait and interface elements must be separated with one
* blank line.
* Class, trait and interface elements must be separated with one or
* none blank line.
*/
'class_attributes_separation' => true,
@@ -98,6 +92,9 @@ $rules = [
// Converts `::class` keywords to FQCN strings.
'class_keyword_remove' => false,
// Namespace must not contain spacing, comments or PHPDoc.
'clean_namespace' => true,
// Using `isset($var) &&` multiple times should be done in one call.
'combine_consecutive_issets' => true,
@@ -111,7 +108,7 @@ $rules = [
* Risky!
* Risky when the function `dirname` is overridden.
*/
'combine_nested_dirname' => false,
'combine_nested_dirname' => true,
/*
* Comments with annotation should be docblock when used on
@@ -119,7 +116,7 @@ $rules = [
*
* Risky!
* Risky as new docblocks might mean more, e.g. a Doctrine entity
* might have a new column in database
* might have a new column in database.
*/
'comment_to_phpdoc' => [
'ignored_tags' => [
@@ -132,7 +129,7 @@ $rules = [
*
* Rule is applied only in a PHP 7.1+ environment.
*/
'compact_nullable_typehint' => false,
'compact_nullable_typehint' => true,
// Concatenation should be spaced according configuration.
'concat_space' => [
@@ -181,7 +178,9 @@ $rules = [
* Doctrine annotations must use configured operator for assignment
* in arrays.
*/
'doctrine_annotation_array_assignment' => true,
'doctrine_annotation_array_assignment' => [
'operator' => ':',
],
/*
* Doctrine annotations without arguments must use the configured
@@ -200,7 +199,15 @@ $rules = [
* space around named arguments assignment operator; there must be
* one space around array assignment operator.
*/
'doctrine_annotation_spaces' => true,
'doctrine_annotation_spaces' => [
'before_array_assignments_colon' => false,
],
/*
* Replaces short-echo `<?=` with long format `<?php echo`/`<?php
* print` syntax, or vice-versa.
*/
'echo_tag_syntax' => true,
/*
* The keyword `elseif` should be used instead of `else if` so that
@@ -238,6 +245,7 @@ $rules = [
'iconv',
'mime_content_type',
'rename',
'rmdir',
'unlink',
],
],
@@ -269,7 +277,7 @@ $rules = [
* Add curly braces to indirect variables to make them clear to
* understand. Requires PHP >= 7.0.
*/
'explicit_indirect_variable' => false,
'explicit_indirect_variable' => true,
/*
* Converts implicit variables into explicit ones in double-quoted
@@ -283,7 +291,7 @@ $rules = [
* - Explicit syntax allows word concatenation inside strings, e.g.
* `"${var}IsAVar"`, implicit doesn't
* - Explicit syntax is easier to detect for IDE/editors and
* therefore has colors/hightlight with higher contrast, which is
* therefore has colors/highlight with higher contrast, which is
* easier to read
* Backtick operator is skipped because it is harder to handle; you
* can use `backtick_to_shell_exec` fixer to normalize backticks to
@@ -327,13 +335,10 @@ $rules = [
* want to override a method, use the Template method pattern.
*
* Risky!
* Risky when overriding `public` methods of `abstract` classes
* Risky when overriding `public` methods of `abstract` classes.
*/
'final_public_method_for_abstract_class' => false,
// Converts `static` access to `self` access in `final` classes.
'final_static_access' => true,
/*
* Order the flags in `fopen` calls, `b` and `t` must be last.
*
@@ -376,15 +381,7 @@ $rules = [
* Risky when any of the configured functions to replace are
* overridden.
*/
'function_to_constant' => [
'functions' => [
'get_class',
'php_sapi_name',
'phpversion',
'pi',
'get_called_class',
],
],
'function_to_constant' => true,
// Ensure single space between function's argument and its typehint.
'function_typehint_space' => true,
@@ -392,6 +389,13 @@ $rules = [
// Configured annotations should be omitted from PHPDoc.
'general_phpdoc_annotation_remove' => true,
// Renames PHPDoc tags.
'general_phpdoc_tag_rename' => [
'replacements' => [
'inheritDocs' => 'inheritDoc',
],
],
// Imports or fully qualifies global classes/functions/constants.
'global_namespace_import' => [
'import_constants' => false,
@@ -399,6 +403,9 @@ $rules = [
'import_classes' => false,
],
// There MUST be group use for the same namespaces.
'group_import' => false,
// Add, replace or remove header comment.
'header_comment' => false,
@@ -443,6 +450,9 @@ $rules = [
*/
'is_null' => true,
// Lambda must not import variables it doesn't use.
'lambda_not_used_import' => true,
// All PHP files must use same line ending.
'line_ending' => true,
@@ -588,6 +598,9 @@ $rules = [
],
],
// Master language constructs shall be used instead of aliases.
'no_alias_language_construct_call' => true,
// Replace control structure alternative syntax to use braces.
'no_alternative_syntax' => true,
@@ -629,30 +642,14 @@ $rules = [
// There should not be empty PHPDoc blocks.
'no_empty_phpdoc' => true,
// Remove useless semicolon statements.
// Remove useless (semicolon) statements.
'no_empty_statement' => true,
/*
* Removes extra blank lines and/or blank lines following
* configuration.
*/
'no_extra_blank_lines' => [
'tokens' => [
'extra',
'case',
'continue',
'default',
'curly_brace_block',
'parenthesis_brace_block',
'return',
'square_brace_block',
'use',
'throw',
'use_trait',
'useTrait',
'switch',
],
],
'no_extra_blank_lines' => true,
/*
* Replace accidental usage of homoglyphs (non ascii characters) in
@@ -700,9 +697,6 @@ $rules = [
*/
'no_short_bool_cast' => true,
// Replace short-echo `<?=` with long format `<?php echo` syntax.
'no_short_echo_tag' => false,
// Single-line whitespace before closing semicolon are prohibited.
'no_singleline_whitespace_before_semicolons' => true,
@@ -725,8 +719,8 @@ $rules = [
'no_superfluous_elseif' => true,
/*
* Removes `@param` and `@return` tags that don't provide any useful
* information.
* Removes `@param`, `@return` and `@var` tags that don't provide
* any useful information.
*/
'no_superfluous_phpdoc_tags' => false,
@@ -742,16 +736,44 @@ $rules = [
// There MUST be no trailing spaces inside comment or PHPDoc.
'no_trailing_whitespace_in_comment' => true,
/*
* There must be no trailing whitespace in strings.
*
* Risky!
* Changing the whitespaces in strings might affect string
* comparisons and outputs.
*/
'no_trailing_whitespace_in_string' => false,
// Removes unneeded parentheses around control statements.
'no_unneeded_control_parentheses' => true,
'no_unneeded_control_parentheses' => [
'statements' => [
'break',
'clone',
'continue',
'echo_print',
'return',
'switch_case',
'yield',
'yield_from',
],
],
/*
* Removes unneeded curly braces that are superfluous and aren't
* part of a control structure's body.
*/
'no_unneeded_curly_braces' => true,
'no_unneeded_curly_braces' => [
'namespaces' => true,
],
// A final class must not have final methods.
/*
* A `final` class must not have `final` methods and `private`
* methods must not be `final`.
*
* Risky!
* Risky when child class overrides a `private` method.
*/
'no_unneeded_final_method' => true,
/*
@@ -772,12 +794,14 @@ $rules = [
* Properties should be set to `null` instead of using `unset`.
*
* Risky!
* Changing variables to `null` instead of unsetting them will mean
* they still show up when looping over class variables. With PHP
* 7.4, this rule might introduce `null` assignments to property
* whose type declaration does not allow it.
* Risky when relying on attributes to be removed using `unset`
* rather than be set to `null`. Changing variables to `null`
* instead of unsetting means these still show up when looping over
* class variables and reference properties remain unbroken. With
* PHP 7.4, this rule might introduce `null` assignments to
* properties whose type declaration does not allow it.
*/
'no_unset_on_property' => false,
'no_unset_on_property' => true,
// Unused `use` statements must be removed.
'no_unused_imports' => true,
@@ -791,6 +815,14 @@ $rules = [
*/
'no_useless_return' => true,
/*
* There must be no `sprintf` calls with only the first argument.
*
* Risky!
* Risky when if the `sprintf` function is overridden.
*/
'no_useless_sprintf' => true,
/*
* In array declaration, there MUST NOT be a whitespace before each
* comma.
@@ -830,13 +862,33 @@ $rules = [
'nullable_type_declaration_for_default_null_value' => false,
/*
* There should not be space before or after object
* `T_OBJECT_OPERATOR` `->`.
* There should not be space before or after object operators `->`
* and `?->`.
*/
'object_operator_without_whitespace' => true,
/*
* Operators - when multiline - must always be at the beginning or
* at the end of the line.
*/
'operator_linebreak' => true,
// Orders the elements of classes/interfaces/traits.
'ordered_class_elements' => false,
'ordered_class_elements' => [
'order' => [
'use_trait',
'constant_public',
'constant_protected',
'constant_private',
'property_public',
'property_protected',
'property_private',
'construct',
'destruct',
'magic',
'phpunit',
],
],
// Ordering `use` statements.
'ordered_imports' => [
@@ -859,6 +911,14 @@ $rules = [
*/
'ordered_interfaces' => false,
/*
* Trait `use` statements must be sorted alphabetically.
*
* Risky!
* Risky when depending on order of the imports.
*/
'ordered_traits' => false,
/*
* PHPUnit assertion method calls like `->assertSame(true, $foo)`
* should be written with dedicated method like
@@ -878,9 +938,7 @@ $rules = [
* Fixer could be risky if one is overriding PHPUnit's native
* methods.
*/
'php_unit_dedicate_assert' => [
'target' => '3.5',
],
'php_unit_dedicate_assert' => true,
/*
* PHPUnit assertions like `assertIsArray` should be used over
@@ -890,7 +948,7 @@ $rules = [
* Risky when PHPUnit methods are overridden or when project has
* PHPUnit incompatibilities.
*/
'php_unit_dedicate_assert_internal_type' => false,
'php_unit_dedicate_assert_internal_type' => true,
/*
* Usages of `->setExpectedException*` methods MUST be replaced by
@@ -900,7 +958,9 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or
* when project has PHPUnit incompatibilities.
*/
'php_unit_expectation' => false,
'php_unit_expectation' => [
'target' => '5.6',
],
// PHPUnit annotations should be a FQCNs including a root namespace.
'php_unit_fqcn_annotation' => true,
@@ -923,7 +983,7 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or
* when project has PHPUnit incompatibilities.
*/
'php_unit_mock' => false,
'php_unit_mock' => true,
/*
* Usage of PHPUnit's mock e.g. `->will($this->returnValue(..))`
@@ -934,7 +994,7 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or
* when project has PHPUnit incompatibilities.
*/
'php_unit_mock_short_will_return' => false,
'php_unit_mock_short_will_return' => true,
/*
* PHPUnit classes MUST be used in namespaced version, e.g.
@@ -955,9 +1015,7 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or
* when project has PHPUnit incompatibilities.
*/
'php_unit_namespaced' => [
'target' => '4.8',
],
'php_unit_namespaced' => true,
/*
* Usages of `@expectedException*` annotations MUST be replaced by
@@ -971,9 +1029,6 @@ $rules = [
'target' => 'newest',
],
// Order `@covers` annotation of PHPUnit tests.
'php_unit_ordered_covers' => true,
/*
* Changes the visibility of the `setUp()` and `tearDown()`
* functions of PHPUnit to `protected`, to match the PHPUnit
@@ -1040,18 +1095,7 @@ $rules = [
* All items of the given phpdoc tags must be either left-aligned or
* (by default) aligned vertically.
*/
'phpdoc_align' => [
'tags' => [
'return',
'throws',
'type',
'var',
'property',
'method',
'param',
],
'align' => 'vertical',
],
'phpdoc_align' => true,
// PHPDoc annotation descriptions should not be a sentence.
'phpdoc_annotation_without_dot' => true,
@@ -1062,8 +1106,8 @@ $rules = [
*/
'phpdoc_indent' => true,
// Fix PHPDoc inline tags, make `@inheritdoc` always inline.
'phpdoc_inline_tag' => true,
// Fixes PHPDoc inline tags.
'phpdoc_inline_tag_normalizer' => true,
/*
* Changes doc blocks from single to multi line, or reversed. Works
@@ -1103,6 +1147,17 @@ $rules = [
*/
'phpdoc_order' => true,
// Order phpdoc tags by value.
'phpdoc_order_by_value' => [
'annotations' => [
'covers',
'method',
'property',
'property-read',
'property-write',
],
],
/*
* The type of `@return` annotations of methods returning a
* reference to itself must the configured one.
@@ -1132,6 +1187,16 @@ $rules = [
*/
'phpdoc_summary' => true,
// Fixes casing of PHPDoc tags.
'phpdoc_tag_casing' => true,
// Forces PHPDoc tags to be either regular annotations or inline.
'phpdoc_tag_type' => [
'tags' => [
'inheritDoc' => 'inline',
],
],
// Docblocks should only be used on structural elements.
'phpdoc_to_comment' => false,
@@ -1140,7 +1205,7 @@ $rules = [
* adjusts accordingly the function signature. Requires PHP >= 7.0.
*
* Risky!
* [1] This rule is EXPERIMENTAL and is not covered with backward
* This rule is EXPERIMENTAL and [1] is not covered with backward
* compatibility promise. [2] `@param` annotation is mandatory for
* the fixer to make changes, signatures of methods without it (no
* docblock, inheritdocs) will not be fixed. [3] Manual actions are
@@ -1148,17 +1213,29 @@ $rules = [
*/
'phpdoc_to_param_type' => false,
/*
* EXPERIMENTAL: Takes `@var` annotation of non-mixed types and
* adjusts accordingly the property signature. Requires PHP >= 7.4.
*
* Risky!
* This rule is EXPERIMENTAL and [1] is not covered with backward
* compatibility promise. [2] `@var` annotation is mandatory for the
* fixer to make changes, signatures of properties without it (no
* docblock) will not be fixed. [3] Manual actions might be required
* for newly typed properties that are read before initialization.
*/
'phpdoc_to_property_type' => false,
/*
* EXPERIMENTAL: Takes `@return` annotation of non-mixed types and
* adjusts accordingly the function signature. Requires PHP >= 7.0.
*
* Risky!
* [1] This rule is EXPERIMENTAL and is not covered with backward
* This rule is EXPERIMENTAL and [1] is not covered with backward
* compatibility promise. [2] `@return` annotation is mandatory for
* the fixer to make changes, signatures of methods without it (no
* docblock, inheritdocs) will not be fixed. [3] Manual actions are
* required if inherited signatures are not properly documented. [4]
* `@inheritdocs` support is under construction.
* required if inherited signatures are not properly documented.
*/
'phpdoc_to_return_type' => false,
@@ -1190,8 +1267,8 @@ $rules = [
'phpdoc_var_annotation_correct_order' => true,
/*
* `@var` and `@type` annotations should not contain the variable
* name.
* `@var` and `@type` annotations of classy properties should not
* contain the name.
*/
'phpdoc_var_without_name' => false,
@@ -1201,7 +1278,7 @@ $rules = [
* Risky!
* Risky when the function `pow` is overridden.
*/
'pow_to_exponentiation' => false,
'pow_to_exponentiation' => true,
/*
* Converts `protected` variables and methods to `private` where
@@ -1218,16 +1295,7 @@ $rules = [
* This fixer may change your class name, which will break the code
* that depends on the old name.
*/
'psr0' => false,
/*
* Class names should match the file name.
*
* Risky!
* This fixer may change your class name, which will break the code
* that depends on the old name.
*/
'psr4' => true,
'psr_autoloading' => false,
/*
* Replaces `rand`, `srand`, `getrandmax` functions calls with their
@@ -1238,12 +1306,23 @@ $rules = [
*/
'random_api_migration' => [
'replacements' => [
'getrandmax' => 'mt_getrandmax',
'rand' => 'mt_rand',
'srand' => 'mt_srand',
'mt_rand' => 'random_int',
'rand' => 'random_int',
],
],
/*
* Callables must be called without using `call_user_func*` when
* possible.
*
* Risky!
* Risky when the `call_user_func` or `call_user_func_array`
* function is overridden or when are used in constructions that
* should be avoided, like `call_user_func_array('foo', ['bar' =>
* 'baz'])` or `call_user_func($foo, $foo = 'bar')`.
*/
'regular_callable_call' => true,
/*
* Local, dynamic and directly referenced variables should not be
* assigned and directly returned by a function or method.
@@ -1256,7 +1335,7 @@ $rules = [
*
* Rule is applied only in a PHP 7+ environment.
*/
'return_type_declaration' => false,
'return_type_declaration' => true,
/*
* Inside class or interface element `self` should be preferred to
@@ -1302,6 +1381,12 @@ $rules = [
*/
'simple_to_complex_string_variable' => true,
/*
* Simplify `if` control structures that return the boolean result
* of their condition.
*/
'simplified_if_return' => true,
/*
* A return statement wishing to return `void` should not return
* `null`.
@@ -1349,6 +1434,9 @@ $rules = [
'strings_containing_single_quote_chars' => false,
],
// Ensures a single space after language constructs.
'single_space_after_construct' => true,
// Each trait `use` must be done as single statement.
'single_trait_insert_per_statement' => true,
@@ -1366,7 +1454,7 @@ $rules = [
* `static`.
*
* Risky!
* Risky when using "->bindTo" on lambdas without referencing to
* Risky when using `->bindTo` on lambdas without referencing to
* `$this`.
*/
'static_lambda' => true,
@@ -1407,17 +1495,32 @@ $rules = [
// Removes extra spaces between colon and case value.
'switch_case_space' => true,
// Switch case must not be ended with `continue` but with `break`.
'switch_continue_to_break' => true,
// Standardize spaces around ternary operator.
'ternary_operator_spaces' => true,
/*
* Use the Elvis operator `?:` where possible.
*
* Risky!
* Risky when relying on functions called on both sides of the `?`
* operator.
*/
'ternary_to_elvis_operator' => true,
/*
* Use `null` coalescing operator `??` where possible. Requires PHP
* >= 7.0.
*/
'ternary_to_null_coalescing' => false,
'ternary_to_null_coalescing' => true,
// PHP multi-line arrays should have a trailing comma.
'trailing_comma_in_multiline_array' => true,
/*
* Multi-line arrays, arguments list and parameters list must have a
* trailing comma.
*/
'trailing_comma_in_multiline' => true,
/*
* Arrays should be formatted like function/method arguments,
@@ -1428,12 +1531,27 @@ $rules = [
// Unary operators should be placed adjacent to their operands.
'unary_operator_spaces' => true,
/*
* Anonymous functions with one-liner return statement must use
* arrow functions.
*
* Risky!
* Risky when using `isset()` on outside variables that are not
* imported with `use ()`.
*/
'use_arrow_functions' => false,
/*
* Visibility MUST be declared on all properties and methods;
* `abstract` and `final` MUST be declared before the visibility;
* `static` MUST be declared after the visibility.
*/
'visibility_required' => true,
'visibility_required' => [
'elements' => [
'method',
'property',
],
],
/*
* Add `void` return type to functions with missing or empty return
@@ -1452,8 +1570,10 @@ $rules = [
'whitespace_after_comma_in_array' => true,
/*
* Write conditions in Yoda style (`true`), non-Yoda style (`false`)
* or ignore those conditions (`null`) based on configuration.
* Write conditions in Yoda style (`true`), non-Yoda style
* (`['equal' => false, 'identical' => false, 'less_and_greater' =>
* false]`) or ignore those conditions (`null`) based on
* configuration.
*/
'yoda_style' => [
'equal' => false,
@@ -1464,12 +1584,14 @@ $rules = [
if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) {
$binFixer = __DIR__ . '/vendor/bin/php-cs-fixer';
if (!is_file($binFixer)) {
$binFixer = 'php-cs-fixer';
}
$dryRun = !\in_array('--force', $_SERVER['argv'], true);
$dryRun = !in_array('--force', $_SERVER['argv'], true);
$command = escapeshellarg($binFixer) . ' fix --config ' . escapeshellarg(__FILE__) . ' --diff --ansi -vv';
$command = escapeshellarg($binFixer) . ' fix --config ' . escapeshellarg(__FILE__) . ' --diff-format udiff --ansi';
if ($dryRun) {
$command .= ' --dry-run';
}
@@ -1489,13 +1611,14 @@ if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) {
exit($returnCode);
}
return \PhpCsFixer\Config::create()
return (new \PhpCsFixer\Config())
->setUsingCache(true)
->setCacheFile(__DIR__ . '/.php_cs.cache')
->setCacheFile(__DIR__ . '/.php-cs-fixer.cache')
->setRules($rules)
->setRiskyAllowed(true)
->setFinder(
\PhpCsFixer\Finder::create()
->ignoreUnreadableDirs()
->in(__DIR__)
)
;

View File

@@ -82,7 +82,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 +92,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'));

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,16 +1,22 @@
`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)
[![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)
[![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)
[![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)
### Версии и зависимости
| Версия | PHP | Документация |
| --------------- | ------------ | ----------------------------------------------------------------------------- |
| ^4.0 (master) | ^7.4 \| ^8.0 | [Документация v4.0](https://github.com/Ne-Lexa/php-zip/blob/master/README.md) |
| 3.4.* | ^7.1 \| ^8.0 | текущая документация |
| 3.0.* - 3.3.* | ^5.5 \| ^7.0 | [Документация v3.3](https://github.com/Ne-Lexa/php-zip/blob/3.3.3/README.md) |
Содержание
----------
- [Функционал](#Features)

View File

@@ -1,16 +1,22 @@
`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)
[![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)
[![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)
[![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 | [Docs v4.0](https://github.com/Ne-Lexa/php-zip/blob/master/README.md) |
| 3.4.* | ^7.1 \| ^8.0 | current docs |
| 3.0.* - 3.3.* | ^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)

View File

@@ -1,6 +1,5 @@
<?php
// see https://stackoverflow.com/questions/33299149/phpstorm-8-and-phpunit-problems-with-runinseparateprocess/37174348#37174348
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/vendor/autoload.php');
}

View File

@@ -21,11 +21,10 @@
}
],
"require": {
"php": "^5.5.9 || ^7.0",
"php": "^7.1.3 | ^8.0",
"ext-zlib": "*",
"psr/http-message": "^1.0",
"paragonie/random_compat": ">=1 <9.99",
"symfony/finder": "^3.0|^4.0|^5.0"
"symfony/finder": "^3.0 | ^4.0 | ^5.0"
},
"require-dev": {
"ext-bz2": "*",
@@ -33,8 +32,9 @@
"ext-fileinfo": "*",
"ext-xml": "*",
"guzzlehttp/psr7": "^1.6",
"phpunit/phpunit": "^4.8|^5.7",
"symfony/var-dumper": "^3.0|^4.0|^5.0"
"phpunit/phpunit": "^7.5.20 | ^8.5.15 | ^9.5.4",
"symfony/var-dumper": "^3.0 | ^4.0 | ^5.0",
"friendsofphp/php-cs-fixer": "^3.0"
},
"autoload": {
"psr-4": {
@@ -47,14 +47,13 @@
}
},
"suggest": {
"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-openssl": "Needed to support encrypt zip entries",
"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"
"php:fix": "php .php-cs-fixer.php --force",
"php:fix:debug": "php .php-cs-fixer.php"
}
}

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,36 +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>
<groups>
<exclude>
<group>large</group>
</exclude>
</groups>
<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" 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>

View File

@@ -56,9 +56,8 @@ final class ZipCompressionMethod
*/
public static function getCompressionMethodName($value)
{
return isset(self::$ZIP_COMPRESSION_METHODS[$value]) ?
self::$ZIP_COMPRESSION_METHODS[$value] :
'Unknown Method';
return self::$ZIP_COMPRESSION_METHODS[$value]
?? 'Unknown Method';
}
/**

View File

@@ -41,9 +41,8 @@ final class ZipEncryptionMethod
{
$value = (int) $value;
return isset(self::$ENCRYPTION_METHODS[$value]) ?
self::$ENCRYPTION_METHODS[$value] :
'Unknown Encryption Method';
return self::$ENCRYPTION_METHODS[$value]
?? 'Unknown Encryption Method';
}
/**

View File

@@ -48,6 +48,6 @@ final class ZipPlatform
*/
public static function getPlatformName($platform)
{
return isset(self::$platforms[$platform]) ? self::$platforms[$platform] : 'Unknown';
return self::$platforms[$platform] ?? 'Unknown';
}
}

View File

@@ -328,7 +328,7 @@ class PKCryptContext
}
if ($byte !== $checkByte) {
throw new ZipAuthenticationException(sprintf('Invalid password'));
throw new ZipAuthenticationException('Invalid password');
}
}

View File

@@ -66,9 +66,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);

View File

@@ -60,9 +60,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;
}
@@ -156,8 +156,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 = '';

View File

@@ -53,9 +53,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;
}

View File

@@ -87,27 +87,11 @@ class ResponseStream implements StreamInterface
}
/**
* Get stream metadata as an associative array or retrieve a specific key.
*
* The keys returned are identical to the keys returned from PHP's
* stream_get_meta_data() function.
*
* @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.
* Closes the stream when the destructed.
*/
public function getMetadata($key = null)
public function __destruct()
{
if (!$this->stream) {
return $key ? null : [];
}
$meta = stream_get_meta_data($this->stream);
return isset($meta[$key]) ? $meta[$key] : null;
$this->close();
}
/**
@@ -135,6 +119,30 @@ class ResponseStream implements StreamInterface
return (string) stream_get_contents($this->stream);
}
/**
* Get stream metadata as an associative array or retrieve a specific key.
*
* The keys returned are identical to the keys returned from PHP's
* stream_get_meta_data() function.
*
* @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.
*/
public function getMetadata($key = null)
{
if (!$this->stream) {
return $key ? null : [];
}
$meta = stream_get_meta_data($this->stream);
return $meta[$key] ?? null;
}
/**
* Seek to the beginning of the stream.
*
@@ -163,7 +171,7 @@ class ResponseStream implements StreamInterface
}
if (!$this->stream) {
return null;
return;
}
// Clear the stat cache if the stream has a URI
if ($this->uri !== null) {
@@ -176,8 +184,6 @@ class ResponseStream implements StreamInterface
return $this->size;
}
return null;
}
/**
@@ -297,14 +303,6 @@ class ResponseStream implements StreamInterface
return $this->stream ? stream_get_contents($this->stream) : '';
}
/**
* Closes the stream when the destructed.
*/
public function __destruct()
{
$this->close();
}
/**
* Closes the stream and any underlying resources.
*/

View File

@@ -59,7 +59,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 +72,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,6 +94,11 @@ class ZipReader
$this->options = $options;
}
public function __destruct()
{
$this->close();
}
/**
* @return array
*/
@@ -162,15 +167,15 @@ class ZipReader
$buffer = fread($this->inStream, $sizeECD);
$unpack = unpack(
'vdiskNo/vcdDiskNo/vcdEntriesDisk/' .
'vcdEntries/VcdSize/VcdPos/vcommentLength',
'vdiskNo/vcdDiskNo/vcdEntriesDisk/'
. 'vcdEntries/VcdSize/VcdPos/vcommentLength',
substr($buffer, 0, 18)
);
if (
$unpack['diskNo'] !== 0 ||
$unpack['cdDiskNo'] !== 0 ||
$unpack['cdEntriesDisk'] !== $unpack['cdEntries']
$unpack['diskNo'] !== 0
|| $unpack['cdDiskNo'] !== 0
|| $unpack['cdEntriesDisk'] !== $unpack['cdEntries']
) {
throw new ZipException(
'ZIP file spanning/splitting is not supported!'
@@ -354,7 +359,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);
@@ -373,8 +380,8 @@ class ZipReader
$unicodePath = str_replace('\\', '/', $unicodePath);
if (
$unicodePath !== '' &&
substr_count($entryName, '/') === substr_count($unicodePath, '/')
$unicodePath !== ''
&& substr_count($entryName, '/') === substr_count($unicodePath, '/')
) {
$entryName = $unicodePath;
}
@@ -425,12 +432,12 @@ class ZipReader
}
$unpack = unpack(
'vversionMadeBy/vversionNeededToExtract/' .
'vgeneralPurposeBitFlag/vcompressionMethod/' .
'VlastModFile/Vcrc/VcompressedSize/' .
'VuncompressedSize/vfileNameLength/vextraFieldLength/' .
'vfileCommentLength/vdiskNumberStart/vinternalFileAttributes/' .
'VexternalFileAttributes/VoffsetLocalHeader',
'vversionMadeBy/vversionNeededToExtract/'
. 'vgeneralPurposeBitFlag/vcompressionMethod/'
. 'VlastModFile/Vcrc/VcompressedSize/'
. 'VuncompressedSize/vfileNameLength/vextraFieldLength/'
. 'vfileCommentLength/vdiskNumberStart/vinternalFileAttributes/'
. 'VexternalFileAttributes/VoffsetLocalHeader',
fread($stream, 42)
);
@@ -521,9 +528,9 @@ class ZipReader
*/
protected function parseExtraFields($buffer, ZipEntry $zipEntry, $local = false)
{
$collection = $local ?
$zipEntry->getLocalExtraFields() :
$zipEntry->getCdExtraFields();
$collection = $local
? $zipEntry->getLocalExtraFields()
: $zipEntry->getCdExtraFields();
if (!empty($buffer)) {
$pos = 0;
@@ -546,9 +553,9 @@ class ZipReader
try {
if ($className !== null) {
try {
$extraField = $local ?
\call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry) :
\call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
$extraField = $local
? \call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry)
: \call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
} catch (\Throwable $e) {
// skip errors while parsing invalid data
continue;
@@ -888,9 +895,4 @@ class ZipReader
fclose($this->inStream);
}
}
public function __destruct()
{
$this->close();
}
}

View File

@@ -105,8 +105,8 @@ class ZipWriter
$entry->enableDataDescriptor(true);
}
$dd = $entry->isDataDescriptorRequired() ||
$entry->isDataDescriptorEnabled();
$dd = $entry->isDataDescriptorRequired()
|| $entry->isDataDescriptorEnabled();
$compressedSize = $entry->getCompressedSize();
$uncompressedSize = $entry->getUncompressedSize();
@@ -232,20 +232,20 @@ class ZipWriter
$offset = ftell($outStream);
$dataMinStartOffset =
$offset +
ZipConstants::LFH_FILENAME_POS +
$extraLength +
$nameLength;
$dataMinStartOffset
= $offset
+ ZipConstants::LFH_FILENAME_POS
+ $extraLength
+ $nameLength;
$padding =
($multiple - ($dataMinStartOffset % $multiple))
$padding
= ($multiple - ($dataMinStartOffset % $multiple))
% $multiple;
if ($padding > 0) {
$dataMinStartOffset += ApkAlignmentExtraField::MIN_SIZE;
$padding =
($multiple - ($dataMinStartOffset % $multiple))
$padding
= ($multiple - ($dataMinStartOffset % $multiple))
% $multiple;
$entry->getLocalExtraFields()->add(
@@ -268,9 +268,9 @@ class ZipWriter
protected function getExtraFieldsContents(ZipEntry $entry, $local)
{
$local = (bool) $local;
$collection = $local ?
$entry->getLocalExtraFields() :
$entry->getCdExtraFields();
$collection = $local
? $entry->getLocalExtraFields()
: $entry->getCdExtraFields();
$extraData = '';
foreach ($collection as $extraField) {
@@ -490,6 +490,7 @@ class ZipWriter
))) {
throw new \RuntimeException('Could not append filter "zlib.deflate" to out stream');
}
break;
case ZipCompressionMethod::BZIP2:
@@ -501,6 +502,7 @@ class ZipWriter
))) {
throw new \RuntimeException('Could not append filter "bzip2.compress" to out stream');
}
break;
case ZipCompressionMethod::STORED:
@@ -585,14 +587,14 @@ class ZipWriter
);
if (
$entry->isZip64ExtensionsRequired() ||
$entry->getLocalExtraFields()->has(Zip64ExtraField::HEADER_ID)
$entry->isZip64ExtensionsRequired()
|| $entry->getLocalExtraFields()->has(Zip64ExtraField::HEADER_ID)
) {
$dd =
$dd
// compressed size 8 bytes
PackUtil::packLongLE($entry->getCompressedSize()) .
= PackUtil::packLongLE($entry->getCompressedSize())
// uncompressed size 8 bytes
PackUtil::packLongLE($entry->getUncompressedSize());
. PackUtil::packLongLE($entry->getUncompressedSize());
} else {
$dd = pack(
'VV',
@@ -635,9 +637,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();
@@ -814,16 +816,16 @@ class ZipWriter
$outStream,
// total number of entries in the
// central directory on this disk 8 bytes
PackUtil::packLongLE($cdEntriesCount) .
PackUtil::packLongLE($cdEntriesCount)
// total number of entries in the
// central directory 8 bytes
PackUtil::packLongLE($cdEntriesCount) .
. PackUtil::packLongLE($cdEntriesCount)
// size of the central directory 8 bytes
PackUtil::packLongLE($centralDirectorySize) .
. PackUtil::packLongLE($centralDirectorySize)
// offset of start of central
// directory with respect to
// the starting disk number 8 bytes
PackUtil::packLongLE($centralDirectoryOffset)
. PackUtil::packLongLE($centralDirectoryOffset)
);
// write zip64 end of central directory locator
@@ -838,12 +840,12 @@ class ZipWriter
// 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) .
. PackUtil::packLongLE($zip64EndOfCentralDirectoryOffset)
// total number of disks 4 bytes
pack('V', 1)
. pack('V', 1)
);
}

View File

@@ -4,6 +4,7 @@ namespace PhpZip\Model\Data;
use PhpZip\Exception\ZipException;
use PhpZip\Model\ZipData;
use PhpZip\Model\ZipEntry;
/**
* Class ZipFileData.
@@ -16,11 +17,12 @@ class ZipFileData implements ZipData
/**
* ZipStringData constructor.
*
* @param ZipEntry $zipEntry
* @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());
}
/**

View File

@@ -39,7 +39,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);
@@ -49,10 +51,44 @@ class ZipNewData implements ZipData
}
$resourceId = (int) $this->stream;
self::$guardClonedStream[$resourceId] =
isset(self::$guardClonedStream[$resourceId]) ?
self::$guardClonedStream[$resourceId] + 1 :
0;
self::$guardClonedStream[$resourceId]
= isset(self::$guardClonedStream[$resourceId])
? self::$guardClonedStream[$resourceId] + 1
: 0;
}
/**
* 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);
}
}
/**
* @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;
}
/**
@@ -61,7 +97,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;
@@ -93,38 +129,4 @@ class ZipNewData implements ZipData
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

@@ -47,6 +47,16 @@ class ZipSourceFileData implements ZipData
$this->uncompressedSize = $zipEntry->getUncompressedSize();
}
/**
* {@inheritDoc}
*/
public function __destruct()
{
if (\is_resource($this->stream)) {
fclose($this->stream);
}
}
/**
* @param ZipEntry $entry
*
@@ -54,14 +64,14 @@ class ZipSourceFileData implements ZipData
*/
public function hasRecompressData(ZipEntry $entry)
{
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();
}
/**
@@ -159,14 +169,4 @@ class ZipSourceFileData implements ZipData
{
return $this->offset;
}
/**
* {@inheritdoc}
*/
public function __destruct()
{
if (\is_resource($this->stream)) {
fclose($this->stream);
}
}
}

View File

@@ -18,6 +18,30 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*/
protected $collection = [];
/**
* @return string
*/
public function __toString()
{
$formats = [];
foreach ($this->collection as $key => $value) {
$formats[] = (string) $value;
}
return implode("\n", $formats);
}
/**
* If clone extra fields.
*/
public function __clone()
{
foreach ($this->collection as $k => $v) {
$this->collection[$k] = clone $v;
}
}
/**
* Returns the number of Extra Fields in this collection.
*
@@ -41,7 +65,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
{
$this->validateHeaderId($headerId);
return isset($this->collection[$headerId]) ? $this->collection[$headerId] : null;
return $this->collection[$headerId] ?? null;
}
/**
@@ -128,8 +152,6 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
return $ef;
}
return null;
}
/**
@@ -157,7 +179,7 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*/
public function offsetGet($offset)
{
return isset($this->collection[$offset]) ? $this->collection[$offset] : null;
return $this->collection[$offset] ?? null;
}
/**
@@ -249,28 +271,4 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
{
$this->collection = [];
}
/**
* @return string
*/
public function __toString()
{
$formats = [];
foreach ($this->collection as $key => $value) {
$formats[] = (string) $value;
}
return implode("\n", $formats);
}
/**
* If clone extra fields.
*/
public function __clone()
{
foreach ($this->collection as $k => $v) {
$this->collection[$k] = clone $v;
}
}
}

View File

@@ -116,8 +116,8 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
'CV',
self::DEFAULT_VERSION,
$this->crc32
) .
$this->unicodeValue;
)
. $this->unicodeValue;
}
/**

View File

@@ -50,6 +50,19 @@ class ApkAlignmentExtraField implements ZipExtraField
$this->padding = $padding;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x APK Alignment: Multiple=%d Padding=%d',
self::HEADER_ID,
$this->multiple,
$this->padding
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
@@ -160,17 +173,4 @@ class ApkAlignmentExtraField implements ZipExtraField
{
return $this->packLocalFileData();
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x APK Alignment: Multiple=%d Padding=%d',
self::HEADER_ID,
$this->multiple,
$this->padding
);
}
}

View File

@@ -85,6 +85,21 @@ class AsiExtraField implements ZipExtraField
$this->link = $link;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x ASI: Mode=%o UID=%d GID=%d Link="%s',
self::HEADER_ID,
$this->mode,
$this->uid,
$this->gid,
$this->link
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
@@ -284,19 +299,4 @@ class AsiExtraField implements ZipExtraField
{
$this->gid = (int) $gid;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x ASI: Mode=%o UID=%d GID=%d Link="%s',
self::HEADER_ID,
$this->mode,
$this->uid,
$this->gid,
$this->link
);
}
}

View File

@@ -116,6 +116,32 @@ class ExtendedTimestampExtraField implements ZipExtraField
$this->createTime = $createTime;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x ExtendedTimestamp:';
if ($this->modifyTime !== null) {
$format .= ' Modify:[%s]';
$args[] = date(\DATE_W3C, $this->modifyTime);
}
if ($this->accessTime !== null) {
$format .= ' Access:[%s]';
$args[] = date(\DATE_W3C, $this->accessTime);
}
if ($this->createTime !== null) {
$format .= ' Create:[%s]';
$args[] = date(\DATE_W3C, $this->createTime);
}
return vsprintf($format, $args);
}
/**
* @param int|null $modifyTime
* @param int|null $accessTime
@@ -414,33 +440,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
try {
return $timestamp !== null ? new \DateTimeImmutable('@' . $timestamp) : null;
} catch (\Exception $e) {
return null;
return;
}
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x ExtendedTimestamp:';
if ($this->modifyTime !== null) {
$format .= ' Modify:[%s]';
$args[] = date(\DATE_W3C, $this->modifyTime);
}
if ($this->accessTime !== null) {
$format .= ' Access:[%s]';
$args[] = date(\DATE_W3C, $this->accessTime);
}
if ($this->createTime !== null) {
$format .= ' Create:[%s]';
$args[] = date(\DATE_W3C, $this->createTime);
}
return vsprintf($format, $args);
}
}

View File

@@ -22,6 +22,14 @@ class JarMarkerExtraField implements ZipExtraField
/** @var int Header id. */
const HEADER_ID = 0xCAFE;
/**
* @return string
*/
public function __toString()
{
return sprintf('0x%04x Jar Marker', self::HEADER_ID);
}
/**
* @param ZipContainer $container
*/
@@ -107,12 +115,4 @@ class JarMarkerExtraField implements ZipExtraField
{
return self::unpackLocalFileData($buffer, $entry);
}
/**
* @return string
*/
public function __toString()
{
return sprintf('0x%04x Jar Marker', self::HEADER_ID);
}
}

View File

@@ -73,6 +73,19 @@ class NewUnixExtraField implements ZipExtraField
$this->gid = (int) $gid;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x NewUnix: UID=%d GID=%d',
self::HEADER_ID,
$this->uid,
$this->gid
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
@@ -221,17 +234,4 @@ class NewUnixExtraField implements ZipExtraField
{
return $this->version;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x NewUnix: UID=%d GID=%d',
self::HEADER_ID,
$this->uid,
$this->gid
);
}
}

View File

@@ -55,6 +55,32 @@ class NtfsExtraField implements ZipExtraField
$this->createNtfsTime = (int) $createNtfsTime;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x NtfsExtra:';
if ($this->modifyNtfsTime !== 0) {
$format .= ' Modify:[%s]';
$args[] = $this->getModifyDateTime()->format(\DATE_ATOM);
}
if ($this->accessNtfsTime !== 0) {
$format .= ' Access:[%s]';
$args[] = $this->getAccessDateTime()->format(\DATE_ATOM);
}
if ($this->createNtfsTime !== 0) {
$format .= ' Create:[%s]';
$args[] = $this->getCreateDateTime()->format(\DATE_ATOM);
}
return vsprintf($format, $args);
}
/**
* @param \DateTimeInterface $modifyDateTime
* @param \DateTimeInterface $accessDateTime
@@ -310,30 +336,4 @@ class NtfsExtraField implements ZipExtraField
return $dateTime;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x NtfsExtra:';
if ($this->modifyNtfsTime !== 0) {
$format .= ' Modify:[%s]';
$args[] = $this->getModifyDateTime()->format(\DATE_ATOM);
}
if ($this->accessNtfsTime !== 0) {
$format .= ' Access:[%s]';
$args[] = $this->getAccessDateTime()->format(\DATE_ATOM);
}
if ($this->createNtfsTime !== 0) {
$format .= ' Create:[%s]';
$args[] = $this->getCreateDateTime()->format(\DATE_ATOM);
}
return vsprintf($format, $args);
}
}

View File

@@ -84,6 +84,37 @@ class OldUnixExtraField implements ZipExtraField
$this->gid = $gid;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x OldUnix:';
if (($modifyTime = $this->getModifyDateTime()) !== null) {
$format .= ' Modify:[%s]';
$args[] = $modifyTime->format(\DATE_ATOM);
}
if (($accessTime = $this->getAccessDateTime()) !== null) {
$format .= ' Access:[%s]';
$args[] = $accessTime->format(\DATE_ATOM);
}
if ($this->uid !== null) {
$format .= ' UID=%d';
$args[] = $this->uid;
}
if ($this->gid !== null) {
$format .= ' GID=%d';
$args[] = $this->gid;
}
return vsprintf($format, $args);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
@@ -226,10 +257,10 @@ class OldUnixExtraField implements ZipExtraField
public function getAccessDateTime()
{
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;
}
}
@@ -255,10 +286,10 @@ class OldUnixExtraField implements ZipExtraField
public function getModifyDateTime()
{
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;
}
}
@@ -293,35 +324,4 @@ class OldUnixExtraField implements ZipExtraField
{
$this->gid = $gid;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x OldUnix:';
if (($modifyTime = $this->getModifyDateTime()) !== null) {
$format .= ' Modify:[%s]';
$args[] = $modifyTime->format(\DATE_ATOM);
}
if (($accessTime = $this->getAccessDateTime()) !== null) {
$format .= ' Access:[%s]';
$args[] = $accessTime->format(\DATE_ATOM);
}
if ($this->uid !== null) {
$format .= ' UID=%d';
$args[] = $this->uid;
}
if ($this->gid !== null) {
$format .= ' GID=%d';
$args[] = $this->gid;
}
return vsprintf($format, $args);
}
}

View File

@@ -50,18 +50,6 @@ class UnicodeCommentExtraField extends AbstractUnicodeExtraField
{
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()
{
return self::HEADER_ID;
}
/**
* @return string
*/
@@ -73,4 +61,16 @@ class UnicodeCommentExtraField extends AbstractUnicodeExtraField
$this->getUnicodeValue()
);
}
/**
* 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()
{
return self::HEADER_ID;
}
}

View File

@@ -51,18 +51,6 @@ class UnicodePathExtraField extends AbstractUnicodeExtraField
{
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()
{
return self::HEADER_ID;
}
/**
* @return string
*/
@@ -74,4 +62,16 @@ class UnicodePathExtraField extends AbstractUnicodeExtraField
$this->getUnicodeValue()
);
}
/**
* 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()
{
return self::HEADER_ID;
}
}

View File

@@ -29,6 +29,17 @@ class UnrecognizedExtraField implements ZipExtraField
$this->data = (string) $data;
}
/**
* @return string
*/
public function __toString()
{
$args = [$this->headerId, $this->data];
$format = '0x%04x Unrecognized Extra Field: "%s"';
return vsprintf($format, $args);
}
/**
* @param int $headerId
*/
@@ -72,7 +83,7 @@ class UnrecognizedExtraField implements ZipExtraField
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function packLocalFileData()
{
@@ -80,7 +91,7 @@ class UnrecognizedExtraField implements ZipExtraField
}
/**
* {@inheritdoc}
* {@inheritDoc}
*/
public function packCentralDirData()
{
@@ -102,15 +113,4 @@ class UnrecognizedExtraField implements ZipExtraField
{
$this->data = (string) $data;
}
/**
* @return string
*/
public function __toString()
{
$args = [$this->headerId, $this->data];
$format = '0x%04x Unrecognized Extra Field: "%s"';
return vsprintf($format, $args);
}
}

View File

@@ -96,6 +96,20 @@ class WinZipAesExtraField implements ZipExtraField
$this->setCompressionMethod($compressionMethod);
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x WINZIP AES: VendorVersion=%d KeyStrength=0x%02x CompressionMethod=%s',
__CLASS__,
$this->vendorVersion,
$this->keyStrength,
$this->compressionMethod
);
}
/**
* @param ZipEntry $entry
*
@@ -118,11 +132,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());
@@ -370,18 +384,4 @@ class WinZipAesExtraField implements ZipExtraField
{
return (int) ($this->getEncryptionStrength() / 8 / 2);
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x WINZIP AES: VendorVersion=%d KeyStrength=0x%02x CompressionMethod=%s',
__CLASS__,
$this->vendorVersion,
$this->keyStrength,
$this->compressionMethod
);
}
}

View File

@@ -51,6 +51,39 @@ class Zip64ExtraField implements ZipExtraField
$this->diskStart = $diskStart;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x ZIP64: ';
$formats = [];
if ($this->uncompressedSize !== null) {
$formats[] = 'SIZE=%d';
$args[] = $this->uncompressedSize;
}
if ($this->compressedSize !== null) {
$formats[] = 'COMP_SIZE=%d';
$args[] = $this->compressedSize;
}
if ($this->localHeaderOffset !== null) {
$formats[] = 'OFFSET=%d';
$args[] = $this->localHeaderOffset;
}
if ($this->diskStart !== null) {
$formats[] = 'DISK_START=%d';
$args[] = $this->diskStart;
}
$format .= implode(' ', $formats);
return vsprintf($format, $args);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
@@ -275,37 +308,4 @@ class Zip64ExtraField implements ZipExtraField
{
$this->diskStart = $diskStart;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x ZIP64: ';
$formats = [];
if ($this->uncompressedSize !== null) {
$formats[] = 'SIZE=%d';
$args[] = $this->uncompressedSize;
}
if ($this->compressedSize !== null) {
$formats[] = 'COMP_SIZE=%d';
$args[] = $this->compressedSize;
}
if ($this->localHeaderOffset !== null) {
$formats[] = 'OFFSET=%d';
$args[] = $this->localHeaderOffset;
}
if ($this->diskStart !== null) {
$formats[] = 'DISK_START=%d';
$args[] = $this->diskStart;
}
$format .= implode(' ', $formats);
return vsprintf($format, $args);
}
}

View File

@@ -101,7 +101,5 @@ final class ZipExtraDriver
if (isset(self::$implementations[$headerId])) {
return self::$implementations[$headerId];
}
return null;
}
}

View File

@@ -11,6 +11,11 @@ use PhpZip\Model\ZipEntry;
*/
interface ZipExtraField
{
/**
* @return string
*/
public function __toString();
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
@@ -55,9 +60,4 @@ interface ZipExtraField
* @return string the data
*/
public function packCentralDirData();
/**
* @return string
*/
public function __toString();
}

View File

@@ -25,6 +25,22 @@ class ImmutableZipContainer implements \Countable
$this->archiveComment = $archiveComment;
}
/**
* 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;
}
}
/**
* @return ZipEntry[]
*/
@@ -53,20 +69,4 @@ class ImmutableZipContainer implements \Countable
{
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

@@ -134,7 +134,7 @@ class ZipContainer extends ImmutableZipContainer
{
$entryName = $entryName instanceof ZipEntry ? $entryName->getName() : (string) $entryName;
return isset($this->entries[$entryName]) ? $this->entries[$entryName] : null;
return $this->entries[$entryName] ?? null;
}
/**
@@ -225,8 +225,8 @@ class ZipContainer extends ImmutableZipContainer
$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];

View File

@@ -64,12 +64,28 @@ class ZipEntry
/**
* Pseudo compression method for WinZip AES encrypted entries.
* Require php extension openssl or mcrypt.
* Require php extension openssl.
*
* @deprecated Use {@see ZipCompressionMethod::WINZIP_AES}
*/
const METHOD_WINZIP_AES = ZipCompressionMethod::WINZIP_AES;
/**
* Collections of Extra Fields in Central Directory.
* Keys from Header ID [int] and value Extra Field [ExtraField].
*
* @var ExtraFieldsCollection
*/
protected $cdExtraFields;
/**
* Collections of Extra Fields int local header.
* Keys from Header ID [int] and value Extra Field [ExtraField].
*
* @var ExtraFieldsCollection
*/
protected $localExtraFields;
/** @var string Entry name (filename in archive) */
private $name;
@@ -118,22 +134,6 @@ class ZipEntry
/** @var int relative Offset Of Local File Header */
private $localHeaderOffset = 0;
/**
* Collections of Extra Fields in Central Directory.
* Keys from Header ID [int] and value Extra Field [ExtraField].
*
* @var ExtraFieldsCollection
*/
protected $cdExtraFields;
/**
* Collections of Extra Fields int local header.
* Keys from Header ID [int] and value Extra Field [ExtraField].
*
* @var ExtraFieldsCollection
*/
protected $localExtraFields;
/** @var string|null comment field */
private $comment;
@@ -163,6 +163,16 @@ class ZipEntry
$this->localExtraFields = new ExtraFieldsCollection();
}
public function __clone()
{
$this->cdExtraFields = clone $this->cdExtraFields;
$this->localExtraFields = clone $this->localExtraFields;
if ($this->data !== null) {
$this->data = clone $this->data;
}
}
/**
* This method only internal use.
*
@@ -267,9 +277,9 @@ class ZipEntry
if ($this->extractVersion !== self::UNKNOWN) {
$this->extractVersion = max(
$this->extractVersion,
$this->isDirectory ?
ZipVersion::v20_DEFLATED_FOLDER_ZIPCRYPTO :
ZipVersion::v10_DEFAULT_MIN
$this->isDirectory
? ZipVersion::v20_DEFLATED_FOLDER_ZIPCRYPTO
: ZipVersion::v10_DEFAULT_MIN
);
}
@@ -343,7 +353,7 @@ class ZipEntry
*
* @internal
*/
public function setData($data)
public function setData(ZipData $data = null)
{
$this->data = $data;
}
@@ -487,9 +497,9 @@ class ZipEntry
}
if (
$this->compressionMethod === ZipCompressionMethod::DEFLATED ||
$this->isDirectory ||
$this->encryptionMethod === ZipEncryptionMethod::PKWARE
$this->compressionMethod === ZipCompressionMethod::DEFLATED
|| $this->isDirectory
|| $this->encryptionMethod === ZipEncryptionMethod::PKWARE
) {
return ZipVersion::v20_DEFLATED_FOLDER_ZIPCRYPTO;
}
@@ -1151,8 +1161,8 @@ class ZipEntry
$headerId = (int) $headerId;
return
isset($this->localExtraFields[$headerId]) ||
isset($this->cdExtraFields[$headerId]);
isset($this->localExtraFields[$headerId])
|| isset($this->cdExtraFields[$headerId]);
}
/**
@@ -1358,12 +1368,12 @@ class ZipEntry
}
if (
$compressionLevel < ZipCompressionLevel::LEVEL_MIN ||
$compressionLevel > ZipCompressionLevel::LEVEL_MAX
$compressionLevel < ZipCompressionLevel::LEVEL_MIN
|| $compressionLevel > ZipCompressionLevel::LEVEL_MAX
) {
throw new InvalidArgumentException(
'Invalid compression level. Minimum level ' .
ZipCompressionLevel::LEVEL_MIN . '. Maximum level ' . ZipCompressionLevel::LEVEL_MAX
'Invalid compression level. Minimum level '
. ZipCompressionLevel::LEVEL_MIN . '. Maximum level ' . ZipCompressionLevel::LEVEL_MAX
);
}
$this->compressionLevel = $compressionLevel;
@@ -1385,15 +1395,18 @@ class ZipEntry
switch ($this->compressionLevel) {
case ZipCompressionLevel::MAXIMUM:
$bit1 = true;
break;
case ZipCompressionLevel::FAST:
$bit2 = true;
break;
case ZipCompressionLevel::SUPER_FAST:
$bit1 = true;
$bit2 = true;
break;
// default is ZipCompressionLevel::NORMAL
}
@@ -1535,8 +1548,6 @@ class ZipEntry
if ($oldUnixExtra !== null) {
return $oldUnixExtra->getAccessDateTime();
}
return null;
}
/**
@@ -1557,17 +1568,5 @@ class ZipEntry
if ($extendedExtra !== null) {
return $extendedExtra->getCreateDateTime();
}
return null;
}
public function __clone()
{
$this->cdExtraFields = clone $this->cdExtraFields;
$this->localExtraFields = clone $this->localExtraFields;
if ($this->data !== null) {
$this->data = clone $this->data;
}
}
}

View File

@@ -31,6 +31,33 @@ class ZipInfo
$this->entry = $entry;
}
/**
* @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()
. '}';
}
/**
* @param ZipEntry $entry
*
@@ -236,31 +263,4 @@ class ZipInfo
'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

@@ -40,15 +40,11 @@ class CryptoUtil
{
if (\extension_loaded('openssl')) {
$numBits = \strlen($key) * 8;
/** @noinspection PhpComposerExtensionStubsInspection */
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');
}
/**
@@ -64,14 +60,10 @@ class CryptoUtil
{
if (\extension_loaded('openssl')) {
$numBits = \strlen($key) * 8;
/** @noinspection PhpComposerExtensionStubsInspection */
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

@@ -82,12 +82,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

@@ -45,34 +45,42 @@ class FileAttribUtil implements DosAttrs, UnixStat
switch ($permission & self::UNX_IFMT) {
case self::UNX_IFDIR:
$mode .= 'd';
break;
case self::UNX_IFREG:
$mode .= '-';
break;
case self::UNX_IFLNK:
$mode .= 'l';
break;
case self::UNX_IFBLK:
$mode .= 'b';
break;
case self::UNX_IFCHR:
$mode .= 'c';
break;
case self::UNX_IFIFO:
$mode .= 'p';
break;
case self::UNX_IFSOCK:
$mode .= 's';
break;
default:
$mode .= '?';
break;
}
$mode .= ($permission & self::UNX_IRUSR) ? 'r' : '-';

View File

@@ -48,7 +48,7 @@ final class FilesUtil
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
$function($fileInfo->getPathname());
}
rmdir($dir);
@rmdir($dir);
}
/**
@@ -72,11 +72,13 @@ final class FilesUtil
case '*':
$regexPattern .= ($escaping ? '\\*' : '.*');
$escaping = false;
break;
case '?':
$regexPattern .= ($escaping ? '\\?' : '.');
$escaping = false;
break;
case '.':
@@ -90,6 +92,7 @@ final class FilesUtil
case '%':
$regexPattern .= '\\' . $currentChar;
$escaping = false;
break;
case '\\':
@@ -99,6 +102,7 @@ final class FilesUtil
} else {
$escaping = true;
}
break;
case '{':
@@ -109,6 +113,7 @@ final class FilesUtil
$inCurrent++;
}
$escaping = false;
break;
case '}':
@@ -121,6 +126,7 @@ final class FilesUtil
$regexPattern = '}';
}
$escaping = false;
break;
case ',':
@@ -131,6 +137,7 @@ final class FilesUtil
} else {
$regexPattern = ',';
}
break;
default:
$escaping = false;
@@ -198,10 +205,10 @@ final class FilesUtil
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;
@@ -273,7 +280,7 @@ final class FilesUtil
public static function normalizeZipPath($path)
{
return implode(
'/',
\DIRECTORY_SEPARATOR,
array_filter(
explode('/', (string) $path),
static function ($part) {
@@ -406,11 +413,9 @@ final class FilesUtil
'x-epoc/x-sisx-app',
];
if (\in_array($mimeType, $badDeflateCompMimeTypes, true)) {
return true;
}
return (bool) (\in_array($mimeType, $badDeflateCompMimeTypes, true))
return false;
;
}
/**

View File

@@ -1,9 +1,5 @@
<?php
/** @noinspection AdditionOperationOnArraysInspection */
/** @noinspection PhpUsageOfSilenceOperatorInspection */
namespace PhpZip;
use PhpZip\Constants\UnixStat;
@@ -46,6 +42,9 @@ use Symfony\Component\Finder\SplFileInfo as SymfonySplFileInfo;
*/
class ZipFile implements ZipFileInterface
{
/** @var ZipContainer */
protected $zipContainer;
/** @var array default mime types */
private static $defaultMimeTypes = [
'zip' => 'application/zip',
@@ -59,9 +58,6 @@ class ZipFile implements ZipFileInterface
'xpi' => 'application/x-xpinstall',
];
/** @var ZipContainer */
protected $zipContainer;
/** @var ZipReader|null */
private $reader;
@@ -73,6 +69,14 @@ class ZipFile implements ZipFileInterface
$this->zipContainer = $this->createZipContainer(null);
}
/**
* Release all resources.
*/
public function __destruct()
{
$this->close();
}
/**
* @param resource $inputStream
* @param array $options
@@ -142,7 +146,9 @@ class ZipFile implements ZipFileInterface
}
if (!($handle = fopen('php://temp', 'r+b'))) {
throw new ZipException("Can't open temp stream.");
// @codeCoverageIgnoreStart
throw new ZipException('A temporary resource cannot be opened for writing.');
// @codeCoverageIgnoreEnd
}
fwrite($handle, $data);
rewind($handle);
@@ -256,8 +262,8 @@ class ZipFile implements ZipFileInterface
*
* @param string $entryName
*
* @throws ZipException
* @throws ZipEntryNotFoundException
* @throws ZipException
*
* @return string
*/
@@ -272,8 +278,8 @@ class ZipFile implements ZipFileInterface
* @param string $entryName
* @param string|null $comment
*
* @throws ZipEntryNotFoundException
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return ZipFile
*/
@@ -289,8 +295,8 @@ class ZipFile implements ZipFileInterface
*
* @param string $entryName
*
* @throws ZipEntryNotFoundException
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return string
*/
@@ -308,8 +314,8 @@ class ZipFile implements ZipFileInterface
/**
* @param string $entryName
*
* @throws ZipEntryNotFoundException
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return resource
*/
@@ -326,8 +332,8 @@ class ZipFile implements ZipFileInterface
*
* @param string|ZipEntry $entryName
*
* @throws ZipException
* @throws ZipEntryNotFoundException
* @throws ZipException
*
* @return ZipInfo
*/
@@ -378,7 +384,7 @@ class ZipFile implements ZipFileInterface
* @param string $destDir location where to extract the files
* @param array|string|null $entries entries to extract
* @param array $options extract options
* @param array $extractedEntries if the extractedEntries argument
* @param array|null $extractedEntries if the extractedEntries argument
* is present, then the specified
* array will be filled with
* information about the
@@ -409,6 +415,7 @@ class ZipFile implements ZipFileInterface
$defaultOptions = [
ZipOptions::EXTRACT_SYMLINKS => false,
];
/** @noinspection AdditionOperationOnArraysInspection */
$options += $defaultOptions;
$zipEntries = $this->zipContainer->getEntries();
@@ -441,9 +448,6 @@ class ZipFile implements ZipFileInterface
$entryName = FilesUtil::normalizeZipPath($entryName);
$file = $destDir . \DIRECTORY_SEPARATOR . $entryName;
if (\DIRECTORY_SEPARATOR === '\\') {
$file = str_replace('/', '\\', $file);
}
$extractedEntries[$file] = $entry;
$modifyTimestamp = $entry->getMTime()->getTimestamp();
$atime = $entry->getATime();
@@ -459,7 +463,9 @@ class ZipFile implements ZipFileInterface
}
if (!mkdir($dir, $dirMode, true) && !is_dir($dir)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir));
// @codeCoverageIgnoreEnd
}
chmod($dir, $dirMode);
}
@@ -495,6 +501,7 @@ class ZipFile implements ZipFileInterface
/** @noinspection PhpUsageOfSilenceOperatorInspection */
if (!($handle = @fopen($file, 'w+b'))) {
// @codeCoverageIgnoreStart
throw new ZipException(
sprintf(
'Cannot extract zip entry %s. File %s cannot open for write.',
@@ -502,12 +509,15 @@ class ZipFile implements ZipFileInterface
$file
)
);
// @codeCoverageIgnoreEnd
}
try {
$zipData->copyDataToStream($handle);
} catch (ZipException $e) {
unlink($file);
if (is_file($file)) {
@unlink($file);
}
throw $e;
}
@@ -562,19 +572,12 @@ class ZipFile implements ZipFileInterface
*/
public function addFromString($entryName, $contents, $compressionMethod = null)
{
if ($entryName === null) {
throw new InvalidArgumentException('Entry name is null');
}
$entryName = $this->normalizeEntryName($entryName);
if ($contents === null) {
throw new InvalidArgumentException('Contents is null');
}
$entryName = ltrim((string) $entryName, '\\/');
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
$contents = (string) $contents;
$length = \strlen($contents);
@@ -583,9 +586,9 @@ class ZipFile implements ZipFileInterface
$compressionMethod = ZipCompressionMethod::STORED;
} else {
$mimeType = FilesUtil::getMimeTypeFromString($contents);
$compressionMethod = FilesUtil::isBadCompressionMimeType($mimeType) ?
ZipCompressionMethod::STORED :
ZipCompressionMethod::DEFLATED;
$compressionMethod = FilesUtil::isBadCompressionMimeType($mimeType)
? ZipCompressionMethod::STORED
: ZipCompressionMethod::DEFLATED;
}
}
@@ -603,6 +606,30 @@ class ZipFile implements ZipFileInterface
return $this;
}
/**
* @param string $entryName
*
* @return string
*/
protected function normalizeEntryName($entryName)
{
if ($entryName === null) {
throw new InvalidArgumentException('Entry name is null');
}
$entryName = ltrim((string) $entryName, '\\/');
if (\DIRECTORY_SEPARATOR === '\\') {
$entryName = str_replace('\\', '/', $entryName);
}
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
return $entryName;
}
/**
* @param Finder $finder
* @param array $options
@@ -618,6 +645,7 @@ class ZipFile implements ZipFileInterface
ZipOptions::COMPRESSION_METHOD => null,
ZipOptions::MODIFIED_TIME => null,
];
/** @noinspection AdditionOperationOnArraysInspection */
$options += $defaultOptions;
if ($options[ZipOptions::STORE_ONLY_FILES]) {
@@ -654,6 +682,7 @@ class ZipFile implements ZipFileInterface
ZipOptions::COMPRESSION_METHOD => null,
ZipOptions::MODIFIED_TIME => null,
];
/** @noinspection AdditionOperationOnArraysInspection */
$options += $defaultOptions;
if (!$file->isReadable()) {
@@ -668,12 +697,7 @@ class ZipFile implements ZipFileInterface
}
}
$entryName = ltrim((string) $entryName, '\\/');
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
$entryName = $this->normalizeEntryName($entryName);
$entryName = $file->isDir() ? rtrim($entryName, '/\\') . '/' : $entryName;
$zipEntry = new ZipEntry($entryName);
@@ -700,15 +724,14 @@ class ZipFile implements ZipFileInterface
} elseif ($file->getSize() < 512) {
$compressionMethod = ZipCompressionMethod::STORED;
} else {
$compressionMethod = FilesUtil::isBadCompressionFile($file->getPathname()) ?
ZipCompressionMethod::STORED :
ZipCompressionMethod::DEFLATED;
$compressionMethod = FilesUtil::isBadCompressionFile($file->getPathname())
? ZipCompressionMethod::STORED
: ZipCompressionMethod::DEFLATED;
}
$zipEntry->setUncompressedSize($file->getSize());
$zipEntry->setCompressionMethod($compressionMethod);
$zipData = new ZipFileData($file);
$zipData = new ZipFileData($zipEntry, $file);
} elseif ($file->isDir()) {
$zipEntry->setCompressionMethod(ZipCompressionMethod::STORED);
$zipEntry->setUncompressedSize(0);
@@ -809,17 +832,9 @@ class ZipFile implements ZipFileInterface
throw new InvalidArgumentException('Stream is not resource');
}
if ($entryName === null) {
throw new InvalidArgumentException('Entry name is null');
}
$entryName = ltrim((string) $entryName, '\\/');
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
$fstat = fstat($stream);
$entryName = $this->normalizeEntryName($entryName);
$zipEntry = new ZipEntry($entryName);
$fstat = fstat($stream);
if ($fstat !== false) {
$unixMode = $fstat['mode'];
@@ -833,9 +848,9 @@ class ZipFile implements ZipFileInterface
$bufferContents = stream_get_contents($stream, min(1024, $length));
rewind($stream);
$mimeType = FilesUtil::getMimeTypeFromString($bufferContents);
$compressionMethod = FilesUtil::isBadCompressionMimeType($mimeType) ?
ZipCompressionMethod::STORED :
ZipCompressionMethod::DEFLATED;
$compressionMethod = FilesUtil::isBadCompressionMimeType($mimeType)
? ZipCompressionMethod::STORED
: ZipCompressionMethod::DEFLATED;
}
$zipEntry->setUncompressedSize($length);
}
@@ -870,14 +885,7 @@ class ZipFile implements ZipFileInterface
*/
public function addEmptyDir($dirName)
{
if ($dirName === null) {
throw new InvalidArgumentException('Dir name is null');
}
$dirName = ltrim((string) $dirName, '\\/');
if ($dirName === '') {
throw new InvalidArgumentException('Empty dir name');
}
$dirName = $this->normalizeEntryName($dirName);
$dirName = rtrim($dirName, '\\/') . '/';
$zipEntry = new ZipEntry($dirName);
@@ -1001,9 +1009,9 @@ class ZipFile implements ZipFileInterface
$localPath = '';
}
$iterator = $iterator instanceof \RecursiveIterator ?
new \RecursiveIteratorIterator($iterator) :
new \IteratorIterator($iterator);
$iterator = $iterator instanceof \RecursiveIterator
? new \RecursiveIteratorIterator($iterator)
: new \IteratorIterator($iterator);
/**
* @var string[] $files
* @var string $path
@@ -1072,6 +1080,7 @@ class ZipFile implements ZipFileInterface
* @throws ZipException
*
* @return ZipFile
*
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
*/
private function addGlob(
@@ -1599,19 +1608,39 @@ class ZipFile implements ZipFileInterface
{
$filename = (string) $filename;
$tempFilename = $filename . '.temp' . uniqid('', true);
$tempFilename = $filename . '.temp' . uniqid('', false);
if (!($handle = @fopen($tempFilename, 'w+b'))) {
throw new InvalidArgumentException('File ' . $tempFilename . ' can not open from write.');
throw new InvalidArgumentException(sprintf('Cannot open "%s" for writing.', $tempFilename));
}
$this->saveAsStream($handle);
$reopen = false;
if ($this->reader !== null) {
$meta = $this->reader->getStreamMetaData();
if ($meta['wrapper_type'] === 'plainfile' && isset($meta['uri'])) {
$readFilePath = realpath($meta['uri']);
$writeFilePath = realpath($filename);
if ($readFilePath !== false && $writeFilePath !== false && $readFilePath === $writeFilePath) {
$this->reader->close();
$reopen = true;
}
}
}
if (!@rename($tempFilename, $filename)) {
if (is_file($tempFilename)) {
unlink($tempFilename);
@unlink($tempFilename);
}
throw new ZipException('Can not move ' . $tempFilename . ' to ' . $filename);
throw new ZipException(sprintf('Cannot move %s to %s', $tempFilename, $filename));
}
if ($reopen) {
return $this->openFile($filename);
}
return $this;
@@ -1817,32 +1846,11 @@ class ZipFile implements ZipFileInterface
$meta = $this->reader->getStreamMetaData();
if ($meta['wrapper_type'] === 'plainfile' && isset($meta['uri'])) {
$this->saveAsFile($meta['uri']);
$this->close();
if (!($handle = @fopen($meta['uri'], 'rb'))) {
throw new ZipException("File {$meta['uri']} can't open.");
}
} else {
$handle = @fopen('php://temp', 'r+b');
if (!$handle) {
throw new ZipException('php://temp cannot open for write.');
}
$this->writeZipToStream($handle);
$this->close();
if ($meta['wrapper_type'] !== 'plainfile' || !isset($meta['uri'])) {
throw new ZipException('Overwrite is only supported for open local files.');
}
return $this->openFromStream($handle);
}
/**
* Release all resources.
*/
public function __destruct()
{
$this->close();
return $this->saveAsFile($meta['uri']);
}
/**

View File

@@ -295,7 +295,7 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
* @param string $destDir location where to extract the files
* @param array|string|null $entries entries to extract
* @param array $options extract options
* @param array $extractedEntries if the extractedEntries argument
* @param array|null $extractedEntries if the extractedEntries argument
* is present, then the specified
* array will be filled with
* information about the

View File

@@ -76,8 +76,10 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
*/
public function testUnicodeErrorParse()
{
$this->setExpectedException(
ZipException::class,
$this->expectException(
ZipException::class
);
$this->expectExceptionMessage(
'Unicode path extra data must have at least 5 bytes.'
);
@@ -90,8 +92,10 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
*/
public function testUnknownVersionParse()
{
$this->setExpectedException(
ZipException::class,
$this->expectException(
ZipException::class
);
$this->expectExceptionMessage(
'Unsupported version [2] for Unicode path extra data.'
);

View File

@@ -96,8 +96,10 @@ final class ApkAlignmentExtraFieldTest extends TestCase
*/
public function testInvalidParse()
{
$this->setExpectedException(
ZipException::class,
$this->expectException(
ZipException::class
);
$this->expectExceptionMessage(
'Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.'
);

View File

@@ -91,8 +91,10 @@ final class AsiExtraFieldTest extends TestCase
*/
public function testInvalidParse()
{
$this->setExpectedException(
Crc32Exception::class,
$this->expectException(
Crc32Exception::class
);
$this->expectExceptionMessage(
'Asi Unix Extra Filed Data (expected CRC32 value'
);

View File

@@ -66,9 +66,9 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
{
return [
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT |
ExtendedTimestampExtraField::CREATE_TIME_BIT,
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
| ExtendedTimestampExtraField::CREATE_TIME_BIT,
911512006,
911430000,
893709400,
@@ -76,8 +76,8 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
"\x07\xC6\x91T6",
],
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT,
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT,
1492955702,
1492955638,
null,
@@ -115,8 +115,8 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
$field->setAccessTime($atime);
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
);
self::assertSame($field->getModifyTime(), $mtime);
self::assertSame($field->getAccessTime(), $atime);
@@ -127,9 +127,9 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
$field->setCreateTime($ctime);
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT |
ExtendedTimestampExtraField::CREATE_TIME_BIT
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
| ExtendedTimestampExtraField::CREATE_TIME_BIT
);
self::assertSame($field->getModifyTime(), $mtime);
self::assertSame($field->getAccessTime(), $atime);
@@ -141,8 +141,8 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
self::assertNull($field->getCreateDateTime());
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT
ExtendedTimestampExtraField::MODIFY_TIME_BIT
| ExtendedTimestampExtraField::ACCESS_TIME_BIT
);
$field->setAccessTime(null);

View File

@@ -32,8 +32,10 @@ final class JarMarkerExtraFieldTest extends TestCase
*/
public function testInvalidUnpackLocalData()
{
$this->setExpectedException(
ZipException::class,
$this->expectException(
ZipException::class
);
$this->expectExceptionMessage(
"JarMarker doesn't expect any data"
);
@@ -45,8 +47,10 @@ final class JarMarkerExtraFieldTest extends TestCase
*/
public function testInvalidUnpackCdData()
{
$this->setExpectedException(
ZipException::class,
$this->expectException(
ZipException::class
);
$this->expectExceptionMessage(
"JarMarker doesn't expect any data"
);

View File

@@ -14,8 +14,10 @@ use PhpZip\Model\Extra\Fields\NtfsExtraField;
*/
final class NtfsExtraFieldTest extends TestCase
{
protected function setUp()
protected function setUp(): void
{
parent::setUp();
if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test');
}
@@ -156,9 +158,9 @@ final class NtfsExtraFieldTest extends TestCase
$atimeTimestamp,
$ctimeTimestamp
) {
self::assertEquals(NtfsExtraField::ntfsTimeToTimestamp($mtimeNtfs), $mtimeTimestamp, '', 0.00001);
self::assertEquals(NtfsExtraField::ntfsTimeToTimestamp($atimeNtfs), $atimeTimestamp, '', 0.00001);
self::assertEquals(NtfsExtraField::ntfsTimeToTimestamp($ctimeNtfs), $ctimeTimestamp, '', 0.00001);
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);

View File

@@ -12,7 +12,7 @@ use PhpZip\Model\Extra\Fields\UnicodeCommentExtraField;
final class UnicodeCommentExtraFieldTest extends AbstractUnicodeExtraFieldTest
{
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function getUnicodeExtraFieldClassName()
{

View File

@@ -14,7 +14,7 @@ use PhpZip\Model\Extra\Fields\UnicodePathExtraField;
final class UnicodePathExtraFieldTest extends AbstractUnicodeExtraFieldTest
{
/**
* {@inheritdoc}
* {@inheritDoc}
*/
protected function getUnicodeExtraFieldClassName()
{

View File

@@ -37,8 +37,10 @@ final class UnrecognizedExtraFieldTest extends TestCase
public function testUnpackLocalData()
{
$this->setExpectedException(
RuntimeException::class,
$this->expectException(
RuntimeException::class
);
$this->expectExceptionMessage(
'Unsupport parse'
);
@@ -47,8 +49,10 @@ final class UnrecognizedExtraFieldTest extends TestCase
public function testUnpackCentralDirData()
{
$this->setExpectedException(
RuntimeException::class,
$this->expectException(
RuntimeException::class
);
$this->expectExceptionMessage(
'Unsupport parse'
);

View File

@@ -157,7 +157,8 @@ final class WinZipAesExtraFieldTest extends TestCase
*/
public function testConstructUnsupportVendorVersion()
{
$this->setExpectedException(InvalidArgumentException::class, 'Unsupport WinZip AES vendor version: 3');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3');
new WinZipAesExtraField(
3,
@@ -171,7 +172,8 @@ final class WinZipAesExtraFieldTest extends TestCase
*/
public function testSetterUnsupportVendorVersion()
{
$this->setExpectedException(InvalidArgumentException::class, 'Unsupport WinZip AES vendor version: 3');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3');
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
@@ -186,7 +188,8 @@ final class WinZipAesExtraFieldTest extends TestCase
*/
public function testConstructUnsupportCompressionMethod()
{
$this->setExpectedException(ZipUnsupportMethodException::class, 'Compression method 3 (Reduced compression factor 2) is not supported.');
$this->expectException(ZipUnsupportMethodException::class);
$this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
@@ -200,7 +203,8 @@ final class WinZipAesExtraFieldTest extends TestCase
*/
public function testSetterUnsupportCompressionMethod()
{
$this->setExpectedException(ZipUnsupportMethodException::class, 'Compression method 3 (Reduced compression factor 2) is not supported.');
$this->expectException(ZipUnsupportMethodException::class);
$this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.');
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
@@ -215,7 +219,8 @@ final class WinZipAesExtraFieldTest extends TestCase
*/
public function testConstructUnsupportKeyStrength()
{
$this->setExpectedException(InvalidArgumentException::class, 'Key strength 16 not support value. Allow values: 1, 2, 3');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
@@ -229,7 +234,8 @@ final class WinZipAesExtraFieldTest extends TestCase
*/
public function testSetterUnsupportKeyStrength()
{
$this->setExpectedException(InvalidArgumentException::class, 'Key strength 16 not support value. Allow values: 1, 2, 3');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,

View File

@@ -15,8 +15,10 @@ use PhpZip\Model\ZipEntry;
*/
final class Zip64ExtraFieldTest extends TestCase
{
protected function setUp()
protected function setUp(): void
{
parent::setUp();
if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test');
}

View File

@@ -40,7 +40,7 @@ class DummyFileSystemStream
public function stream_open($path, $mode, $options, &$opened_path)
{
$parsedUrl = parse_url($path);
$uri = str_replace('//', '/', $parsedUrl['path']);
$uri = substr($parsedUrl['path'], 1);
$this->fp = @fopen($uri, $mode);
return $this->fp !== false;

View File

@@ -21,7 +21,7 @@ class Issue24Test extends ZipTestCase
*
* @noinspection PhpMissingParentCallCommonInspection
*/
public static function setUpBeforeClass()
public static function setUpBeforeClass(): void
{
stream_wrapper_register(self::PROTO_DUMMYFS, DummyFileSystemStream::class);
}

View File

@@ -15,7 +15,7 @@ use PhpZip\ZipFile;
*
* @small
*/
class PhpZipExtResourceTest extends ZipTestCase
final class PhpZipExtResourceTest extends ZipTestCase
{
/**
* Bug #7214 (zip_entry_read() binary safe).
@@ -33,11 +33,11 @@ class PhpZipExtResourceTest extends ZipTestCase
foreach ($zipFile as $name => $contents) {
$info = $zipFile->getEntryInfo($name);
static::assertSame(\strlen($contents), $info->getSize());
self::assertSame(\strlen($contents), $info->getSize());
}
$zipFile->close();
static::assertCorrectZipArchive($filename);
self::assertCorrectZipArchive($filename);
}
/**
@@ -57,13 +57,13 @@ class PhpZipExtResourceTest extends ZipTestCase
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
static::assertCorrectZipArchive($this->outputFilename);
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
static::assertCount(2, $zipFile);
static::assertTrue(isset($zipFile['1.txt']));
static::assertTrue(isset($zipFile['2.txt']));
static::assertSame($zipFile['2.txt'], $zipFile['1.txt']);
self::assertCount(2, $zipFile);
self::assertTrue(isset($zipFile['1.txt']));
self::assertTrue(isset($zipFile['2.txt']));
self::assertSame($zipFile['2.txt'], $zipFile['1.txt']);
$zipFile->close();
}
@@ -80,14 +80,14 @@ class PhpZipExtResourceTest extends ZipTestCase
*/
public function testBug40228($filename)
{
static::assertTrue(mkdir($this->outputDirname, 0755, true));
self::assertTrue(mkdir($this->outputDirname, 0755, true));
$zipFile = new ZipFile();
$zipFile->openFile($filename);
$zipFile->extractTo($this->outputDirname);
$zipFile->close();
static::assertTrue(is_dir($this->outputDirname . '/test/empty'));
self::assertDirectoryExists($this->outputDirname . '/test/empty');
}
/**
@@ -109,7 +109,8 @@ class PhpZipExtResourceTest extends ZipTestCase
*/
public function testBug49072()
{
$this->setExpectedException(Crc32Exception::class, 'file1');
$this->expectException(Crc32Exception::class);
$this->expectExceptionMessage('file1');
$filename = __DIR__ . '/resources/pecl/bug49072.zip';
@@ -128,20 +129,16 @@ class PhpZipExtResourceTest extends ZipTestCase
public function testBug70752()
{
if (\PHP_INT_SIZE === 4) { // php 32 bit
$this->setExpectedException(
RuntimeException::class,
'Traditional PKWARE Encryption is not supported in 32-bit PHP.'
);
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Traditional PKWARE Encryption is not supported in 32-bit PHP.');
} else { // php 64 bit
$this->setExpectedException(
ZipAuthenticationException::class,
'Invalid password'
);
$this->expectException(ZipAuthenticationException::class);
$this->expectExceptionMessage('Invalid password');
}
$filename = __DIR__ . '/resources/pecl/bug70752.zip';
static::assertTrue(mkdir($this->outputDirname, 0755, true));
self::assertTrue(mkdir($this->outputDirname, 0755, true));
$zipFile = new ZipFile();
$zipFile->openFile($filename);
@@ -149,13 +146,11 @@ class PhpZipExtResourceTest extends ZipTestCase
try {
$zipFile->extractTo($this->outputDirname);
static::markTestIncomplete('failed test');
self::markTestIncomplete('failed test');
} catch (ZipException $exception) {
static::assertFileNotExists($this->outputDirname . '/bug70752.txt');
self::assertFileDoesNotExist($this->outputDirname . '/bug70752.txt');
throw $exception;
} finally {
$zipFile->close();
}
}
@@ -168,7 +163,8 @@ class PhpZipExtResourceTest extends ZipTestCase
*/
public function testPecl12414()
{
$this->setExpectedException(ZipException::class, 'Corrupt zip file. Cannot read zip entry.');
$this->expectException(ZipException::class);
$this->expectExceptionMessage('Corrupt zip file. Cannot read zip entry.');
$filename = __DIR__ . '/resources/pecl/pecl12414.zip';

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace PhpZip\Tests\Polyfill;
use PHPUnit\Framework\TestCase;
/**
* @internal
*
* @small
*/
class LegacyTestCase extends TestCase
{
use PhpUnit8CompatTrait;
use PhpUnit9CompatTrait;
}

View File

@@ -0,0 +1,22 @@
<?php
namespace PhpZip\Tests\Polyfill;
use PHPUnit\Runner\Version;
trait PhpUnit8CompatTrait
{
/**
* @param string $regularExpression
*/
public function expectExceptionMessageMatches(string $regularExpression): void
{
if (version_compare(Version::id(), '8.0.0', '<')) {
$this->expectExceptionMessageRegExp($regularExpression);
return;
}
parent::expectExceptionMessageMatches($regularExpression);
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace PhpZip\Tests\Polyfill;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Runner\Version;
use SebastianBergmann\RecursionContext\InvalidArgumentException;
trait PhpUnit9CompatTrait
{
/**
* Asserts that a file does not exist.
*
* @param string $filename
* @param string $message
*
* @throws InvalidArgumentException
* @throws ExpectationFailedException
*
* @noinspection PhpDeprecationInspection
*/
public static function assertFileDoesNotExist(string $filename, string $message = ''): void
{
if (version_compare(Version::id(), '9.1.0', '<')) {
self::assertFileNotExists($filename, $message);
return;
}
parent::assertFileDoesNotExist($filename, $message);
}
/**
* Asserts that a directory does not exist.
*
* @noinspection PhpDeprecationInspection
*
* @param string $directory
* @param string $message
*
* @throws ExpectationFailedException
* @throws InvalidArgumentException
*/
public static function assertDirectoryDoesNotExist(string $directory, string $message = ''): void
{
if (version_compare(Version::id(), '9.1.0', '<')) {
self::assertDirectoryNotExists($directory, $message);
return;
}
parent::assertDirectoryDoesNotExist($directory, $message);
}
}

View File

@@ -76,7 +76,7 @@ class Zip64Test extends ZipTestCase
self::assertCorrectZipArchive($this->outputFilename);
if (!is_dir($this->outputDirname)) {
mkdir($this->outputDirname, 0755, true);
static::assertTrue(mkdir($this->outputDirname, 0755, true));
}
$zipFile->openFile($this->outputFilename);

View File

@@ -12,22 +12,8 @@ use Symfony\Component\Finder\Finder;
*
* @small
*/
final class SymlinkTest extends ZipFileTest
final class SymlinkTest extends ZipTestCase
{
/**
* This method is called before the first test of this test class is run.
*/
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
if (\DIRECTORY_SEPARATOR === '\\') {
self::markTestSkipped('only linux test');
return;
}
}
/**
* @dataProvider provideAllowSymlink
*
@@ -37,6 +23,8 @@ final class SymlinkTest extends ZipFileTest
*/
public function testSymlink($allowSymlink)
{
self::skipTestForWindows();
if (!is_dir($this->outputDirname)) {
self::assertTrue(mkdir($this->outputDirname, 0755, true));
}
@@ -57,7 +45,7 @@ final class SymlinkTest extends ZipFileTest
self::assertCorrectZipArchive($this->outputFilename);
FilesUtil::removeDir($this->outputDirname);
self::assertFalse(is_dir($this->outputDirname));
self::assertDirectoryDoesNotExist($this->outputDirname);
self::assertTrue(mkdir($this->outputDirname, 0755, true));
$zipFile->openFile($this->outputFilename);

View File

@@ -55,7 +55,7 @@ class ZipAlignTest extends ZipTestCase
for ($i = 0; $i < 100; $i++) {
$zipFile->addFromString(
'entry' . $i . '.txt',
random_bytes(mt_rand(100, 4096)),
random_bytes(random_int(100, 4096)),
ZipCompressionMethod::STORED
);
}
@@ -97,7 +97,7 @@ class ZipAlignTest extends ZipTestCase
for ($i = 0; $i < 100; $i++) {
$zipFile->addFromString(
'entry' . $i . '.txt',
random_bytes(mt_rand(100, 4096)),
random_bytes(random_int(100, 4096)),
ZipCompressionMethod::STORED
);
}
@@ -126,7 +126,7 @@ class ZipAlignTest extends ZipTestCase
for ($i = 0; $i < 100; $i++) {
$zipFile->addFromString(
'entry' . $i . '.txt',
random_bytes(mt_rand(100, 4096)),
random_bytes(random_int(100, 4096)),
ZipCompressionMethod::STORED
);
}
@@ -147,14 +147,14 @@ class ZipAlignTest extends ZipTestCase
$zipFile->openFile($this->outputFilename);
$zipFile->deleteFromRegex('~entry2[\\d]+\\.txt$~s');
for ($i = 0; $i < 100; $i++) {
$isStored = (bool) mt_rand(0, 1);
$isStored = (bool) random_int(0, 1);
$zipFile->addFromString(
'entry_new_' . ($isStored ? 'stored' : 'deflated') . '_' . $i . '.txt',
random_bytes(mt_rand(100, 4096)),
$isStored ?
ZipCompressionMethod::STORED :
ZipCompressionMethod::DEFLATED
random_bytes(random_int(100, 4096)),
$isStored
? ZipCompressionMethod::STORED
: ZipCompressionMethod::DEFLATED
);
}
$zipFile->setZipAlign(4);

View File

@@ -89,7 +89,8 @@ class ZipEntryTest extends TestCase
*/
public function testEmptyName($entryName, $exceptionMessage)
{
$this->setExpectedException(InvalidArgumentException::class, $exceptionMessage);
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage($exceptionMessage);
new ZipEntry($entryName);
}
@@ -174,7 +175,8 @@ class ZipEntryTest extends TestCase
*/
public function testOutOfRangeCompressionMethod($compressionMethod)
{
$this->setExpectedException(InvalidArgumentException::class, 'method out of range: ' . $compressionMethod);
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('method out of range: ' . $compressionMethod);
$zipEntry = new ZipEntry('entry');
$zipEntry->setCompressionMethod($compressionMethod);
@@ -201,7 +203,8 @@ class ZipEntryTest extends TestCase
*/
public function testUnsupportCompressionMethod($compressionMethod, $exceptionMessage)
{
$this->setExpectedException(ZipUnsupportMethodException::class, $exceptionMessage);
$this->expectException(ZipUnsupportMethodException::class);
$this->expectExceptionMessage($exceptionMessage);
$zipEntry = new ZipEntry('entry');
$zipEntry->setCompressionMethod($compressionMethod);
@@ -253,7 +256,8 @@ class ZipEntryTest extends TestCase
public function testEmptyCharset()
{
$this->setExpectedException(InvalidArgumentException::class, 'Empty charset');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Empty charset');
$zipEntry = new ZipEntry('entry');
$zipEntry->setCharset('');
@@ -398,7 +402,8 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidCreatedOs($zipOS)
{
$this->setExpectedException(InvalidArgumentException::class, 'Platform out of range');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Platform out of range');
$zipEntry = new ZipEntry('entry');
$zipEntry->setCreatedOS($zipOS);
@@ -422,7 +427,8 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidExtractedOs($zipOS)
{
$this->setExpectedException(InvalidArgumentException::class, 'Platform out of range');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Platform out of range');
$zipEntry = new ZipEntry('entry');
$zipEntry->setExtractedOS($zipOS);
@@ -545,7 +551,8 @@ class ZipEntryTest extends TestCase
public function testInvalidCompressedSize()
{
$this->setExpectedException(InvalidArgumentException::class, 'Compressed size < -1');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Compressed size < -1');
$zipEntry = new ZipEntry('entry');
$zipEntry->setCompressedSize(-2);
@@ -553,7 +560,8 @@ class ZipEntryTest extends TestCase
public function testInvalidUncompressedSize()
{
$this->setExpectedException(InvalidArgumentException::class, 'Uncompressed size < -1');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Uncompressed size < -1');
$zipEntry = new ZipEntry('entry');
$zipEntry->setUncompressedSize(-2);
@@ -568,7 +576,8 @@ class ZipEntryTest extends TestCase
$zipEntry->setLocalHeaderOffset($localHeaderOffset);
static::assertSame($zipEntry->getLocalHeaderOffset(), $localHeaderOffset);
$this->setExpectedException(InvalidArgumentException::class, 'Negative $localHeaderOffset');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Negative $localHeaderOffset');
$zipEntry->setLocalHeaderOffset(-1);
}
@@ -652,7 +661,8 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidGPBF($gpbf)
{
$this->setExpectedException(InvalidArgumentException::class, 'general purpose bit flags out of range');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('general purpose bit flags out of range');
$zipEntry = new ZipEntry('entry');
$zipEntry->setGeneralPurposeBitFlags($gpbf);
@@ -683,8 +693,8 @@ class ZipEntryTest extends TestCase
$zipEntry = new ZipEntry('entry');
$zipEntry->setCompressionMethod(ZipCompressionMethod::DEFLATED);
$gpbf = ($bit1 ? GeneralPurposeBitFlag::COMPRESSION_FLAG1 : 0) |
($bit2 ? GeneralPurposeBitFlag::COMPRESSION_FLAG2 : 0);
$gpbf = ($bit1 ? GeneralPurposeBitFlag::COMPRESSION_FLAG1 : 0)
| ($bit2 ? GeneralPurposeBitFlag::COMPRESSION_FLAG2 : 0);
$zipEntry->setGeneralPurposeBitFlags($gpbf);
static::assertSame($zipEntry->getCompressionLevel(), $compressionLevel);
@@ -791,10 +801,12 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidCompressionLevel($compressionLevel)
{
$this->setExpectedException(
InvalidArgumentException::class,
'Invalid compression level. Minimum level ' . ZipCompressionLevel::LEVEL_MIN .
'. Maximum level ' . ZipCompressionLevel::LEVEL_MAX
$this->expectException(
InvalidArgumentException::class
);
$this->expectExceptionMessage(
'Invalid compression level. Minimum level ' . ZipCompressionLevel::LEVEL_MIN
. '. Maximum level ' . ZipCompressionLevel::LEVEL_MAX
);
$zipEntry = new ZipEntry('entry');
@@ -855,7 +867,8 @@ class ZipEntryTest extends TestCase
return;
}
$this->setExpectedException(InvalidArgumentException::class, 'DosTime out of range');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('DosTime out of range');
$zipEntry = new ZipEntry('entry');
$zipEntry->setDosTime($dosTime);
@@ -1012,7 +1025,8 @@ class ZipEntryTest extends TestCase
return;
}
$this->setExpectedException(InvalidArgumentException::class, 'external attributes out of range');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('external attributes out of range');
$zipEntry = new ZipEntry('entry');
$zipEntry->setExternalAttributes($externalAttributes);
@@ -1045,7 +1059,8 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidInternalAttributes($internalAttributes)
{
$this->setExpectedException(InvalidArgumentException::class, 'internal attributes out of range');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('internal attributes out of range');
$zipEntry = new ZipEntry('entry');
$zipEntry->setInternalAttributes($internalAttributes);
@@ -1140,7 +1155,8 @@ class ZipEntryTest extends TestCase
*/
public function testLongComment()
{
$this->setExpectedException(InvalidArgumentException::class, 'Comment too long');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Comment too long');
$longComment = random_bytes(0xffff + 1);
$zipEntry = new ZipEntry('entry');
@@ -1305,8 +1321,10 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidEncryptionMethod($encryptionMethod)
{
$this->setExpectedException(
InvalidArgumentException::class,
$this->expectException(
InvalidArgumentException::class
);
$this->expectExceptionMessage(
'Encryption method ' . $encryptionMethod . ' is not supported.'
);
@@ -1456,7 +1474,7 @@ class ZipEntryTest extends TestCase
* @param \DateTimeInterface|null $atime
* @param \DateTimeInterface|null $ctime
*/
public function testMTimeATimeCTime(ExtraFieldsCollection $extraFieldsCollection, $mtime, $atime, $ctime)
public function testMTimeATimeCTime(ExtraFieldsCollection $extraFieldsCollection, \DateTimeInterface $mtime, \DateTimeInterface $atime = null, \DateTimeInterface $ctime = null)
{
$unixTimestamp = time();
@@ -1571,9 +1589,10 @@ class ZipEntryTest extends TestCase
public function testClone()
{
$newUnixExtra = new NewUnixExtraField();
$zipData = new ZipFileData(new \SplFileInfo(__FILE__));
$zipEntry = new ZipEntry('entry');
$zipData = new ZipFileData($zipEntry, new \SplFileInfo(__FILE__));
$zipEntry->addExtraField($newUnixExtra);
$zipEntry->setData($zipData);

View File

@@ -30,7 +30,7 @@ abstract class ZipFileSetTestCase extends ZipTestCase
/**
* Before test.
*/
protected function setUp()
protected function setUp(): void
{
parent::setUp();
$this->fillDirectory();
@@ -75,7 +75,7 @@ abstract class ZipFileSetTestCase extends ZipTestCase
$zipEntryName = $localPath . $file;
if (isset($actualResultFiles[$file])) {
static::assertTrue(isset($zipFile[$zipEntryName]));
static::assertTrue(isset($zipFile[$zipEntryName]), 'Not found entry name ' . $zipEntryName);
static::assertSame(
$zipFile[$zipEntryName],
$content,

File diff suppressed because it is too large Load Diff

118
tests/ZipInfoTest.php Normal file
View File

@@ -0,0 +1,118 @@
<?php
namespace PhpZip\Tests;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipEncryptionMethod;
use PhpZip\Constants\ZipPlatform;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\ZipInfo;
use PhpZip\ZipFile;
/**
* Testing the {@see ZipInfo} class.
*
* {@see ZipInfo} is {@deprecated}. Use the {@see ZipEntry} class.
*
* @internal
*
* @small
*/
final class ZipInfoTest extends ZipTestCase
{
public function testZipAllInfo()
{
$zipFile = new ZipFile();
$zipFile['entry'] = 'contents';
$zipFile['entry 2'] = 'contents';
$zipAllInfo = $zipFile->getAllInfo();
$zipFile->close();
self::assertCount(2, $zipAllInfo);
self::assertContainsOnlyInstancesOf(ZipInfo::class, $zipAllInfo);
}
/**
* @throws ZipEntryNotFoundException
* @throws ZipException
*/
public function testZipEntryInfo()
{
$zipFile = new ZipFile();
$zipFile['entry'] = 'contents';
$zipFile['entry 2'] = 'contents';
$zipInfo = $zipFile->getEntryInfo('entry');
$zipFile->close();
self::assertInstanceOf(ZipInfo::class, $zipInfo);
}
/**
* @throws ZipEntryNotFoundException
* @throws ZipException
*/
public function testZipInfoEntryNotFound()
{
$this->expectException(
ZipEntryNotFoundException::class
);
$this->expectExceptionMessage(
'Zip Entry "unknown.name" was not found in the archive.'
);
$zipFile = new ZipFile();
$zipFile->getEntryInfo('unknown.name');
}
/**
* @throws ZipEntryNotFoundException
* @throws ZipException
*/
public function testZipInfo()
{
$zipFile = new ZipFile();
$zipFile->openFile(__DIR__ . '/resources/Advanced-v1.0.0.epub');
$entryName = 'META-INF/container.xml';
$zipEntry = $zipFile->getEntry($entryName);
$zipInfo = $zipFile->getEntryInfo($entryName);
$zipFile->close();
self::assertSame($zipInfo->getName(), $zipEntry->getName());
self::assertSame($zipInfo->isFolder(), $zipEntry->isDirectory());
self::assertSame($zipInfo->getSize(), $zipEntry->getUncompressedSize());
self::assertSame($zipInfo->getCompressedSize(), $zipEntry->getCompressedSize());
self::assertSame($zipInfo->getMtime(), $zipEntry->getMTime()->getTimestamp());
self::assertSame(
$zipInfo->getCtime(),
$zipEntry->getCTime() !== null ? $zipEntry->getCTime()->getTimestamp() : null
);
self::assertSame(
$zipInfo->getAtime(),
$zipEntry->getATime() !== null ? $zipEntry->getATime()->getTimestamp() : null
);
self::assertNotEmpty($zipInfo->getAttributes());
self::assertSame($zipInfo->isEncrypted(), $zipEntry->isEncrypted());
self::assertSame($zipInfo->getComment(), $zipEntry->getComment());
self::assertSame($zipInfo->getCrc(), $zipEntry->getCrc());
self::assertSame(
$zipInfo->getMethod(),
ZipCompressionMethod::getCompressionMethodName($zipEntry->getCompressionMethod())
);
self::assertSame(
$zipInfo->getMethodName(),
ZipCompressionMethod::getCompressionMethodName($zipEntry->getCompressionMethod())
);
self::assertSame(
$zipInfo->getEncryptionMethodName(),
ZipEncryptionMethod::getEncryptionMethodName($zipEntry->getEncryptionMethod())
);
self::assertSame($zipInfo->getPlatform(), ZipPlatform::getPlatformName($zipEntry->getExtractedOS()));
self::assertSame(ZipInfo::getPlatformName($zipEntry), ZipPlatform::getPlatformName($zipEntry->getExtractedOS()));
self::assertSame($zipInfo->getVersion(), $zipEntry->getExtractVersion());
self::assertNull($zipInfo->getEncryptionMethod());
self::assertSame($zipInfo->getCompressionLevel(), $zipEntry->getCompressionLevel());
self::assertSame($zipInfo->getCompressionMethod(), $zipEntry->getCompressionMethod());
self::assertNotEmpty($zipInfo->toArray());
}
}

View File

@@ -24,7 +24,7 @@ class ZipMatcherTest extends TestCase
$matcher = $zipFile->matcher();
static::assertInstanceOf(ZipEntryMatcher::class, $matcher);
static::assertInternalType('array', $matcher->getMatches());
static::assertIsArray($matcher->getMatches());
static::assertCount(0, $matcher);
$matcher->add(1)->add(10)->add(20);

View File

@@ -31,10 +31,8 @@ class ZipPasswordTest extends ZipFileSetTestCase
public function testSetPassword()
{
if (\PHP_INT_SIZE === 4) { // php 32 bit
$this->setExpectedException(
RuntimeException::class,
'Traditional PKWARE Encryption is not supported in 32-bit PHP.'
);
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('Traditional PKWARE Encryption is not supported in 32-bit PHP.');
}
$password = base64_encode(random_bytes(100));
@@ -66,10 +64,10 @@ class ZipPasswordTest extends ZipFileSetTestCase
foreach ($zipFile->getAllInfo() as $info) {
static::assertTrue($info->isEncrypted());
static::assertContains('Traditional PKWARE encryption', $info->getEncryptionMethodName());
static::assertStringContainsString('Traditional PKWARE encryption', $info->getEncryptionMethodName());
$decryptContent = $zipFile[$info->getName()];
static::assertNotEmpty($decryptContent);
static::assertContains('<?php', $decryptContent);
static::assertStringContainsString('<?php', $decryptContent);
}
// change encryption method to WinZip Aes and update file
@@ -77,7 +75,8 @@ class ZipPasswordTest extends ZipFileSetTestCase
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
static::assertCorrectZipArchive($this->outputFilename, $password);
/** @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/ WinZip 99-character limit */
static::assertCorrectZipArchive($this->outputFilename, substr($password, 0, 99));
// check from WinZip AES encryption
$zipFile->openFile($this->outputFilename);
@@ -99,11 +98,11 @@ class ZipPasswordTest extends ZipFileSetTestCase
foreach ($zipFile->getAllInfo() as $info) {
static::assertTrue($info->isEncrypted());
static::assertContains('Deflated', $info->getMethodName());
static::assertContains('WinZip AES-256', $info->getEncryptionMethodName());
static::assertStringContainsString('Deflated', $info->getMethodName());
static::assertStringContainsString('WinZip AES-256', $info->getEncryptionMethodName());
$decryptContent = $zipFile[$info->getName()];
static::assertNotEmpty($decryptContent);
static::assertContains('<?php', $decryptContent);
static::assertStringContainsString('<?php', $decryptContent);
}
// clear password
@@ -131,13 +130,15 @@ class ZipPasswordTest extends ZipFileSetTestCase
public function testTraditionalEncryption()
{
if (\PHP_INT_SIZE === 4) { // php 32 bit
$this->setExpectedException(
RuntimeException::class,
$this->expectException(
RuntimeException::class
);
$this->expectExceptionMessage(
'Traditional PKWARE Encryption is not supported in 32-bit PHP.'
);
}
$password = base64_encode(random_bytes(50));
$password = md5(random_bytes(50));
$zip = new ZipFile();
$zip->addDirRecursive($this->outputDirname);
@@ -154,7 +155,7 @@ class ZipPasswordTest extends ZipFileSetTestCase
foreach ($zip->getAllInfo() as $info) {
if (!$info->isFolder()) {
static::assertTrue($info->isEncrypted());
static::assertContains('Traditional PKWARE encryption', $info->getEncryptionMethodName());
static::assertStringContainsString('Traditional PKWARE encryption', $info->getEncryptionMethodName());
}
}
$zip->close();
@@ -189,7 +190,7 @@ class ZipPasswordTest extends ZipFileSetTestCase
if (!$info->isFolder()) {
static::assertTrue($info->isEncrypted());
static::assertSame($info->getEncryptionMethod(), $encryptionMethod);
static::assertContains('WinZip AES-' . $bitSize, $info->getEncryptionMethodName());
static::assertStringContainsString('WinZip AES-' . $bitSize, $info->getEncryptionMethodName());
}
}
$zip->close();
@@ -214,8 +215,10 @@ class ZipPasswordTest extends ZipFileSetTestCase
public function testEncryptionEntries()
{
if (\PHP_INT_SIZE === 4) { // php 32 bit
$this->setExpectedException(
RuntimeException::class,
$this->expectException(
RuntimeException::class
);
$this->expectExceptionMessage(
'Traditional PKWARE Encryption is not supported in 32-bit PHP.'
);
}
@@ -246,11 +249,11 @@ class ZipPasswordTest extends ZipFileSetTestCase
$info = $zip->getEntryInfo('.hidden');
static::assertTrue($info->isEncrypted());
static::assertContains('Traditional PKWARE encryption', $info->getEncryptionMethodName());
static::assertStringContainsString('Traditional PKWARE encryption', $info->getEncryptionMethodName());
$info = $zip->getEntryInfo('text file.txt');
static::assertTrue($info->isEncrypted());
static::assertContains('WinZip AES', $info->getEncryptionMethodName());
static::assertStringContainsString('WinZip AES', $info->getEncryptionMethodName());
static::assertFalse($zip->getEntryInfo('Текстовый документ.txt')->isEncrypted());
static::assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
@@ -265,8 +268,10 @@ class ZipPasswordTest extends ZipFileSetTestCase
public function testEncryptionEntriesWithDefaultPassword()
{
if (\PHP_INT_SIZE === 4) { // php 32 bit
$this->setExpectedException(
RuntimeException::class,
$this->expectException(
RuntimeException::class
);
$this->expectExceptionMessage(
'Traditional PKWARE Encryption is not supported in 32-bit PHP.'
);
}
@@ -300,15 +305,15 @@ class ZipPasswordTest extends ZipFileSetTestCase
$info = $zip->getEntryInfo('.hidden');
static::assertTrue($info->isEncrypted());
static::assertContains('Traditional PKWARE encryption', $info->getEncryptionMethodName());
static::assertStringContainsString('Traditional PKWARE encryption', $info->getEncryptionMethodName());
$info = $zip->getEntryInfo('text file.txt');
static::assertTrue($info->isEncrypted());
static::assertContains('WinZip AES', $info->getEncryptionMethodName());
static::assertStringContainsString('WinZip AES', $info->getEncryptionMethodName());
$info = $zip->getEntryInfo('Текстовый документ.txt');
static::assertTrue($info->isEncrypted());
static::assertContains('WinZip AES', $info->getEncryptionMethodName());
static::assertStringContainsString('WinZip AES', $info->getEncryptionMethodName());
static::assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
@@ -320,7 +325,8 @@ class ZipPasswordTest extends ZipFileSetTestCase
*/
public function testSetEncryptionMethodInvalid()
{
$this->setExpectedException(InvalidArgumentException::class, 'Encryption method 9999 is not supported.');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Encryption method 9999 is not supported.');
$zipFile = new ZipFile();
$encryptionMethod = 9999;
@@ -368,7 +374,8 @@ class ZipPasswordTest extends ZipFileSetTestCase
*/
public function testInvalidEncryptionMethodEntry()
{
$this->setExpectedException(InvalidArgumentException::class, 'Encryption method 99 is not supported.');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Encryption method 99 is not supported.');
$zipFile = new ZipFile();
$zipFile->addFromString('file', 'content', ZipCompressionMethod::STORED);
@@ -470,7 +477,7 @@ class ZipPasswordTest extends ZipFileSetTestCase
foreach ($zipFile as $name => $contents) {
static::assertNotEmpty($name);
static::assertNotEmpty($contents);
static::assertContains('test contents', $contents);
static::assertStringContainsString('test contents', $contents);
static::assertSame($zipFile2[$name], $contents);
}
$zipFile2->close();

View File

@@ -29,20 +29,20 @@ class ZipStreamOpenTest extends TestCase
*/
public function testOpenStream($resource, $exceptionClass = null, $exceptionMessage = null)
{
if ($resource === null) {
static::markTestSkipped('skip null resource');
return;
if ($resource === null || $resource === false) {
static::markTestSkipped('skip resource');
}
if ($exceptionClass !== null) {
$this->setExpectedException(
$exceptionClass,
$this->expectException(
$exceptionClass
);
$this->expectExceptionMessage(
$exceptionMessage
);
}
static::assertInternalType('resource', $resource);
static::assertIsResource($resource);
$zipFile = new ZipFile();
$zipFile->openFromStream($resource);

View File

@@ -2,14 +2,14 @@
namespace PhpZip\Tests;
use PHPUnit\Framework\TestCase;
use PhpZip\Constants\ZipConstants;
use PhpZip\Tests\Polyfill\LegacyTestCase;
use PhpZip\Util\FilesUtil;
/**
* PHPUnit test case and helper methods.
*/
abstract class ZipTestCase extends TestCase
abstract class ZipTestCase extends LegacyTestCase
{
/** @var string */
protected $outputFilename;
@@ -22,25 +22,23 @@ abstract class ZipTestCase extends TestCase
*
* @noinspection PhpMissingParentCallCommonInspection
*/
protected function setUp()
protected function setUp(): void
{
$id = uniqid('phpzip', true);
$tempDir = sys_get_temp_dir() . '/phpunit-phpzip';
$id = uniqid('phpzip', false);
$tempDir = sys_get_temp_dir() . \DIRECTORY_SEPARATOR . 'phpunit-phpzip';
if (!is_dir($tempDir) && !mkdir($tempDir, 0755, true) && !is_dir($tempDir)) {
throw new \RuntimeException('Dir ' . $tempDir . " can't created");
throw new \RuntimeException(sprintf('Directory "%s" was not created', $tempDir));
}
$this->outputFilename = $tempDir . '/' . $id . '.zip';
$this->outputDirname = $tempDir . '/' . $id;
$this->outputFilename = $tempDir . \DIRECTORY_SEPARATOR . $id . '.zip';
$this->outputDirname = $tempDir . \DIRECTORY_SEPARATOR . $id;
}
/**
* After test.
*/
protected function tearDown()
protected function tearDown(): void
{
parent::tearDown();
if ($this->outputFilename !== null && file_exists($this->outputFilename)) {
unlink($this->outputFilename);
}
@@ -58,64 +56,91 @@ abstract class ZipTestCase extends TestCase
*/
public static function assertCorrectZipArchive($filename, $password = null)
{
if (self::existsProgram('unzip')) {
$command = 'unzip';
if ($password !== null) {
$command .= ' -P ' . escapeshellarg($password);
}
$command .= ' -t ' . escapeshellarg($filename);
$command .= ' 2>&1';
exec($command, $output, $returnCode);
$output = implode(\PHP_EOL, $output);
if ($password !== null && $returnCode === 81) {
if (self::existsProgram('7z')) {
/**
* WinZip 99-character limit.
*
* @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
*/
$password = substr($password, 0, 99);
$command = '7z t -p' . escapeshellarg($password) . ' ' . escapeshellarg($filename);
exec($command, $output, $returnCode);
/**
* @var array $output
*/
$output = implode(\PHP_EOL, $output);
static::assertSame($returnCode, 0);
static::assertNotContains(' Errors', $output);
static::assertContains(' Ok', $output);
} else {
fwrite(\STDERR, 'Program unzip cannot support this function.' . \PHP_EOL);
fwrite(\STDERR, 'Please install 7z. For Ubuntu-like: sudo apt-get install p7zip-full' . \PHP_EOL);
}
} else {
static::assertSame($returnCode, 0, $output);
static::assertNotContains('incorrect password', $output);
static::assertContains(' OK', $output);
static::assertContains('No errors', $output);
}
if (self::existsProgram('7z')) {
self::assertCorrectZipArchiveFrom7z($filename, $password);
} elseif (self::existsProgram('unzip')) {
self::assertCorrectZipArchiveFromUnzip($filename, $password);
} else {
fwrite(\STDERR, 'Skipped testing the zip archive for errors using third-party utilities.' . \PHP_EOL);
fwrite(\STDERR, 'To fix this, install 7-zip or unzip.' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Ubuntu: sudo apt-get install p7zip-full unzip' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Windows:' . \PHP_EOL);
fwrite(\STDERR, ' * 7-zip - https://www.7-zip.org/download.html' . \PHP_EOL);
fwrite(\STDERR, ' * unzip - http://gnuwin32.sourceforge.net/packages/unzip.htm' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
}
}
/**
* @param string $filename
* @param string|null $password
*/
private static function assertCorrectZipArchiveFrom7z($filename, $password = null)
{
$command = '7z t';
if ($password !== null) {
$command .= ' -p' . escapeshellarg($password);
}
$command .= ' ' . escapeshellarg($filename) . ' 2>&1';
exec($command, $output, $returnCode);
$output = implode(\PHP_EOL, $output);
static::assertSame($returnCode, 0);
static::assertStringNotContainsString(' Errors', $output);
static::assertStringContainsString(' Ok', $output);
}
/**
* @param string $filename
* @param string|null $password
*/
private static function assertCorrectZipArchiveFromUnzip($filename, $password = null)
{
$command = 'unzip';
if ($password !== null) {
$command .= ' -P ' . escapeshellarg($password);
}
$command .= ' -t ' . escapeshellarg($filename) . ' 2>&1';
exec($command, $output, $returnCode);
$output = implode(\PHP_EOL, $output);
if ($password !== null && $returnCode === 81) {
fwrite(\STDERR, 'Program unzip cannot support this function.' . \PHP_EOL);
fwrite(\STDERR, 'You have to install 7-zip to complete this test.' . \PHP_EOL);
fwrite(\STDERR, 'Install 7-Zip on Ubuntu: sudo apt-get install p7zip-full' . \PHP_EOL);
fwrite(\STDERR, 'Install 7-Zip on Windows: https://www.7-zip.org/download.html' . \PHP_EOL);
return;
}
static::assertSame($returnCode, 0, $output);
static::assertNotContains('incorrect password', $output);
static::assertStringContainsString(' OK', $output);
static::assertStringContainsString('No errors', $output);
}
/**
* @param string $program
* @param array $successCodes
*
* @return bool
*/
protected static function existsProgram($program)
protected static function existsProgram($program, array $successCodes = [0])
{
if (\DIRECTORY_SEPARATOR !== '\\') {
exec('which ' . escapeshellarg($program), $output, $returnCode);
$command = \DIRECTORY_SEPARATOR === '\\'
? escapeshellarg($program)
: 'which ' . escapeshellarg($program);
$command .= ' 2>&1';
return $returnCode === 0;
}
// false for Windows
return false;
exec($command, $output, $returnCode);
return \in_array($returnCode, $successCodes, true);
}
/**
@@ -130,7 +155,7 @@ abstract class ZipTestCase extends TestCase
$output = implode(\PHP_EOL, $output);
static::assertContains('Empty zipfile', $output);
static::assertStringContainsString('Empty zipfile', $output);
}
$actualEmptyZipData = pack('VVVVVv', ZipConstants::END_CD, 0, 0, 0, 0, 0);
static::assertStringEqualsFile($filename, $actualEmptyZipData);
@@ -144,7 +169,7 @@ abstract class ZipTestCase extends TestCase
*/
public static function assertVerifyZipAlign($filename, $showErrors = false)
{
if (self::existsProgram('zipalign')) {
if (self::existsProgram('zipalign', [0, 2])) {
exec('zipalign -c -v 4 ' . escapeshellarg($filename), $output, $returnCode);
if ($showErrors && $returnCode !== 0) {
@@ -155,22 +180,29 @@ abstract class ZipTestCase extends TestCase
}
fwrite(\STDERR, "Cannot find the program 'zipalign' for the test" . \PHP_EOL);
fwrite(\STDERR, 'To fix this, install zipalign.' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Ubuntu: sudo apt-get install zipalign' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Windows:' . \PHP_EOL);
fwrite(\STDERR, ' 1. Install Android Studio' . \PHP_EOL);
fwrite(\STDERR, ' 2. Install Android Sdk' . \PHP_EOL);
fwrite(\STDERR, ' 3. Add zipalign path to $Path' . \PHP_EOL);
return null;
}
/**
* @return bool
*/
public static function skipTestForRootUser()
{
/** @noinspection PhpComposerExtensionStubsInspection */
if (\extension_loaded('posix') && posix_getuid() === 0) {
static::markTestSkipped('Skip the test for a user with root privileges');
return true;
}
}
return false;
public static function skipTestForWindows()
{
if (\DIRECTORY_SEPARATOR === '\\') {
static::markTestSkipped('Skip on Windows');
}
}
}