Bläddra i källkod

php 7.1 - 8.0 compatible version

issue #78
Ne-Lexa 5 år sedan
förälder
incheckning
584784c119
73 ändrade filer med 1445 tillägg och 958 borttagningar
  1. 1 2
      .gitattributes
  2. 130 0
      .github/workflows/build.yml
  3. 239 125
      .php-cs-fixer.php
  4. 13 7
      README.RU.md
  5. 13 7
      README.md
  6. 0 6
      bootstrap.php
  7. 13 9
      composer.json
  8. 0 0
      logo.svg
  9. 2 3
      phpunit.xml
  10. 2 3
      src/Constants/ZipCompressionMethod.php
  11. 2 3
      src/Constants/ZipEncryptionMethod.php
  12. 1 1
      src/Constants/ZipPlatform.php
  13. 1 1
      src/IO/Filter/Cipher/Pkware/PKCryptContext.php
  14. 3 3
      src/IO/Filter/Cipher/Pkware/PKEncryptionStreamFilter.php
  15. 5 5
      src/IO/Filter/Cipher/WinZipAes/WinZipAesDecryptionStreamFilter.php
  16. 3 3
      src/IO/Filter/Cipher/WinZipAes/WinZipAesEncryptionStreamFilter.php
  17. 28 30
      src/IO/Stream/ResponseStream.php
  18. 29 29
      src/IO/ZipReader.php
  19. 31 29
      src/IO/ZipWriter.php
  20. 38 38
      src/Model/Data/ZipNewData.php
  21. 18 18
      src/Model/Data/ZipSourceFileData.php
  22. 26 28
      src/Model/Extra/ExtraFieldsCollection.php
  23. 2 2
      src/Model/Extra/Fields/AbstractUnicodeExtraField.php
  24. 13 13
      src/Model/Extra/Fields/ApkAlignmentExtraField.php
  25. 15 15
      src/Model/Extra/Fields/AsiExtraField.php
  26. 27 27
      src/Model/Extra/Fields/ExtendedTimestampExtraField.php
  27. 8 8
      src/Model/Extra/Fields/JarMarkerExtraField.php
  28. 13 13
      src/Model/Extra/Fields/NewUnixExtraField.php
  29. 26 26
      src/Model/Extra/Fields/NtfsExtraField.php
  30. 37 37
      src/Model/Extra/Fields/OldUnixExtraField.php
  31. 12 12
      src/Model/Extra/Fields/UnicodeCommentExtraField.php
  32. 12 12
      src/Model/Extra/Fields/UnicodePathExtraField.php
  33. 13 13
      src/Model/Extra/Fields/UnrecognizedExtraField.php
  34. 19 19
      src/Model/Extra/Fields/WinZipAesExtraField.php
  35. 33 33
      src/Model/Extra/Fields/Zip64ExtraField.php
  36. 0 2
      src/Model/Extra/ZipExtraDriver.php
  37. 5 5
      src/Model/Extra/ZipExtraField.php
  38. 16 16
      src/Model/ImmutableZipContainer.php
  39. 3 3
      src/Model/ZipContainer.php
  40. 43 44
      src/Model/ZipEntry.php
  41. 27 27
      src/Model/ZipInfo.php
  42. 4 12
      src/Util/CryptoUtil.php
  43. 6 6
      src/Util/DateTimeConverter.php
  44. 8 0
      src/Util/FileAttribUtil.php
  45. 9 4
      src/Util/FilesUtil.php
  46. 24 24
      src/ZipFile.php
  47. 1 1
      src/ZipFileInterface.php
  48. 8 4
      tests/Extra/Fields/AbstractUnicodeExtraFieldTest.php
  49. 4 2
      tests/Extra/Fields/ApkAlignmentExtraFieldTest.php
  50. 4 2
      tests/Extra/Fields/AsiExtraFieldTest.php
  51. 12 12
      tests/Extra/Fields/ExtendedTimestampExtraFieldTest.php
  52. 8 4
      tests/Extra/Fields/JarMarkerExtraFieldTest.php
  53. 6 4
      tests/Extra/Fields/NtfsExtraFieldTest.php
  54. 1 1
      tests/Extra/Fields/UnicodeCommentExtraFieldTest.php
  55. 1 1
      tests/Extra/Fields/UnicodePathExtraFieldTest.php
  56. 8 4
      tests/Extra/Fields/UnrecognizedExtraFieldTest.php
  57. 12 6
      tests/Extra/Fields/WinZipAesExtraFieldTest.php
  58. 3 1
      tests/Extra/Fields/Zip64ExtraFieldTest.php
  59. 1 1
      tests/Issue24Test.php
  60. 14 8
      tests/PhpZipExtResourceTest.php
  61. 18 0
      tests/Polyfill/LegacyTestCase.php
  62. 22 0
      tests/Polyfill/PhpUnit8CompatTrait.php
  63. 53 0
      tests/Polyfill/PhpUnit9CompatTrait.php
  64. 1 1
      tests/SymlinkTest.php
  65. 8 8
      tests/ZipAlignTest.php
  66. 41 23
      tests/ZipEntryTest.php
  67. 1 1
      tests/ZipFileSetTestCase.php
  68. 191 103
      tests/ZipFileTest.php
  69. 4 2
      tests/ZipInfoTest.php
  70. 1 1
      tests/ZipMatcherTest.php
  71. 31 25
      tests/ZipPasswordTest.php
  72. 5 5
      tests/ZipStreamOpenTest.php
  73. 13 15
      tests/ZipTestCase.php

+ 1 - 2
.gitattributes

@@ -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 - 0
.github/workflows/build.yml

@@ -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-v1
+            PHPUNIT_COVERAGE: 0
+            PHP_INI:          date.timezone='UTC', memory_limit=-1, opcache.enable=1, opcache.enable_cli=1
+
+        strategy:
+            matrix:
+                os:
+                    - ubuntu-latest
+                    - windows-latest
+                    - macos-latest
+                php:
+                    - '7.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

+ 239 - 125
.php_cs → .php-cs-fixer.php

@@ -1,16 +1,18 @@
 <?php
 
+declare(strict_types=1);
+
 /**
- * PHP Code Style Fixer (config created for version 2.16.4 (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
@@ -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
@@ -270,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
@@ -332,9 +339,6 @@ $rules = [
      */
     '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.
      *
@@ -377,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,
@@ -393,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,
@@ -400,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,
 
@@ -444,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,
 
@@ -589,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,
 
@@ -630,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
@@ -701,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,
 
@@ -743,14 +736,36 @@ $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 and `private`
@@ -779,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,
@@ -798,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.
@@ -837,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' => [
@@ -866,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
@@ -885,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
@@ -897,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
@@ -907,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,
@@ -930,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(..))`
@@ -941,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.
@@ -962,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
@@ -978,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
@@ -1047,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,
@@ -1069,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
@@ -1110,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.
@@ -1139,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,
 
@@ -1155,6 +1213,19 @@ $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.
@@ -1164,8 +1235,7 @@ $rules = [
      * 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,
 
@@ -1208,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
@@ -1225,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
@@ -1245,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.
@@ -1263,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
@@ -1309,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`.
@@ -1356,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,
 
@@ -1414,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,
@@ -1435,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
@@ -1459,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,
@@ -1477,7 +1590,7 @@ if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) {
     }
     $dryRun = !in_array('--force', $_SERVER['argv'], true);
 
-    $command = escapeshellarg($binFixer) . ' fix --config ' . escapeshellarg(__FILE__) . ' --diff-format udiff --ansi -vv';
+    $command = escapeshellarg($binFixer) . ' fix --config ' . escapeshellarg(__FILE__) . ' --diff --ansi -vv';
 
     if ($dryRun) {
         $command .= ' --dry-run';
@@ -1498,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__)
     )
 ;

+ 13 - 7
README.RU.md

@@ -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)

+ 13 - 7
README.md

@@ -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)

+ 0 - 6
bootstrap.php

@@ -1,6 +0,0 @@
-<?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');
-}

+ 13 - 9
composer.json

@@ -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": "*",
-        "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,18 @@
         }
     },
     "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"
+    },
+    "config": {
+        "platform": {
+            "php": "7.1.3"
+        }
     }
 }

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
logo.svg


+ 2 - 3
phpunit.xml

@@ -1,10 +1,9 @@
 <?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"
+         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
          colors="true"
-         bootstrap="bootstrap.php">
+         bootstrap="vendor/autoload.php">
     <php>
         <ini name="error_reporting" value="-1"/>
     </php>

+ 2 - 3
src/Constants/ZipCompressionMethod.php

@@ -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';
     }
 
     /**

+ 2 - 3
src/Constants/ZipEncryptionMethod.php

@@ -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';
     }
 
     /**

+ 1 - 1
src/Constants/ZipPlatform.php

@@ -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';
     }
 }

+ 1 - 1
src/IO/Filter/Cipher/Pkware/PKCryptContext.php

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

+ 3 - 3
src/IO/Filter/Cipher/Pkware/PKEncryptionStreamFilter.php

@@ -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);

+ 5 - 5
src/IO/Filter/Cipher/WinZipAes/WinZipAesDecryptionStreamFilter.php

@@ -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 = '';

+ 3 - 3
src/IO/Filter/Cipher/WinZipAes/WinZipAesEncryptionStreamFilter.php

@@ -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;
         }

+ 28 - 30
src/IO/Stream/ResponseStream.php

@@ -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.
      */

+ 29 - 29
src/IO/ZipReader.php

@@ -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!'
@@ -375,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;
                     }
@@ -427,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)
         );
 
@@ -523,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;
@@ -548,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;
@@ -890,9 +895,4 @@ class ZipReader
             fclose($this->inStream);
         }
     }
-
-    public function __destruct()
-    {
-        $this->close();
-    }
 }

+ 31 - 29
src/IO/ZipWriter.php

@@ -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)
             );
         }
 

+ 38 - 38
src/Model/Data/ZipNewData.php

@@ -51,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;
     }
 
     /**
@@ -95,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);
-        }
-    }
 }

+ 18 - 18
src/Model/Data/ZipSourceFileData.php

@@ -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);
-        }
-    }
 }

+ 26 - 28
src/Model/Extra/ExtraFieldsCollection.php

@@ -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;
-        }
-    }
 }

+ 2 - 2
src/Model/Extra/Fields/AbstractUnicodeExtraField.php

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

+ 13 - 13
src/Model/Extra/Fields/ApkAlignmentExtraField.php

@@ -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
-        );
-    }
 }

+ 15 - 15
src/Model/Extra/Fields/AsiExtraField.php

@@ -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
-        );
-    }
 }

+ 27 - 27
src/Model/Extra/Fields/ExtendedTimestampExtraField.php

@@ -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);
-    }
 }

+ 8 - 8
src/Model/Extra/Fields/JarMarkerExtraField.php

@@ -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);
-    }
 }

+ 13 - 13
src/Model/Extra/Fields/NewUnixExtraField.php

@@ -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
-        );
-    }
 }

+ 26 - 26
src/Model/Extra/Fields/NtfsExtraField.php

@@ -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);
-    }
 }

+ 37 - 37
src/Model/Extra/Fields/OldUnixExtraField.php

@@ -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);
-    }
 }

+ 12 - 12
src/Model/Extra/Fields/UnicodeCommentExtraField.php

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

+ 12 - 12
src/Model/Extra/Fields/UnicodePathExtraField.php

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

+ 13 - 13
src/Model/Extra/Fields/UnrecognizedExtraField.php

@@ -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);
-    }
 }

+ 19 - 19
src/Model/Extra/Fields/WinZipAesExtraField.php

@@ -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
-        );
-    }
 }

+ 33 - 33
src/Model/Extra/Fields/Zip64ExtraField.php

@@ -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);
-    }
 }

+ 0 - 2
src/Model/Extra/ZipExtraDriver.php

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

+ 5 - 5
src/Model/Extra/ZipExtraField.php

@@ -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();
 }

+ 16 - 16
src/Model/ImmutableZipContainer.php

@@ -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;
-        }
-    }
 }

+ 3 - 3
src/Model/ZipContainer.php

@@ -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];
 

+ 43 - 44
src/Model/ZipEntry.php

@@ -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;
-        }
     }
 }

+ 27 - 27
src/Model/ZipInfo.php

@@ -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()
-            . '}';
-    }
 }

+ 4 - 12
src/Util/CryptoUtil.php

@@ -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);
+            return openssl_decrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $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);
+            return openssl_encrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
         }
 
-        throw new RuntimeException('Extension openssl or mcrypt not loaded');
+        throw new RuntimeException('Openssl extension not loaded');
     }
 }

+ 6 - 6
src/Util/DateTimeConverter.php

@@ -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) {

+ 8 - 0
src/Util/FileAttribUtil.php

@@ -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' : '-';

+ 9 - 4
src/Util/FilesUtil.php

@@ -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;
@@ -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;
+         ;
     }
 
     /**

+ 24 - 24
src/ZipFile.php

@@ -42,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',
@@ -55,9 +58,6 @@ class ZipFile implements ZipFileInterface
         'xpi' => 'application/x-xpinstall',
     ];
 
-    /** @var ZipContainer */
-    protected $zipContainer;
-
     /** @var ZipReader|null */
     private $reader;
 
@@ -69,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
@@ -376,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
@@ -576,9 +584,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;
             }
         }
 
@@ -714,9 +722,9 @@ 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->setCompressionMethod($compressionMethod);
@@ -838,9 +846,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);
             }
@@ -999,9 +1007,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
@@ -1843,14 +1851,6 @@ class ZipFile implements ZipFileInterface
         return $this->saveAsFile($meta['uri']);
     }
 
-    /**
-     * Release all resources.
-     */
-    public function __destruct()
-    {
-        $this->close();
-    }
-
     /**
      * Offset to set.
      *

+ 1 - 1
src/ZipFileInterface.php

@@ -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

+ 8 - 4
tests/Extra/Fields/AbstractUnicodeExtraFieldTest.php

@@ -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.'
         );
 

+ 4 - 2
tests/Extra/Fields/ApkAlignmentExtraFieldTest.php

@@ -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.'
         );
 

+ 4 - 2
tests/Extra/Fields/AsiExtraFieldTest.php

@@ -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'
         );
 

+ 12 - 12
tests/Extra/Fields/ExtendedTimestampExtraFieldTest.php

@@ -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);

+ 8 - 4
tests/Extra/Fields/JarMarkerExtraFieldTest.php

@@ -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"
         );
 

+ 6 - 4
tests/Extra/Fields/NtfsExtraFieldTest.php

@@ -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);

+ 1 - 1
tests/Extra/Fields/UnicodeCommentExtraFieldTest.php

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

+ 1 - 1
tests/Extra/Fields/UnicodePathExtraFieldTest.php

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

+ 8 - 4
tests/Extra/Fields/UnrecognizedExtraFieldTest.php

@@ -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'
         );
 

+ 12 - 6
tests/Extra/Fields/WinZipAesExtraFieldTest.php

@@ -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,

+ 3 - 1
tests/Extra/Fields/Zip64ExtraFieldTest.php

@@ -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');
         }

+ 1 - 1
tests/Issue24Test.php

@@ -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);
     }

+ 14 - 8
tests/PhpZipExtResourceTest.php

@@ -87,7 +87,7 @@ class PhpZipExtResourceTest extends ZipTestCase
         $zipFile->extractTo($this->outputDirname);
         $zipFile->close();
 
-        static::assertTrue(is_dir($this->outputDirname . '/test/empty'));
+        static::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,13 +129,17 @@ class PhpZipExtResourceTest extends ZipTestCase
     public function testBug70752()
     {
         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.'
             );
         } else { // php 64 bit
-            $this->setExpectedException(
-                ZipAuthenticationException::class,
+            $this->expectException(
+                ZipAuthenticationException::class
+            );
+            $this->expectExceptionMessage(
                 'Invalid password'
             );
         }
@@ -151,7 +156,7 @@ class PhpZipExtResourceTest extends ZipTestCase
             $zipFile->extractTo($this->outputDirname);
             static::markTestIncomplete('failed test');
         } catch (ZipException $exception) {
-            static::assertFileNotExists($this->outputDirname . '/bug70752.txt');
+            static::assertFileDoesNotExist($this->outputDirname . '/bug70752.txt');
 
             throw $exception;
         } finally {
@@ -168,7 +173,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';
 

+ 18 - 0
tests/Polyfill/LegacyTestCase.php

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

+ 22 - 0
tests/Polyfill/PhpUnit8CompatTrait.php

@@ -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);
+    }
+}

+ 53 - 0
tests/Polyfill/PhpUnit9CompatTrait.php

@@ -0,0 +1,53 @@
+<?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', '<')) {
+            static::assertFileNotExists($filename, $message);
+
+            return;
+        }
+
+        parent::assertFileDoesNotExist($filename, $message);
+    }
+
+    /**
+     * Asserts that a directory does not exist.
+     *
+     * @throws ExpectationFailedException
+     * @throws InvalidArgumentException
+     *
+     * @noinspection PhpDeprecationInspection
+     */
+    public static function assertDirectoryDoesNotExist(string $directory, string $message = ''): void
+    {
+        if (version_compare(Version::id(), '9.1.0', '<')) {
+            static::assertDirectoryNotExists($directory, $message);
+
+            return;
+        }
+
+        parent::assertDirectoryDoesNotExist($directory, $message);
+    }
+}

+ 1 - 1
tests/SymlinkTest.php

@@ -47,7 +47,7 @@ final class SymlinkTest extends ZipTestCase
         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);

+ 8 - 8
tests/ZipAlignTest.php

@@ -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);

+ 41 - 23
tests/ZipEntryTest.php

@@ -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();
 

+ 1 - 1
tests/ZipFileSetTestCase.php

@@ -30,7 +30,7 @@ abstract class ZipFileSetTestCase extends ZipTestCase
     /**
      * Before test.
      */
-    protected function setUp()
+    protected function setUp(): void
     {
         parent::setUp();
         $this->fillDirectory();

+ 191 - 103
tests/ZipFileTest.php

@@ -31,7 +31,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFileCantExists()
     {
-        $this->setExpectedException(ZipException::class, 'does not exist');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('does not exist');
 
         $zipFile = new ZipFile();
         $zipFile->openFile(uniqid('', false));
@@ -50,7 +51,8 @@ class ZipFileTest extends ZipTestCase
             return;
         }
 
-        $this->setExpectedException(ZipException::class, 'can\'t open');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('can\'t open');
 
         static::assertNotFalse(file_put_contents($this->outputFilename, 'content'));
         static::assertTrue(chmod($this->outputFilename, 0222));
@@ -64,7 +66,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFileEmptyFile()
     {
-        $this->setExpectedException(ZipException::class, 'Corrupt zip file');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('Corrupt zip file');
 
         static::assertNotFalse(touch($this->outputFilename));
         $zipFile = new ZipFile();
@@ -77,8 +80,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFileInvalidZip()
     {
-        $this->setExpectedException(
-            ZipException::class,
+        $this->expectException(
+            ZipException::class
+        );
+        $this->expectExceptionMessage(
             'Invalid zip file. The end of the central directory could not be found.'
         );
 
@@ -92,7 +97,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStringNullString()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Empty string passed');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Empty string passed');
 
         $zipFile = new ZipFile();
         $zipFile->openFromString(null);
@@ -103,7 +109,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStringEmptyString()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Empty string passed');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Empty string passed');
 
         $zipFile = new ZipFile();
         $zipFile->openFromString('');
@@ -115,8 +122,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStringInvalidZip()
     {
-        $this->setExpectedException(
-            ZipException::class,
+        $this->expectException(
+            ZipException::class
+        );
+        $this->expectExceptionMessage(
             'Invalid zip file. The end of the central directory could not be found.'
         );
 
@@ -149,7 +158,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStreamNullStream()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Stream must be a resource');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Stream must be a resource');
 
         $zipFile = new ZipFile();
         $zipFile->openFromStream(null);
@@ -160,7 +170,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStreamInvalidResourceType()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Stream must be a resource');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Stream must be a resource');
 
         $zipFile = new ZipFile();
         /** @noinspection PhpParamsInspection */
@@ -169,19 +180,22 @@ class ZipFileTest extends ZipTestCase
 
     /**
      * @throws ZipException
+     *
+     * @noinspection PhpComposerExtensionStubsInspection
      */
     public function testOpenFromStreamInvalidResourceType2()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Invalid resource type');
+        $this->expectException(InvalidArgumentException::class);
+        $exceptionMessage = PHP_VERSION_ID < 80000 ?
+            'Invalid resource type' :
+            'Stream must be a resource';
+        $this->expectExceptionMessage($exceptionMessage);
 
         $zipFile = new ZipFile();
 
         if (!\extension_loaded('gd')) {
             static::markTestSkipped('not extension gd');
-
-            return;
         }
-        /** @noinspection PhpComposerExtensionStubsInspection */
         $zipFile->openFromStream(imagecreate(1, 1));
     }
 
@@ -190,7 +204,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStreamInvalidResourceType3()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Directory stream not supported');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Directory stream not supported');
 
         $zipFile = new ZipFile();
         $zipFile->openFromStream(opendir(__DIR__));
@@ -203,7 +218,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStreamNoSeekable()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The stream wrapper type "http" is not supported');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The stream wrapper type "http" is not supported');
 
         if (!$fp = @fopen('http://localhost', 'rb')) {
             if (!$fp = @fopen('http://example.org', 'rb')) {
@@ -222,7 +238,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStreamEmptyContents()
     {
-        $this->setExpectedException(ZipException::class, 'Corrupt zip file');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('Corrupt zip file');
 
         $fp = fopen($this->outputFilename, 'w+b');
         $zipFile = new ZipFile();
@@ -235,8 +252,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testOpenFromStreamInvalidZip()
     {
-        $this->setExpectedException(
-            ZipException::class,
+        $this->expectException(
+            ZipException::class
+        );
+        $this->expectExceptionMessage(
             'Invalid zip file. The end of the central directory could not be found.'
         );
 
@@ -508,7 +527,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testRenameEntryNull()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'name is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('name is null');
 
         $zipFile = new ZipFile();
         $zipFile->rename(null, 'new-file');
@@ -519,7 +539,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testRenameEntryNull2()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'name is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('name is null');
 
         $zipFile = new ZipFile();
         $zipFile->rename('old-file', null);
@@ -530,7 +551,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testRenameEntryToExistsNewEntry()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'is exists');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('is exists');
 
         $zipFile = new ZipFile();
         $zipFile['file'] = 'content';
@@ -548,7 +570,7 @@ class ZipFileTest extends ZipTestCase
      */
     public function testRenameEntryNotFound()
     {
-        $this->setExpectedException(ZipEntryNotFoundException::class);
+        $this->expectException(ZipEntryNotFoundException::class);
 
         $zipFile = new ZipFile();
         $zipFile['file'] = 'content';
@@ -615,7 +637,7 @@ class ZipFileTest extends ZipTestCase
      */
     public function testDeleteFromNameNotFoundEntry()
     {
-        $this->setExpectedException(ZipEntryNotFoundException::class);
+        $this->expectException(ZipEntryNotFoundException::class);
 
         $zipFile = new ZipFile();
         $zipFile->deleteFromName('entry');
@@ -674,7 +696,8 @@ class ZipFileTest extends ZipTestCase
 
     public function testDeleteFromGlobFailNull()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The glob pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->deleteFromGlob(null);
@@ -682,7 +705,8 @@ class ZipFileTest extends ZipTestCase
 
     public function testDeleteFromGlobFailEmpty()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The glob pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->deleteFromGlob('');
@@ -722,7 +746,8 @@ class ZipFileTest extends ZipTestCase
 
     public function testDeleteFromRegexFailNull()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The regex pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->deleteFromRegex(null);
@@ -730,7 +755,8 @@ class ZipFileTest extends ZipTestCase
 
     public function testDeleteFromRegexFailEmpty()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The regex pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->deleteFromRegex('');
@@ -806,10 +832,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testVeryLongArchiveComment()
     {
-        $this->setExpectedException(InvalidArgumentException::class);
+        $this->expectException(InvalidArgumentException::class);
 
-        $comment = 'Very long comment' . \PHP_EOL .
-            'Очень длинный комментарий' . \PHP_EOL;
+        $comment = 'Very long comment' . \PHP_EOL
+            . 'Очень длинный комментарий' . \PHP_EOL;
         $comment = str_repeat($comment, ceil(0xffff / \strlen($comment)) + \strlen($comment) + 1);
 
         $zipFile = new ZipFile();
@@ -873,7 +899,7 @@ class ZipFileTest extends ZipTestCase
             static::assertSame($zipFile->getEntryComment($entryName), (string) $entriesItem['comment']);
         }
         // modify comment
-        $entries['file5.txt']['comment'] = mt_rand(1, 100000000);
+        $entries['file5.txt']['comment'] = random_int(1, 100000000);
         $zipFile->setEntryComment('file5.txt', $entries['file5.txt']['comment']);
         $zipFile->saveAsFile($this->outputFilename);
         $zipFile->close();
@@ -898,10 +924,11 @@ class ZipFileTest extends ZipTestCase
      */
     public function testVeryLongEntryComment()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Comment too long');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Comment too long');
 
-        $comment = 'Very long comment' . \PHP_EOL .
-            'Очень длинный комментарий' . \PHP_EOL;
+        $comment = 'Very long comment' . \PHP_EOL
+            . 'Очень длинный комментарий' . \PHP_EOL;
         $comment = str_repeat($comment, ceil(0xffff / \strlen($comment)) + \strlen($comment) + 1);
 
         $zipFile = new ZipFile();
@@ -914,7 +941,7 @@ class ZipFileTest extends ZipTestCase
      */
     public function testSetEntryCommentNotFoundEntry()
     {
-        $this->setExpectedException(ZipEntryNotFoundException::class);
+        $this->expectException(ZipEntryNotFoundException::class);
 
         $zipFile = new ZipFile();
         $zipFile->setEntryComment('test', 'comment');
@@ -979,8 +1006,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testSetInvalidCompressionLevel($compressionLevel)
     {
-        $this->setExpectedException(
-            InvalidArgumentException::class,
+        $this->expectException(
+            InvalidArgumentException::class
+        );
+        $this->expectExceptionMessage(
             'Invalid compression level. Minimum level 1. Maximum level 9'
         );
 
@@ -1048,7 +1077,7 @@ class ZipFileTest extends ZipTestCase
             );
 
             if ($contents === null) {
-                static::assertTrue(is_dir($fullExtractedFilename));
+                static::assertDirectoryExists($fullExtractedFilename);
                 static::assertTrue(FilesUtil::isEmptyDir($fullExtractedFilename));
             } else {
                 static::assertTrue(is_file($fullExtractedFilename));
@@ -1106,7 +1135,7 @@ class ZipFileTest extends ZipTestCase
 
             if (\in_array($entryName, $extractEntries, true)) {
                 if ($value === null) {
-                    static::assertTrue(is_dir($fullExtractFilename));
+                    static::assertDirectoryExists($fullExtractFilename);
                     static::assertTrue(FilesUtil::isEmptyDir($fullExtractFilename));
                 } else {
                     static::assertTrue(is_file($fullExtractFilename));
@@ -1114,7 +1143,7 @@ class ZipFileTest extends ZipTestCase
                     static::assertEquals($contents, $value);
                 }
             } elseif ($value === null) {
-                static::assertFalse(is_dir($fullExtractFilename));
+                static::assertDirectoryDoesNotExist($fullExtractFilename);
             } else {
                 static::assertFalse(is_file($fullExtractFilename));
             }
@@ -1131,7 +1160,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testExtractFail()
     {
-        $this->setExpectedException(ZipException::class, 'not found');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('not found');
 
         $zipFile = new ZipFile();
         $zipFile['file'] = 'content';
@@ -1147,7 +1177,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testExtractFail2()
     {
-        $this->setExpectedException(ZipException::class, 'Destination is not directory');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('Destination is not directory');
 
         $zipFile = new ZipFile();
         $zipFile['file'] = 'content';
@@ -1163,7 +1194,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testExtractFail3()
     {
-        $this->setExpectedException(ZipException::class, 'Destination is not writable directory');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('Destination is not writable directory');
 
         if (static::skipTestForRootUser()) {
             return;
@@ -1186,8 +1218,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromArrayAccessNullName()
     {
-        $this->setExpectedException(
-            InvalidArgumentException::class,
+        $this->expectException(
+            InvalidArgumentException::class
+        );
+        $this->expectExceptionMessage(
             'Key must not be null, but must contain the name of the zip entry.'
         );
 
@@ -1200,8 +1234,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromArrayAccessEmptyName()
     {
-        $this->setExpectedException(
-            InvalidArgumentException::class,
+        $this->expectException(
+            InvalidArgumentException::class
+        );
+        $this->expectExceptionMessage(
             'Key is empty, but must contain the name of the zip entry.'
         );
 
@@ -1214,7 +1250,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromStringNullContents()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Contents is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Contents is null');
 
         $zipFile = new ZipFile();
         $zipFile->addFromString('file', null);
@@ -1225,7 +1262,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromStringNullEntryName()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Entry name is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Entry name is null');
 
         $zipFile = new ZipFile();
         $zipFile->addFromString(null, 'contents');
@@ -1236,8 +1274,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromStringUnsupportedMethod()
     {
-        $this->setExpectedException(
-            ZipUnsupportMethodException::class,
+        $this->expectException(
+            ZipUnsupportMethodException::class
+        );
+        $this->expectExceptionMessage(
             'Compression method 99 (AES Encryption) is not supported.'
         );
 
@@ -1251,7 +1291,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromStringEmptyEntryName()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Empty entry name');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Empty entry name');
 
         $zipFile = new ZipFile();
         $zipFile->addFromString('', 'contents');
@@ -1292,7 +1333,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromStreamInvalidResource()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Stream is not resource');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Stream is not resource');
 
         $zipFile = new ZipFile();
         /** @noinspection PhpParamsInspection */
@@ -1304,7 +1346,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromStreamEmptyEntryName()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Empty entry name');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Empty entry name');
 
         $handle = fopen(__FILE__, 'rb');
 
@@ -1317,8 +1360,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFromStreamUnsupportedMethod()
     {
-        $this->setExpectedException(
-            ZipUnsupportMethodException::class,
+        $this->expectException(
+            ZipUnsupportMethodException::class
+        );
+        $this->expectExceptionMessage(
             'Compression method 99 (AES Encryption) is not supported.'
         );
 
@@ -1367,7 +1412,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFileNullFileName()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Filename is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Filename is null');
 
         $zipFile = new ZipFile();
         $zipFile->addFile(null);
@@ -1378,7 +1424,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFileCantExists()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'File path/to/file is not readable');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('File path/to/file is not readable');
 
         $zipFile = new ZipFile();
         $zipFile->addFile('path/to/file');
@@ -1389,8 +1436,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFileUnsupportedMethod()
     {
-        $this->setExpectedException(
-            ZipUnsupportMethodException::class,
+        $this->expectException(
+            ZipUnsupportMethodException::class
+        );
+        $this->expectExceptionMessage(
             'Compression method 99 (AES Encryption) is not supported.'
         );
 
@@ -1412,7 +1461,8 @@ class ZipFileTest extends ZipTestCase
             return;
         }
 
-        $this->setExpectedException(InvalidArgumentException::class, 'is not readable');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('is not readable');
 
         static::assertNotFalse(file_put_contents($this->outputFilename, ''));
         static::assertTrue(chmod($this->outputFilename, 0244));
@@ -1426,7 +1476,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddDirNullDirname()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Input dir is null');
 
         $zipFile = new ZipFile();
         $zipFile->addDir(null);
@@ -1437,7 +1488,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddDirEmptyDirname()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addDir('');
@@ -1448,7 +1500,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddDirCantExists()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('does not exist');
 
         $zipFile = new ZipFile();
         $zipFile->addDir(uniqid('', false));
@@ -1459,7 +1512,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddDirRecursiveNullDirname()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Input dir is null');
 
         $zipFile = new ZipFile();
         $zipFile->addDirRecursive(null);
@@ -1470,7 +1524,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddDirRecursiveEmptyDirname()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addDirRecursive('');
@@ -1481,7 +1536,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddDirRecursiveCantExists()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('does not exist');
 
         $zipFile = new ZipFile();
         $zipFile->addDirRecursive(uniqid('', false));
@@ -1492,7 +1548,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobNull()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Input dir is null');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlob(null, '*.png');
@@ -1503,7 +1560,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobEmpty()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlob('', '*.png');
@@ -1514,7 +1572,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobCantExists()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('does not exist');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlob('path/to/path', '*.png');
@@ -1525,7 +1584,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobNullPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The glob pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlob(__DIR__, null);
@@ -1536,7 +1596,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobEmptyPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The glob pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlob(__DIR__, '');
@@ -1547,7 +1608,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobRecursiveNull()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Input dir is null');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlobRecursive(null, '*.png');
@@ -1558,7 +1620,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobRecursiveEmpty()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlobRecursive('', '*.png');
@@ -1569,7 +1632,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobRecursiveCantExists()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('does not exist');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlobRecursive('path/to/path', '*.png');
@@ -1580,7 +1644,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobRecursiveNullPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The glob pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlobRecursive(__DIR__, null);
@@ -1591,7 +1656,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromGlobRecursiveEmptyPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The glob pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlobRecursive(__DIR__, '');
@@ -1602,7 +1668,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexDirectoryNull()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegex(null, '~\.png$~i');
@@ -1613,7 +1680,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexDirectoryEmpty()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegex('', '~\.png$~i');
@@ -1624,7 +1692,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexCantExists()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('does not exist');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegex('path/to/path', '~\.png$~i');
@@ -1635,7 +1704,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexNullPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The regex pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegex(__DIR__, null);
@@ -1646,7 +1716,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexEmptyPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The regex pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegex(__DIR__, '');
@@ -1657,7 +1728,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexRecursiveDirectoryNull()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegexRecursive(null, '~\.png$~i');
@@ -1668,7 +1740,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexRecursiveEmpty()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The input directory is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegexRecursive('', '~\.png$~i');
@@ -1679,7 +1752,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexRecursiveCantExists()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('does not exist');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromGlobRecursive('path/to/path', '~\.png$~i');
@@ -1690,7 +1764,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexRecursiveNullPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The regex pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegexRecursive(__DIR__, null);
@@ -1701,7 +1776,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddFilesFromRegexRecursiveEmptyPattern()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('The regex pattern is not specified');
 
         $zipFile = new ZipFile();
         $zipFile->addFilesFromRegexRecursive(__DIR__, '');
@@ -1712,7 +1788,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testSaveAsStreamBadStream()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'handle is not resource');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('handle is not resource');
 
         $zipFile = new ZipFile();
         /** @noinspection PhpParamsInspection */
@@ -1737,7 +1814,8 @@ class ZipFileTest extends ZipTestCase
 
         $this->outputFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . basename($this->outputFilename);
 
-        $this->setExpectedExceptionRegExp(InvalidArgumentException::class, '~Cannot open ".*?" for writing.~');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessageMatches('~Cannot open ".*?" for writing.~');
 
         $zipFile = new ZipFile();
         $zipFile->saveAsFile($this->outputFilename);
@@ -1752,7 +1830,7 @@ class ZipFileTest extends ZipTestCase
     public function testZipFileArrayAccessAndCountableAndIterator()
     {
         $files = [];
-        $numFiles = mt_rand(20, 100);
+        $numFiles = random_int(20, 100);
         for ($i = 0; $i < $numFiles; $i++) {
             $files['file' . $i . '.txt'] = random_bytes(255);
         }
@@ -1894,7 +1972,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddEmptyDirNullName()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Entry name is null');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Entry name is null');
 
         $zipFile = new ZipFile();
         $zipFile->addEmptyDir(null);
@@ -1905,7 +1984,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testAddEmptyDirEmptyName()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Empty entry name');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Empty entry name');
 
         $zipFile = new ZipFile();
         $zipFile->addEmptyDir('');
@@ -1913,7 +1993,8 @@ class ZipFileTest extends ZipTestCase
 
     public function testNotFoundEntry()
     {
-        $this->setExpectedException(ZipEntryNotFoundException::class, '"bad entry name"');
+        $this->expectException(ZipEntryNotFoundException::class);
+        $this->expectExceptionMessage('"bad entry name"');
 
         $zipFile = new ZipFile();
         $zipFile['bad entry name'];
@@ -1960,7 +2041,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testRewriteString()
     {
-        $this->setExpectedException(ZipException::class, 'Overwrite is only supported for open local files');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('Overwrite is only supported for open local files');
 
         $zipFile = new ZipFile();
         $zipFile['file'] = 'content';
@@ -1986,7 +2068,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testRewriteNullStream()
     {
-        $this->setExpectedException(ZipException::class, 'input stream is null');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('input stream is null');
 
         $zipFile = new ZipFile();
         $zipFile->rewrite();
@@ -2177,7 +2260,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testInvalidCompressionLevel()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Invalid compression level');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Invalid compression level');
 
         $zipFile = new ZipFile();
         $zipFile->addFromString('file', 'content');
@@ -2189,7 +2273,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testInvalidCompressionLevelEntry()
     {
-        $this->setExpectedException(InvalidArgumentException::class, 'Invalid compression level');
+        $this->expectException(InvalidArgumentException::class);
+        $this->expectExceptionMessage('Invalid compression level');
 
         $zipFile = new ZipFile();
         $zipFile->addFromString('file', 'content');
@@ -2249,8 +2334,10 @@ class ZipFileTest extends ZipTestCase
      */
     public function testInvalidCompressionMethodEntry()
     {
-        $this->setExpectedException(
-            ZipUnsupportMethodException::class,
+        $this->expectException(
+            ZipUnsupportMethodException::class
+        );
+        $this->expectExceptionMessage(
             'Compression method 99 (AES Encryption) is not supported.'
         );
 
@@ -2428,7 +2515,7 @@ class ZipFileTest extends ZipTestCase
     {
         for ($i = 0; $i < 2; $i++) {
             $fp = $zipFile->getEntryStream($entryName);
-            static::assertInternalType('resource', $fp);
+            static::assertIsResource($fp);
             static::assertSame(stream_get_contents($fp), $contents);
             fclose($fp);
         }
@@ -2566,7 +2653,8 @@ class ZipFileTest extends ZipTestCase
      */
     public function testNoData()
     {
-        $this->setExpectedException(ZipException::class, 'No data for zip entry file');
+        $this->expectException(ZipException::class);
+        $this->expectExceptionMessage('No data for zip entry file');
 
         $entryName = 'file';
 

+ 4 - 2
tests/ZipInfoTest.php

@@ -54,8 +54,10 @@ final class ZipInfoTest extends ZipTestCase
      */
     public function testZipInfoEntryNotFound()
     {
-        $this->setExpectedException(
-            ZipEntryNotFoundException::class,
+        $this->expectException(
+            ZipEntryNotFoundException::class
+        );
+        $this->expectExceptionMessage(
             'Zip Entry "unknown.name" was not found in the archive.'
         );
 

+ 1 - 1
tests/ZipMatcherTest.php

@@ -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);

+ 31 - 25
tests/ZipPasswordTest.php

@@ -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
@@ -100,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
@@ -132,8 +130,10 @@ 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.'
             );
         }
@@ -155,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();
@@ -190,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();
@@ -215,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.'
             );
         }
@@ -247,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());
@@ -266,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.'
             );
         }
@@ -301,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());
 
@@ -321,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;
@@ -369,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);
@@ -471,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();

+ 5 - 5
tests/ZipStreamOpenTest.php

@@ -31,18 +31,18 @@ class ZipStreamOpenTest extends TestCase
     {
         if ($resource === null || $resource === false) {
             static::markTestSkipped('skip resource');
-
-            return;
         }
 
         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);

+ 13 - 15
tests/ZipTestCase.php

@@ -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,7 +22,7 @@ abstract class ZipTestCase extends TestCase
      *
      * @noinspection PhpMissingParentCallCommonInspection
      */
-    protected function setUp()
+    protected function setUp(): void
     {
         $id = uniqid('phpzip', false);
         $tempDir = sys_get_temp_dir() . \DIRECTORY_SEPARATOR . 'phpunit-phpzip';
@@ -37,10 +37,8 @@ abstract class ZipTestCase extends TestCase
     /**
      * After test.
      */
-    protected function tearDown()
+    protected function tearDown(): void
     {
-        parent::tearDown();
-
         if ($this->outputFilename !== null && file_exists($this->outputFilename)) {
             unlink($this->outputFilename);
         }
@@ -92,8 +90,8 @@ abstract class ZipTestCase extends TestCase
         $output = implode(\PHP_EOL, $output);
 
         static::assertSame($returnCode, 0);
-        static::assertNotContains(' Errors', $output);
-        static::assertContains(' Ok', $output);
+        static::assertStringNotContainsString(' Errors', $output);
+        static::assertStringContainsString(' Ok', $output);
     }
 
     /**
@@ -123,8 +121,8 @@ abstract class ZipTestCase extends TestCase
 
         static::assertSame($returnCode, 0, $output);
         static::assertNotContains('incorrect password', $output);
-        static::assertContains(' OK', $output);
-        static::assertContains('No errors', $output);
+        static::assertStringContainsString(' OK', $output);
+        static::assertStringContainsString('No errors', $output);
     }
 
     /**
@@ -135,9 +133,9 @@ abstract class ZipTestCase extends TestCase
      */
     protected static function existsProgram($program, array $successCodes = [0])
     {
-        $command = \DIRECTORY_SEPARATOR === '\\' ?
-            escapeshellarg($program) :
-            'which ' . escapeshellarg($program);
+        $command = \DIRECTORY_SEPARATOR === '\\'
+            ? escapeshellarg($program)
+            : 'which ' . escapeshellarg($program);
         $command .= ' 2>&1';
 
         exec($command, $output, $returnCode);
@@ -157,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);
@@ -189,7 +187,7 @@ abstract class ZipTestCase extends TestCase
         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);
+        fwrite(\STDERR, ' 3. Add zipalign path to $Path' . \PHP_EOL);
 
         return null;
     }

Vissa filer visades inte eftersom för många filer har ändrats