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

8 Commits

Author SHA1 Message Date
Ne-Lexa
c9130665cd fix test 2021-05-21 09:02:22 +03:00
Ne-Lexa
ac3533dece fix test 2021-05-16 23:40:27 +03:00
Ne-Lexa
bb1a607d0d fix test 2021-05-16 23:28:33 +03:00
Ne-Lexa
8954da9e32 fix test for Windows
issue #78
2021-05-16 23:20:34 +03:00
Ne-Lexa
c3523992ed fix test
issue #78
2021-05-16 23:09:44 +03:00
Ne-Lexa
495dd47df4 updated phpunit config
issue #78
2021-05-16 22:56:05 +03:00
Ne-Lexa
b126252266 removed platform config from composer
issue #78
2021-05-16 22:41:26 +03:00
Ne-Lexa
584784c119 php 7.1 - 8.0 compatible version
issue #78
2021-05-16 12:18:23 +03:00
123 changed files with 7935 additions and 5186 deletions

6
.gitattributes vendored
View File

@@ -1,11 +1,7 @@
.gitattributes export-ignore .gitattributes export-ignore
.github export-ignore .github export-ignore
.gitignore export-ignore .gitignore export-ignore
.php_cs export-ignore .php-cs-fixer.php export-ignore
bootstrap.php export-ignore bootstrap.php export-ignore
logo.svg export-ignore
phpunit.xml export-ignore phpunit.xml export-ignore
phpunit.xml.dist export-ignore
psalm.xml export-ignore
psalm.xml.dist export-ignore
tests export-ignore tests export-ignore

View File

@@ -9,8 +9,8 @@ jobs:
name: PHP ${{ matrix.php }} Test on ${{ matrix.os }} name: PHP ${{ matrix.php }} Test on ${{ matrix.os }}
env: env:
extensions: bz2, dom, iconv, fileinfo, openssl, xml, zlib extensions: bz2, dom, iconv, fileinfo, openssl, xml, zlib, gd
key: cache-v1 key: cache-v2
PHPUNIT_COVERAGE: 0 PHPUNIT_COVERAGE: 0
PHP_INI: date.timezone='UTC', memory_limit=-1, opcache.enable=1, opcache.enable_cli=1 PHP_INI: date.timezone='UTC', memory_limit=-1, opcache.enable=1, opcache.enable_cli=1
@@ -21,9 +21,11 @@ jobs:
- windows-latest - windows-latest
- macos-latest - macos-latest
php: php:
- '7.1'
- '7.2'
- '7.3'
- '7.4' - '7.4'
- '8.0' - '8.0'
- '8.1'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -35,7 +37,7 @@ jobs:
- -
name: Install linux dependencies name: Install linux dependencies
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install unzip p7zip-full run: sudo apt-get install unzip p7zip-full zipalign
- -
name: Install windows dependencies name: Install windows dependencies
@@ -49,12 +51,12 @@ jobs:
- -
name: Disable JIT for PHP 8 on Linux and Mac name: Disable JIT for PHP 8 on Linux and Mac
if: (matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest') && matrix.php != '7.4' 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 run: echo "PHP_INI=\"${PHP_INI}, opcache.jit=0, opcache.jit_buffer_size=0\"" >> $GITHUB_ENV
- -
name: Disable JIT for PHP 8 on Windows name: Disable JIT for PHP 8 on Windows
if: matrix.os == 'windows-latest' && matrix.php != '7.4' 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 run: echo "PHP_INI=\"$PHP_INI, opcache.jit=0, opcache.jit_buffer_size=0\"" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- -
@@ -120,10 +122,6 @@ jobs:
if: env.PHPUNIT_COVERAGE == 1 if: env.PHPUNIT_COVERAGE == 1
run: vendor/bin/phpunit -v --coverage-clover=coverage.clover --testsuite "all_tests" --group small,medium,large run: vendor/bin/phpunit -v --coverage-clover=coverage.clover --testsuite "all_tests" --group small,medium,large
-
name: Static analysis
run: vendor/bin/psalm --shepherd --stats --output-format=checkstyle | cs2pr --graceful-warnings --colorize
- -
name: Upload code coverage scrutinizer name: Upload code coverage scrutinizer
if: env.PHPUNIT_COVERAGE == 1 if: env.PHPUNIT_COVERAGE == 1

9
.gitignore vendored
View File

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

View File

@@ -2,27 +2,17 @@
declare(strict_types=1); declare(strict_types=1);
/* /**
* PHP Code Style Fixer (config created for version 3.4.0 (Si!)). * PHP Code Style Fixer (config created for version 3.0.0 (Constitution)).
* *
* Use one of the following console commands to just see the * Use one of the following console commands to just see the
* changes that will be made. * changes that will be made.
* - `php-cs-fixer fix --config='.php-cs-fixer.php' --dry-run` * - `php-cs-fixer fix --config='.php-cs-fixer.php' --dry-run`
* - `php '.php-cs-fixer.php'` * - `php '.php-cs-fixer.php'`
* - `php7.1 '.php-cs-fixer.php'`
* - `php7.2 '.php-cs-fixer.php'`
* - `php7.3 '.php-cs-fixer.php'`
* - `php7.4 '.php-cs-fixer.php'`
* - `php8.0 '.php-cs-fixer.php'`
* *
* Use one of the following console commands to fix PHP code: * Use one of the following console commands to fix PHP code:
* - `php-cs-fixer fix --config='.php-cs-fixer.php' * - `php-cs-fixer fix --config='.php-cs-fixer.php'
* - `php '.php-cs-fixer.php' --force` * - `php '.php-cs-fixer.php' --force`
* - `php7.1 '.php-cs-fixer.php' --force`
* - `php7.2 '.php-cs-fixer.php' --force`
* - `php7.3 '.php-cs-fixer.php' --force`
* - `php7.4 '.php-cs-fixer.php' --force`
* - `php8.0 '.php-cs-fixer.php' --force`
* *
* @see https://cs.symfony.com/ * @see https://cs.symfony.com/
*/ */
@@ -49,9 +39,6 @@ $rules = [
'syntax' => 'short', 'syntax' => 'short',
], ],
// Use the null coalescing assignment operator `??=` where possible.
'assign_null_coalescing_to_coalesce_equal' => true,
/* /*
* Converts backtick operators to `shell_exec` calls. * Converts backtick operators to `shell_exec` calls.
* *
@@ -74,11 +61,7 @@ $rules = [
'blank_line_after_opening_tag' => true, 'blank_line_after_opening_tag' => true,
// An empty line feed must precede any configured statement. // An empty line feed must precede any configured statement.
'blank_line_before_statement' => [ 'blank_line_before_statement' => true,
'statements' => [
'return',
],
],
/* /*
* The body of each structure MUST be enclosed by braces. Braces * The body of each structure MUST be enclosed by braces. Braces
@@ -86,7 +69,6 @@ $rules = [
* indented. * indented.
*/ */
'braces' => [ 'braces' => [
'allow_single_line_anonymous_class_with_empty_body' => true,
'allow_single_line_closure' => true, 'allow_single_line_closure' => true,
], ],
@@ -103,7 +85,12 @@ $rules = [
* Whitespace around the keywords of a class, trait or interfaces * Whitespace around the keywords of a class, trait or interfaces
* definition should be one space. * definition should be one space.
*/ */
'class_definition' => true, 'class_definition' => [
'single_item_single_line' => true,
],
// Converts `::class` keywords to FQCN strings.
'class_keyword_remove' => false,
// Namespace must not contain spacing, comments or PHPDoc. // Namespace must not contain spacing, comments or PHPDoc.
'clean_namespace' => true, 'clean_namespace' => true,
@@ -155,12 +142,6 @@ $rules = [
*/ */
'constant_case' => true, 'constant_case' => true,
/*
* Control structure continuation keyword must be on the configured
* line.
*/
'control_structure_continuation_position' => true,
/* /*
* Class `DateTimeImmutable` should be used instead of `DateTime`. * Class `DateTimeImmutable` should be used instead of `DateTime`.
* *
@@ -176,16 +157,13 @@ $rules = [
*/ */
'declare_equal_normalize' => true, 'declare_equal_normalize' => true,
// There must not be spaces around `declare` statement parentheses.
'declare_parentheses' => true,
/* /*
* Force strict types declaration in all files. Requires PHP >= 7.0. * Force strict types declaration in all files. Requires PHP >= 7.0.
* *
* Risky! * Risky!
* Forcing strict types will stop non strict code from working. * Forcing strict types will stop non strict code from working.
*/ */
'declare_strict_types' => true, 'declare_strict_types' => false,
/* /*
* Replaces `dirname(__FILE__)` expression with equivalent `__DIR__` * Replaces `dirname(__FILE__)` expression with equivalent `__DIR__`
@@ -200,7 +178,9 @@ $rules = [
* Doctrine annotations must use configured operator for assignment * Doctrine annotations must use configured operator for assignment
* in arrays. * in arrays.
*/ */
'doctrine_annotation_array_assignment' => true, 'doctrine_annotation_array_assignment' => [
'operator' => ':',
],
/* /*
* Doctrine annotations without arguments must use the configured * Doctrine annotations without arguments must use the configured
@@ -219,17 +199,15 @@ $rules = [
* space around named arguments assignment operator; there must be * space around named arguments assignment operator; there must be
* one space around array assignment operator. * 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 * Replaces short-echo `<?=` with long format `<?php echo`/`<?php
* print` syntax, or vice-versa. * print` syntax, or vice-versa.
*/ */
'echo_tag_syntax' => [ 'echo_tag_syntax' => true,
'format' => 'short',
'long_function' => 'echo',
'shorten_simple_statements_only' => true,
],
/* /*
* The keyword `elseif` should be used instead of `else if` so that * The keyword `elseif` should be used instead of `else if` so that
@@ -237,14 +215,6 @@ $rules = [
*/ */
'elseif' => true, 'elseif' => true,
// Empty loop-body must be in configured style.
'empty_loop_body' => [
'style' => 'braces',
],
// Empty loop-condition must be in configured style.
'empty_loop_condition' => true,
// PHP code MUST use only UTF-8 without BOM (remove BOM). // PHP code MUST use only UTF-8 without BOM (remove BOM).
'encoding' => true, 'encoding' => true,
@@ -411,27 +381,13 @@ $rules = [
* Risky when any of the configured functions to replace are * Risky when any of the configured functions to replace are
* overridden. * overridden.
*/ */
'function_to_constant' => [ 'function_to_constant' => true,
'functions' => [
'get_called_class',
'get_class',
'get_class_this',
'php_sapi_name',
'phpversion',
'pi',
],
],
// Ensure single space between function's argument and its typehint. // Ensure single space between function's argument and its typehint.
'function_typehint_space' => true, 'function_typehint_space' => true,
// Configured annotations should be omitted from PHPDoc. // Configured annotations should be omitted from PHPDoc.
'general_phpdoc_annotation_remove' => [ 'general_phpdoc_annotation_remove' => true,
'annotations' => [
'author',
'license',
],
],
// Renames PHPDoc tags. // Renames PHPDoc tags.
'general_phpdoc_tag_rename' => [ 'general_phpdoc_tag_rename' => [
@@ -451,21 +407,13 @@ $rules = [
'group_import' => false, 'group_import' => false,
// Add, replace or remove header comment. // Add, replace or remove header comment.
'header_comment' => [ 'header_comment' => false,
'header' => 'This file is part of the nelexa/zip package.'."\n"
.'(c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>'."\n"
.'For the full copyright and license information, please view the LICENSE'."\n"
.'file that was distributed with this source code.',
'comment_type' => 'comment',
'location' => 'after_declare_strict',
'separate' => 'both',
],
/* /*
* Heredoc/nowdoc content must be properly indented. Requires PHP >= * Heredoc/nowdoc content must be properly indented. Requires PHP >=
* 7.3. * 7.3.
*/ */
'heredoc_indentation' => true, 'heredoc_indentation' => false,
// Convert `heredoc` to `nowdoc` where possible. // Convert `heredoc` to `nowdoc` where possible.
'heredoc_to_nowdoc' => true, 'heredoc_to_nowdoc' => true,
@@ -494,9 +442,6 @@ $rules = [
// Code MUST use configured indentation type. // Code MUST use configured indentation type.
'indentation_type' => true, 'indentation_type' => true,
// Integer literals must be in correct case.
'integer_literal_case' => true,
/* /*
* Replaces `is_null($var)` expression with `null === $var`. * Replaces `is_null($var)` expression with `null === $var`.
* *
@@ -518,9 +463,7 @@ $rules = [
* List (`array` destructuring) assignment should be declared using * List (`array` destructuring) assignment should be declared using
* the configured syntax. Requires PHP >= 7.1. * the configured syntax. Requires PHP >= 7.1.
*/ */
'list_syntax' => [ 'list_syntax' => false,
'syntax' => 'short',
],
/* /*
* Use `&&` and `||` logical operators instead of `and` and `or`. * Use `&&` and `||` logical operators instead of `and` and `or`.
@@ -557,8 +500,7 @@ $rules = [
* function. * function.
* *
* Risky! * Risky!
* Risky when any of the functions are overridden, or when relying * Risky when any of the functions are overridden.
* on the string byte size rather than its length in characters.
*/ */
'mb_str_functions' => false, 'mb_str_functions' => false,
@@ -572,7 +514,6 @@ $rules = [
*/ */
'method_argument_space' => [ 'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline', 'on_multiline' => 'ensure_fully_multiline',
'after_heredoc' => true,
], ],
/* /*
@@ -581,16 +522,6 @@ $rules = [
*/ */
'method_chaining_indentation' => true, 'method_chaining_indentation' => true,
/*
* Replace `strpos()` calls with `str_starts_with()` or
* `str_contains()` if possible.
*
* Risky!
* Risky if `strpos`, `str_starts_with` or `str_contains` functions
* are overridden.
*/
'modernize_strpos' => false,
/* /*
* Replaces `intval`, `floatval`, `doubleval`, `strval` and * Replaces `intval`, `floatval`, `doubleval`, `strval` and
* `boolval` function calls with according type casting operator. * `boolval` function calls with according type casting operator.
@@ -718,20 +649,7 @@ $rules = [
* Removes extra blank lines and/or blank lines following * Removes extra blank lines and/or blank lines following
* configuration. * configuration.
*/ */
'no_extra_blank_lines' => [ 'no_extra_blank_lines' => true,
'tokens' => [
'case',
'continue',
'curly_brace_block',
'default',
'extra',
'parenthesis_brace_block',
'square_brace_block',
'switch',
'throw',
'use',
],
],
/* /*
* Replace accidental usage of homoglyphs (non ascii characters) in * Replace accidental usage of homoglyphs (non ascii characters) in
@@ -782,12 +700,6 @@ $rules = [
// Single-line whitespace before closing semicolon are prohibited. // Single-line whitespace before closing semicolon are prohibited.
'no_singleline_whitespace_before_semicolons' => true, 'no_singleline_whitespace_before_semicolons' => true,
/*
* There must be no space around double colons (also called Scope
* Resolution Operator or Paamayim Nekudotayim).
*/
'no_space_around_double_colon' => true,
/* /*
* When making a method or function call, there MUST NOT be a space * When making a method or function call, there MUST NOT be a space
* between the method or function name and the opening parenthesis. * between the method or function name and the opening parenthesis.
@@ -810,10 +722,7 @@ $rules = [
* Removes `@param`, `@return` and `@var` tags that don't provide * Removes `@param`, `@return` and `@var` tags that don't provide
* any useful information. * any useful information.
*/ */
'no_superfluous_phpdoc_tags' => [ 'no_superfluous_phpdoc_tags' => false,
'allow_mixed' => true,
'allow_unused_params' => true,
],
// Remove trailing commas in list function calls. // Remove trailing commas in list function calls.
'no_trailing_comma_in_list_call' => true, 'no_trailing_comma_in_list_call' => true,
@@ -834,7 +743,7 @@ $rules = [
* Changing the whitespaces in strings might affect string * Changing the whitespaces in strings might affect string
* comparisons and outputs. * comparisons and outputs.
*/ */
'no_trailing_whitespace_in_string' => true, 'no_trailing_whitespace_in_string' => false,
// Removes unneeded parentheses around control statements. // Removes unneeded parentheses around control statements.
'no_unneeded_control_parentheses' => [ 'no_unneeded_control_parentheses' => [
@@ -854,7 +763,9 @@ $rules = [
* Removes unneeded curly braces that are superfluous and aren't * Removes unneeded curly braces that are superfluous and aren't
* part of a control structure's body. * 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` * A `final` class must not have `final` methods and `private`
@@ -874,7 +785,7 @@ $rules = [
* systems (such as some Symfony components) that rely on those (for * systems (such as some Symfony components) that rely on those (for
* example through reflection). * example through reflection).
*/ */
'no_unreachable_default_argument_value' => true, 'no_unreachable_default_argument_value' => false,
// Variables must be set `null` instead of using `(unset)` casting. // Variables must be set `null` instead of using `(unset)` casting.
'no_unset_cast' => true, 'no_unset_cast' => true,
@@ -890,7 +801,7 @@ $rules = [
* PHP 7.4, this rule might introduce `null` assignments to * PHP 7.4, this rule might introduce `null` assignments to
* properties whose type declaration does not allow it. * properties whose type declaration does not allow it.
*/ */
'no_unset_on_property' => false, 'no_unset_on_property' => true,
// Unused `use` statements must be removed. // Unused `use` statements must be removed.
'no_unused_imports' => true, 'no_unused_imports' => true,
@@ -916,9 +827,7 @@ $rules = [
* In array declaration, there MUST NOT be a whitespace before each * In array declaration, there MUST NOT be a whitespace before each
* comma. * comma.
*/ */
'no_whitespace_before_comma_in_array' => [ 'no_whitespace_before_comma_in_array' => true,
'after_heredoc' => true,
],
// Remove trailing whitespace at the end of blank lines. // Remove trailing whitespace at the end of blank lines.
'no_whitespace_in_blank_line' => true, 'no_whitespace_in_blank_line' => true,
@@ -950,7 +859,7 @@ $rules = [
* *
* Rule is applied only in a PHP 7.1+ environment. * Rule is applied only in a PHP 7.1+ environment.
*/ */
'nullable_type_declaration_for_default_null_value' => true, 'nullable_type_declaration_for_default_null_value' => false,
/* /*
* There should not be space before or after object operators `->` * There should not be space before or after object operators `->`
@@ -958,9 +867,6 @@ $rules = [
*/ */
'object_operator_without_whitespace' => true, 'object_operator_without_whitespace' => true,
// Literal octal must be in `0o` notation.
'octal_notation' => true,
/* /*
* Operators - when multiline - must always be at the beginning or * Operators - when multiline - must always be at the beginning or
* at the end of the line. * at the end of the line.
@@ -971,6 +877,16 @@ $rules = [
'ordered_class_elements' => [ 'ordered_class_elements' => [
'order' => [ 'order' => [
'use_trait', 'use_trait',
'constant_public',
'constant_protected',
'constant_private',
'property_public',
'property_protected',
'property_private',
'construct',
'destruct',
'magic',
'phpunit',
], ],
], ],
@@ -1001,7 +917,7 @@ $rules = [
* Risky! * Risky!
* Risky when depending on order of the imports. * Risky when depending on order of the imports.
*/ */
'ordered_traits' => true, 'ordered_traits' => false,
/* /*
* PHPUnit assertion method calls like `->assertSame(true, $foo)` * PHPUnit assertion method calls like `->assertSame(true, $foo)`
@@ -1022,9 +938,7 @@ $rules = [
* Fixer could be risky if one is overriding PHPUnit's native * Fixer could be risky if one is overriding PHPUnit's native
* methods. * methods.
*/ */
'php_unit_dedicate_assert' => [ 'php_unit_dedicate_assert' => true,
'target' => 'newest',
],
/* /*
* PHPUnit assertions like `assertIsArray` should be used over * PHPUnit assertions like `assertIsArray` should be used over
@@ -1044,7 +958,9 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or * Risky when PHPUnit classes are overridden or not accessible, or
* when project has PHPUnit incompatibilities. * when project has PHPUnit incompatibilities.
*/ */
'php_unit_expectation' => true, 'php_unit_expectation' => [
'target' => '5.6',
],
// PHPUnit annotations should be a FQCNs including a root namespace. // PHPUnit annotations should be a FQCNs including a root namespace.
'php_unit_fqcn_annotation' => true, 'php_unit_fqcn_annotation' => true,
@@ -1109,7 +1025,9 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or * Risky when PHPUnit classes are overridden or not accessible, or
* when project has PHPUnit incompatibilities. * when project has PHPUnit incompatibilities.
*/ */
'php_unit_no_expectation_annotation' => true, 'php_unit_no_expectation_annotation' => [
'target' => 'newest',
],
/* /*
* Changes the visibility of the `setUp()` and `tearDown()` * Changes the visibility of the `setUp()` and `tearDown()`
@@ -1177,18 +1095,7 @@ $rules = [
* All items of the given phpdoc tags must be either left-aligned or * All items of the given phpdoc tags must be either left-aligned or
* (by default) aligned vertically. * (by default) aligned vertically.
*/ */
'phpdoc_align' => [ 'phpdoc_align' => true,
'tags' => [
'return',
'throws',
'type',
'var',
'property',
'method',
'param',
],
'align' => 'vertical',
],
// PHPDoc annotation descriptions should not be a sentence. // PHPDoc annotation descriptions should not be a sentence.
'phpdoc_annotation_without_dot' => true, 'phpdoc_annotation_without_dot' => true,
@@ -1241,7 +1148,15 @@ $rules = [
'phpdoc_order' => true, 'phpdoc_order' => true,
// Order phpdoc tags by value. // Order phpdoc tags by value.
'phpdoc_order_by_value' => true, 'phpdoc_order_by_value' => [
'annotations' => [
'covers',
'method',
'property',
'property-read',
'property-write',
],
],
/* /*
* The type of `@return` annotations of methods returning a * The type of `@return` annotations of methods returning a
@@ -1296,7 +1211,7 @@ $rules = [
* docblock, inheritdocs) will not be fixed. [3] Manual actions are * docblock, inheritdocs) will not be fixed. [3] Manual actions are
* required if inherited signatures are not properly documented. * required if inherited signatures are not properly documented.
*/ */
'phpdoc_to_param_type' => true, 'phpdoc_to_param_type' => false,
/* /*
* EXPERIMENTAL: Takes `@var` annotation of non-mixed types and * EXPERIMENTAL: Takes `@var` annotation of non-mixed types and
@@ -1322,7 +1237,7 @@ $rules = [
* docblock, inheritdocs) will not be fixed. [3] Manual actions are * docblock, inheritdocs) will not be fixed. [3] Manual actions are
* required if inherited signatures are not properly documented. * required if inherited signatures are not properly documented.
*/ */
'phpdoc_to_return_type' => true, 'phpdoc_to_return_type' => false,
/* /*
* PHPDoc should start and end with content, excluding the very * PHPDoc should start and end with content, excluding the very
@@ -1355,7 +1270,7 @@ $rules = [
* `@var` and `@type` annotations of classy properties should not * `@var` and `@type` annotations of classy properties should not
* contain the name. * contain the name.
*/ */
'phpdoc_var_without_name' => true, 'phpdoc_var_without_name' => false,
/* /*
* Converts `pow` to the `**` operator. * Converts `pow` to the `**` operator.
@@ -1384,11 +1299,10 @@ $rules = [
/* /*
* Replaces `rand`, `srand`, `getrandmax` functions calls with their * Replaces `rand`, `srand`, `getrandmax` functions calls with their
* `mt_*` analogs or `random_int`. * `mt_*` analogs.
* *
* Risky! * Risky!
* Risky when the configured functions are overridden. Or when * Risky when the configured functions are overridden.
* relying on the seed based generating of the numbers.
*/ */
'random_api_migration' => [ 'random_api_migration' => [
'replacements' => [ 'replacements' => [
@@ -1477,7 +1391,7 @@ $rules = [
* A return statement wishing to return `void` should not return * A return statement wishing to return `void` should not return
* `null`. * `null`.
*/ */
'simplified_null_return' => true, 'simplified_null_return' => false,
/* /*
* A PHP file without end tag must always end with a single empty * A PHP file without end tag must always end with a single empty
@@ -1551,7 +1465,7 @@ $rules = [
* Risky! * Risky!
* Changing comparisons to strict might change code behavior. * Changing comparisons to strict might change code behavior.
*/ */
'strict_comparison' => true, 'strict_comparison' => false,
/* /*
* Functions should be used with `$strict` param set to `true`. * Functions should be used with `$strict` param set to `true`.
@@ -1564,18 +1478,7 @@ $rules = [
* Risky when the fixed function is overridden or if the code relies * Risky when the fixed function is overridden or if the code relies
* on non-strict usage. * on non-strict usage.
*/ */
'strict_param' => true, 'strict_param' => false,
/*
* String tests for empty must be done against `''`, not with
* `strlen`.
*
* Risky!
* Risky when `strlen` is overridden, when called using a
* `stringable` object, also no longer triggers warning when called
* using non-string(able).
*/
'string_length_to_empty' => true,
/* /*
* All multi-line strings must use correct line ending. * All multi-line strings must use correct line ending.
@@ -1617,9 +1520,7 @@ $rules = [
* Multi-line arrays, arguments list and parameters list must have a * Multi-line arrays, arguments list and parameters list must have a
* trailing comma. * trailing comma.
*/ */
'trailing_comma_in_multiline' => [ 'trailing_comma_in_multiline' => true,
'after_heredoc' => true,
],
/* /*
* Arrays should be formatted like function/method arguments, * Arrays should be formatted like function/method arguments,
@@ -1627,9 +1528,6 @@ $rules = [
*/ */
'trim_array_spaces' => true, 'trim_array_spaces' => true,
// A single space or none should be around union type operator.
'types_spaces' => true,
// Unary operators should be placed adjacent to their operands. // Unary operators should be placed adjacent to their operands.
'unary_operator_spaces' => true, 'unary_operator_spaces' => true,
@@ -1641,7 +1539,7 @@ $rules = [
* Risky when using `isset()` on outside variables that are not * Risky when using `isset()` on outside variables that are not
* imported with `use ()`. * imported with `use ()`.
*/ */
'use_arrow_functions' => true, 'use_arrow_functions' => false,
/* /*
* Visibility MUST be declared on all properties and methods; * Visibility MUST be declared on all properties and methods;
@@ -1650,7 +1548,6 @@ $rules = [
*/ */
'visibility_required' => [ 'visibility_required' => [
'elements' => [ 'elements' => [
'const',
'method', 'method',
'property', 'property',
], ],
@@ -1664,7 +1561,7 @@ $rules = [
* Risky! * Risky!
* Modifies the signature of functions. * Modifies the signature of functions.
*/ */
'void_return' => true, 'void_return' => false,
/* /*
* In array declaration, there MUST be a whitespace after each * In array declaration, there MUST be a whitespace after each
@@ -1686,51 +1583,19 @@ $rules = [
]; ];
if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) { if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) {
$which = static function ($program, $default = null) { $binFixer = __DIR__ . '/vendor/bin/php-cs-fixer';
exec(sprintf('command -v %s', escapeshellarg($program)), $output, $resultCode);
if ($resultCode === 0) {
return trim($output[0]);
}
return $default;
};
$findExistsFile = static function (array $files): ?string {
foreach ($files as $file) {
if ($file !== null && is_file($file)) {
return $file;
}
}
return null;
};
$fixerBinaries = [
__DIR__ . '/vendor/bin/php-cs-fixer',
__DIR__ . '/tools/php-cs-fixer/vendor/bin/php-cs-fixer',
$which('php-cs-fixer'),
isset($_SERVER['COMPOSER_HOME']) ? $_SERVER['COMPOSER_HOME'] . '/vendor/bin/php-cs-fixer' : null,
];
$fixerBin = $findExistsFile($fixerBinaries) ?? 'php-cs-fixer';
$phpBin = $_SERVER['_'] ?? 'php';
if (!is_file($binFixer)) {
$binFixer = 'php-cs-fixer';
}
$dryRun = !in_array('--force', $_SERVER['argv'], true); $dryRun = !in_array('--force', $_SERVER['argv'], true);
$commandFormat = '%s %s fix --config %s --diff --ansi -vv%s';
$command = sprintf(
$commandFormat,
escapeshellarg($phpBin),
escapeshellarg($fixerBin),
escapeshellarg(__FILE__),
$dryRun ? ' --dry-run' : ''
);
$outputCommand = sprintf(
$commandFormat,
$phpBin,
strpos($fixerBin, ' ') === false ? $fixerBin : escapeshellarg($fixerBin),
escapeshellarg(__FILE__),
$dryRun ? ' --dry-run' : ''
);
fwrite(\STDOUT, "\e[22;94m" . $outputCommand . "\e[m\n\n"); $command = escapeshellarg($binFixer) . ' fix --config ' . escapeshellarg(__FILE__) . ' --diff --ansi -vv';
if ($dryRun) {
$command .= ' --dry-run';
}
system($command, $returnCode); system($command, $returnCode);
if ($dryRun || $returnCode === 8) { if ($dryRun || $returnCode === 8) {
@@ -1738,16 +1603,9 @@ if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) {
fwrite(\STDOUT, " [DEBUG] Dry run php-cs-fixer config.\e[K\n"); fwrite(\STDOUT, " [DEBUG] Dry run php-cs-fixer config.\e[K\n");
fwrite(\STDOUT, " Only shows which files would have been modified.\e[K\n"); fwrite(\STDOUT, " Only shows which files would have been modified.\e[K\n");
fwrite(\STDOUT, " To apply the rules, use the --force option:\e[K\n\e[K\n"); fwrite(\STDOUT, " To apply the rules, use the --force option:\e[K\n\e[K\n");
fwrite( fwrite(\STDOUT, " \e[1;40;92mphp {$_SERVER['argv'][0]} --force\e[K\n\e[0m\n");
\STDOUT,
sprintf(
" \e[1;40;92m%s %s --force\e[K\n\e[0m\n",
basename($phpBin),
$_SERVER['argv'][0]
)
);
} elseif ($returnCode !== 0) { } elseif ($returnCode !== 0) {
fwrite(\STDERR, sprintf("\n\e[1;41;97m\e[K\n ERROR CODE: %s\e[K\n\e[0m\n", $returnCode)); fwrite(\STDERR, "\n\e[1;41;97m\e[K\n ERROR CODE: {$returnCode}\e[K\n\e[0m\n");
} }
exit($returnCode); exit($returnCode);

View File

@@ -65,12 +65,6 @@ namespace PHPSTORM_META {
expectedArguments(\PhpZip\ZipFile::outputAsResponse(), 2, argumentsSet("zip_mime_types")); expectedArguments(\PhpZip\ZipFile::outputAsResponse(), 2, argumentsSet("zip_mime_types"));
expectedArguments(\PhpZip\ZipFile::outputAsResponse(), 3, argumentsSet("bool")); expectedArguments(\PhpZip\ZipFile::outputAsResponse(), 3, argumentsSet("bool"));
expectedArguments(\PhpZip\ZipFile::outputAsPsr7Response(), 2, argumentsSet("zip_mime_types"));
expectedArguments(\PhpZip\ZipFile::outputAsPsr7Response(), 3, argumentsSet("bool"));
expectedArguments(\PhpZip\ZipFile::outputAsSymfonyResponse(), 1, argumentsSet("zip_mime_types"));
expectedArguments(\PhpZip\ZipFile::outputAsSymfonyResponse(), 2, argumentsSet("bool"));
registerArgumentsSet( registerArgumentsSet(
'dos_charset', 'dos_charset',
\PhpZip\Constants\DosCodePage::CP_LATIN_US, \PhpZip\Constants\DosCodePage::CP_LATIN_US,

67
.travis.yml Normal file
View File

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

View File

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

465
README.md
View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="bootstrap.php"> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" colors="true" bootstrap="bootstrap.php">
<coverage> <coverage>
<include> <include>
<directory>src</directory> <directory>src</directory>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,19 +1,13 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants; namespace PhpZip\Constants;
use PhpZip\IO\ZipReader; use PhpZip\IO\ZipReader;
use PhpZip\ZipFile; use PhpZip\ZipFile;
/**
* Interface ZipOptions.
*/
interface ZipOptions interface ZipOptions
{ {
/** /**
@@ -21,7 +15,7 @@ interface ZipOptions
* *
* @see ZipFile::addFromFinder() * @see ZipFile::addFromFinder()
*/ */
public const STORE_ONLY_FILES = 'only_files'; const STORE_ONLY_FILES = 'only_files';
/** /**
* Uses the specified compression method. * Uses the specified compression method.
@@ -29,7 +23,7 @@ interface ZipOptions
* @see ZipFile::addFromFinder() * @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile() * @see ZipFile::addSplFile()
*/ */
public const COMPRESSION_METHOD = 'compression_method'; const COMPRESSION_METHOD = 'compression_method';
/** /**
* Set the specified record modification time. * Set the specified record modification time.
@@ -39,7 +33,7 @@ interface ZipOptions
* @see ZipFile::addFromFinder() * @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile() * @see ZipFile::addSplFile()
*/ */
public const MODIFIED_TIME = 'mtime'; const MODIFIED_TIME = 'mtime';
/** /**
* Specifies the encoding of the record name for cases when the UTF-8 * Specifies the encoding of the record name for cases when the UTF-8
@@ -54,7 +48,7 @@ interface ZipOptions
* @see ZipReader::getDefaultOptions() * @see ZipReader::getDefaultOptions()
* @see DosCodePage::getCodePages() * @see DosCodePage::getCodePages()
*/ */
public const CHARSET = 'charset'; const CHARSET = 'charset';
/** /**
* Allows ({@see true}) or denies ({@see false}) unpacking unix symlinks. * Allows ({@see true}) or denies ({@see false}) unpacking unix symlinks.
@@ -64,5 +58,5 @@ interface ZipOptions
* *
* @see https://josipfranjkovic.blogspot.com/2014/12/reading-local-files-from-facebooks.html * @see https://josipfranjkovic.blogspot.com/2014/12/reading-local-files-from-facebooks.html
*/ */
public const EXTRACT_SYMLINKS = 'extract_symlinks'; const EXTRACT_SYMLINKS = 'extract_symlinks';
} }

View File

@@ -1,109 +1,53 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants; namespace PhpZip\Constants;
/**
* Class ZipPlatform.
*/
final class ZipPlatform final class ZipPlatform
{ {
/** @var int MS-DOS OS */ /** @var int MS-DOS OS */
public const OS_DOS = 0; const OS_DOS = 0;
/** @var int Amica OS */
public const OS_AMIGA = 1;
/** @var int OpenVMS */
public const OS_OPENVMS = 2;
/** @var int Unix OS */ /** @var int Unix OS */
public const OS_UNIX = 3; const OS_UNIX = 3;
/** @var int VM/CMS */ /** MacOS platform */
public const OS_VM_CMS = 4; const OS_MAC_OSX = 19;
/** @var int AtariST */
public const OS_ATARI_ST = 5;
/** @var int OS/2 (HPFS) */
public const OS_OS_2 = 6;
/** @var int Macintosh */
public const OS_MACINTOSH = 7;
/** @var int Z-System */
public const OS_Z_SYSTEM = 8;
/** @var int CPM */
public const OS_CPM = 9;
/** @var int Windows NTFS / TOPS-20 */
public const OS_WINDOWS_NTFS = 10;
/** @var int MVS */
public const OS_MVS = 11;
/** @var int VSE */
public const OS_VSE = 12;
public const OS_QDOS = 12;
/** @var int Acorn RISC OS */
public const OS_ACORN_RISC = 13;
/** @var int VFAT */
public const OS_VFAT = 14;
/** @var int alternate MVS */
public const OS_ALTERNATE_MVS = 15;
/** @var int BeOS */
public const OS_BEOS = 16;
/** @var int Tandem */
public const OS_TANDEM = 17;
/** @var int OS/400 or THEOS */
public const OS_OS_400 = 18;
/** @var int macOS */
public const OS_MAC_OSX = 19;
/** @var int AtheOS */
public const OS_ATHEOS = 30;
/** @var array Zip Platforms */ /** @var array Zip Platforms */
private const PLATFORMS = [ private static $platforms = [
self::OS_DOS => 'MS-DOS', self::OS_DOS => 'MS-DOS',
self::OS_AMIGA => 'Amiga', 1 => 'Amiga',
self::OS_OPENVMS => 'OpenVMS', 2 => 'OpenVMS',
self::OS_UNIX => 'Unix', self::OS_UNIX => 'Unix',
self::OS_VM_CMS => 'VM/CMS', 4 => 'VM/CMS',
self::OS_ATARI_ST => 'Atari ST', 5 => 'Atari ST',
self::OS_OS_2 => 'HPFS (OS/2, NT 3.x)', 6 => 'HPFS (OS/2, NT 3.x)',
self::OS_MACINTOSH => 'Macintosh', 7 => 'Macintosh',
self::OS_Z_SYSTEM => 'Z-System', 8 => 'Z-System',
self::OS_CPM => 'CP/M', 9 => 'CP/M',
self::OS_WINDOWS_NTFS => 'Windows NTFS or TOPS-20', 10 => 'Windows NTFS or TOPS-20',
self::OS_MVS => 'MVS or NTFS', 11 => 'MVS or NTFS',
self::OS_VSE => 'VSE or SMS/QDOS', 12 => 'VSE or SMS/QDOS',
self::OS_ACORN_RISC => 'Acorn RISC OS', 13 => 'Acorn RISC OS',
self::OS_VFAT => 'VFAT', 14 => 'VFAT',
self::OS_ALTERNATE_MVS => 'alternate MVS', 15 => 'alternate MVS',
self::OS_BEOS => 'BeOS', 16 => 'BeOS',
self::OS_TANDEM => 'Tandem', 17 => 'Tandem',
self::OS_OS_400 => 'OS/400', 18 => 'OS/400',
self::OS_MAC_OSX => 'OS/X (Darwin)', self::OS_MAC_OSX => 'OS/X (Darwin)',
self::OS_ATHEOS => 'AtheOS/Syllable', 30 => 'AtheOS/Syllable',
]; ];
public static function getPlatformName(int $platform): string /**
* @param int $platform
*
* @return string
*/
public static function getPlatformName($platform)
{ {
return self::PLATFORMS[$platform] ?? 'Unknown'; return self::$platforms[$platform] ?? 'Unknown';
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO; namespace PhpZip\IO;
use PhpZip\Constants\DosCodePage; use PhpZip\Constants\DosCodePage;
@@ -33,22 +24,28 @@ use PhpZip\Model\Extra\ZipExtraDriver;
use PhpZip\Model\Extra\ZipExtraField; use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ImmutableZipContainer; use PhpZip\Model\ImmutableZipContainer;
use PhpZip\Model\ZipEntry; use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/** /**
* Zip reader. * Zip reader.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/ */
class ZipReader class ZipReader
{ {
/** @var int file size */ /** @var int file size */
protected int $size; protected $size;
/** @var resource */ /** @var resource */
protected $inStream; protected $inStream;
protected array $options; /** @var array */
protected $options;
/** /**
* @param resource $inStream * @param resource $inStream
* @param array $options
*/ */
public function __construct($inStream, array $options = []) public function __construct($inStream, array $options = [])
{ {
@@ -97,7 +94,15 @@ class ZipReader
$this->options = $options; $this->options = $options;
} }
protected function getDefaultOptions(): array public function __destruct()
{
$this->close();
}
/**
* @return array
*/
protected function getDefaultOptions()
{ {
return [ return [
ZipOptions::CHARSET => null, ZipOptions::CHARSET => null,
@@ -106,8 +111,10 @@ class ZipReader
/** /**
* @throws ZipException * @throws ZipException
*
* @return ImmutableZipContainer
*/ */
public function read(): ImmutableZipContainer public function read()
{ {
if ($this->size < ZipConstants::END_CD_MIN_LEN) { if ($this->size < ZipConstants::END_CD_MIN_LEN) {
throw new ZipException('Corrupt zip file'); throw new ZipException('Corrupt zip file');
@@ -119,7 +126,10 @@ class ZipReader
return new ImmutableZipContainer($entries, $endOfCentralDirectory->getComment()); return new ImmutableZipContainer($entries, $endOfCentralDirectory->getComment());
} }
public function getStreamMetaData(): array /**
* @return array
*/
public function getStreamMetaData()
{ {
return stream_get_meta_data($this->inStream); return stream_get_meta_data($this->inStream);
} }
@@ -143,8 +153,10 @@ class ZipReader
* .ZIP file comment (variable size) * .ZIP file comment (variable size)
* *
* @throws ZipException * @throws ZipException
*
* @return EndOfCentralDirectory
*/ */
protected function readEndOfCentralDirectory(): EndOfCentralDirectory protected function readEndOfCentralDirectory()
{ {
if (!$this->findEndOfCentralDirectory()) { if (!$this->findEndOfCentralDirectory()) {
throw new ZipException('Invalid zip file. The end of the central directory could not be found.'); throw new ZipException('Invalid zip file. The end of the central directory could not be found.');
@@ -154,34 +166,26 @@ class ZipReader
$sizeECD = $this->size - ftell($this->inStream); $sizeECD = $this->size - ftell($this->inStream);
$buffer = fread($this->inStream, $sizeECD); $buffer = fread($this->inStream, $sizeECD);
[ $unpack = unpack(
'diskNo' => $diskNo,
'cdDiskNo' => $cdDiskNo,
'cdEntriesDisk' => $cdEntriesDisk,
'cdEntries' => $cdEntries,
'cdSize' => $cdSize,
'cdPos' => $cdPos,
'commentLength' => $commentLength,
] = unpack(
'vdiskNo/vcdDiskNo/vcdEntriesDisk/' 'vdiskNo/vcdDiskNo/vcdEntriesDisk/'
. 'vcdEntries/VcdSize/VcdPos/vcommentLength', . 'vcdEntries/VcdSize/VcdPos/vcommentLength',
substr($buffer, 0, 18) substr($buffer, 0, 18)
); );
if ( if (
$diskNo !== 0 $unpack['diskNo'] !== 0
|| $cdDiskNo !== 0 || $unpack['cdDiskNo'] !== 0
|| $cdEntriesDisk !== $cdEntries || $unpack['cdEntriesDisk'] !== $unpack['cdEntries']
) { ) {
throw new ZipException( throw new ZipException(
'ZIP file spanning/splitting is not supported!' 'ZIP file spanning/splitting is not supported!'
); );
} }
// .ZIP file comment (variable sizeECD)
$comment = null; $comment = null;
if ($commentLength > 0) { if ($unpack['commentLength'] > 0) {
// .ZIP file comment (variable sizeECD) $comment = substr($buffer, 18, $unpack['commentLength']);
$comment = substr($buffer, 18, $commentLength);
} }
// Check for ZIP64 End Of Central Directory Locator exists. // Check for ZIP64 End Of Central Directory Locator exists.
@@ -189,10 +193,10 @@ class ZipReader
fseek($this->inStream, $zip64ECDLocatorPosition); fseek($this->inStream, $zip64ECDLocatorPosition);
// zip64 end of central dir locator // zip64 end of central dir locator
// signature 4 bytes (0x07064b50) // signature 4 bytes (0x07064b50)
if ( if ($zip64ECDLocatorPosition > 0 && unpack(
$zip64ECDLocatorPosition > 0 'V',
&& unpack('V', fread($this->inStream, 4))[1] === ZipConstants::ZIP64_END_CD_LOC fread($this->inStream, 4)
) { )[1] === ZipConstants::ZIP64_END_CD_LOC) {
if (!$this->isZip64Support()) { if (!$this->isZip64Support()) {
throw new ZipException('ZIP64 not supported this archive.'); throw new ZipException('ZIP64 not supported this archive.');
} }
@@ -202,9 +206,9 @@ class ZipReader
$endCentralDirectory->setComment($comment); $endCentralDirectory->setComment($comment);
} else { } else {
$endCentralDirectory = new EndOfCentralDirectory( $endCentralDirectory = new EndOfCentralDirectory(
$cdEntries, $unpack['cdEntries'],
$cdPos, $unpack['cdPos'],
$cdSize, $unpack['cdSize'],
false, false,
$comment $comment
); );
@@ -213,10 +217,13 @@ class ZipReader
return $endCentralDirectory; return $endCentralDirectory;
} }
protected function findEndOfCentralDirectory(): bool /**
* @return bool
*/
protected function findEndOfCentralDirectory()
{ {
$max = $this->size - ZipConstants::END_CD_MIN_LEN; $max = $this->size - ZipConstants::END_CD_MIN_LEN;
$min = $max >= 0xFFFF ? $max - 0xFFFF : 0; $min = $max >= 0xffff ? $max - 0xffff : 0;
// Search for End of central directory record. // Search for End of central directory record.
for ($position = $max; $position >= $min; $position--) { for ($position = $max; $position >= $min; $position--) {
fseek($this->inStream, $position); fseek($this->inStream, $position);
@@ -246,13 +253,11 @@ class ZipReader
* *
* @return int Zip64 End Of Central Directory position * @return int Zip64 End Of Central Directory position
*/ */
protected function findZip64ECDPosition(): int protected function findZip64ECDPosition()
{ {
[ $diskNo = unpack('V', fread($this->inStream, 4))[1];
'diskNo' => $diskNo, $zip64ECDPos = PackUtil::unpackLongLE(fread($this->inStream, 8));
'zip64ECDPos' => $zip64ECDPos, $totalDisks = unpack('V', fread($this->inStream, 4))[1];
'totalDisks' => $totalDisks,
] = unpack('VdiskNo/Pzip64ECDPos/VtotalDisks', fread($this->inStream, 16));
if ($diskNo !== 0 || $totalDisks > 1) { if ($diskNo !== 0 || $totalDisks > 1) {
throw new ZipException('ZIP file spanning/splitting is not supported!'); throw new ZipException('ZIP file spanning/splitting is not supported!');
@@ -284,9 +289,13 @@ class ZipReader
* the starting disk number 8 bytes * the starting disk number 8 bytes
* zip64 extensible data sector (variable size) * zip64 extensible data sector (variable size)
* *
* @param int $zip64ECDPosition
*
* @throws ZipException * @throws ZipException
*
* @return EndOfCentralDirectory
*/ */
protected function readZip64EndOfCentralDirectory(int $zip64ECDPosition): EndOfCentralDirectory protected function readZip64EndOfCentralDirectory($zip64ECDPosition)
{ {
fseek($this->inStream, $zip64ECDPosition); fseek($this->inStream, $zip64ECDPosition);
@@ -296,30 +305,25 @@ class ZipReader
throw new ZipException('Expected ZIP64 End Of Central Directory Record!'); throw new ZipException('Expected ZIP64 End Of Central Directory Record!');
} }
[ $data = unpack(
// 'size' => $size, // 'Psize/vversionMadeBy/vextractVersion/' .
// 'versionMadeBy' => $versionMadeBy, 'VdiskNo/VcdDiskNo',
// 'extractVersion' => $extractVersion, substr($buffer, 16, 8)
'diskNo' => $diskNo,
'cdDiskNo' => $cdDiskNo,
'cdEntriesDisk' => $cdEntriesDisk,
'entryCount' => $entryCount,
'cdSize' => $cdSize,
'cdPos' => $cdPos,
] = unpack(
// 'Psize/vversionMadeBy/vextractVersion/'.
'VdiskNo/VcdDiskNo/PcdEntriesDisk/PentryCount/PcdSize/PcdPos',
substr($buffer, 16, 40)
); );
// $platform = ZipPlatform::fromValue(($versionMadeBy & 0xFF00) >> 8); $cdEntriesDisk = PackUtil::unpackLongLE(substr($buffer, 24, 8));
// $softwareVersion = $versionMadeBy & 0x00FF; $entryCount = PackUtil::unpackLongLE(substr($buffer, 32, 8));
$cdSize = PackUtil::unpackLongLE(substr($buffer, 40, 8));
$cdPos = PackUtil::unpackLongLE(substr($buffer, 48, 8));
if ($diskNo !== 0 || $cdDiskNo !== 0 || $entryCount !== $cdEntriesDisk) { // $platform = ZipPlatform::fromValue(($data['versionMadeBy'] & 0xFF00) >> 8);
// $softwareVersion = $data['versionMadeBy'] & 0x00FF;
if ($data['diskNo'] !== 0 || $data['cdDiskNo'] !== 0 || $entryCount !== $cdEntriesDisk) {
throw new ZipException('ZIP file spanning/splitting is not supported!'); throw new ZipException('ZIP file spanning/splitting is not supported!');
} }
if ($entryCount < 0 || $entryCount > 0x7FFFFFFF) { if ($entryCount < 0 || $entryCount > 0x7fffffff) {
throw new ZipException('Total Number Of Entries In The Central Directory out of range!'); throw new ZipException('Total Number Of Entries In The Central Directory out of range!');
} }
@@ -341,11 +345,13 @@ class ZipReader
* central directory alone, but not the data that requires the local * central directory alone, but not the data that requires the local
* file header or additional data to be read. * file header or additional data to be read.
* *
* @param EndOfCentralDirectory $endCD
*
* @throws ZipException * @throws ZipException
* *
* @return ZipEntry[] * @return ZipEntry[]
*/ */
protected function readCentralDirectory(EndOfCentralDirectory $endCD): array protected function readCentralDirectory(EndOfCentralDirectory $endCD)
{ {
$entries = []; $entries = [];
@@ -370,10 +376,13 @@ class ZipReader
if ($unicodePathExtraField !== null && $unicodePathExtraField->getCrc32() === crc32($entryName)) { if ($unicodePathExtraField !== null && $unicodePathExtraField->getCrc32() === crc32($entryName)) {
$unicodePath = $unicodePathExtraField->getUnicodeValue(); $unicodePath = $unicodePathExtraField->getUnicodeValue();
if ($unicodePath !== '') { if ($unicodePath !== null) {
$unicodePath = str_replace('\\', '/', $unicodePath); $unicodePath = str_replace('\\', '/', $unicodePath);
if (substr_count($entryName, '/') === substr_count($unicodePath, '/')) { if (
$unicodePath !== ''
&& substr_count($entryName, '/') === substr_count($unicodePath, '/')
) {
$entryName = $unicodePath; $entryName = $unicodePath;
} }
} }
@@ -413,32 +422,18 @@ class ZipReader
* @param resource $stream * @param resource $stream
* *
* @throws ZipException * @throws ZipException
*
* @return ZipEntry
*/ */
protected function readZipEntry($stream): ZipEntry protected function readZipEntry($stream)
{ {
if (unpack('V', fread($stream, 4))[1] !== ZipConstants::CENTRAL_FILE_HEADER) { if (unpack('V', fread($stream, 4))[1] !== ZipConstants::CENTRAL_FILE_HEADER) {
throw new ZipException('Corrupt zip file. Cannot read zip entry.'); throw new ZipException('Corrupt zip file. Cannot read zip entry.');
} }
[ $unpack = unpack(
'versionMadeBy' => $versionMadeBy,
'versionNeededToExtract' => $versionNeededToExtract,
'generalPurposeBitFlags' => $generalPurposeBitFlags,
'compressionMethod' => $compressionMethod,
'lastModFile' => $dosTime,
'crc' => $crc,
'compressedSize' => $compressedSize,
'uncompressedSize' => $uncompressedSize,
'fileNameLength' => $fileNameLength,
'extraFieldLength' => $extraFieldLength,
'fileCommentLength' => $fileCommentLength,
'diskNumberStart' => $diskNumberStart,
'internalFileAttributes' => $internalFileAttributes,
'externalFileAttributes' => $externalFileAttributes,
'offsetLocalHeader' => $offsetLocalHeader,
] = unpack(
'vversionMadeBy/vversionNeededToExtract/' 'vversionMadeBy/vversionNeededToExtract/'
. 'vgeneralPurposeBitFlags/vcompressionMethod/' . 'vgeneralPurposeBitFlag/vcompressionMethod/'
. 'VlastModFile/Vcrc/VcompressedSize/' . 'VlastModFile/Vcrc/VcompressedSize/'
. 'VuncompressedSize/vfileNameLength/vextraFieldLength/' . 'VuncompressedSize/vfileNameLength/vextraFieldLength/'
. 'vfileCommentLength/vdiskNumberStart/vinternalFileAttributes/' . 'vfileCommentLength/vdiskNumberStart/vinternalFileAttributes/'
@@ -446,23 +441,27 @@ class ZipReader
fread($stream, 42) fread($stream, 42)
); );
if ($diskNumberStart !== 0) { if ($unpack['diskNumberStart'] !== 0) {
throw new ZipException('ZIP file spanning/splitting is not supported!'); throw new ZipException('ZIP file spanning/splitting is not supported!');
} }
$generalPurposeBitFlags = $unpack['generalPurposeBitFlag'];
$isUtf8 = ($generalPurposeBitFlags & GeneralPurposeBitFlag::UTF8) !== 0; $isUtf8 = ($generalPurposeBitFlags & GeneralPurposeBitFlag::UTF8) !== 0;
$name = fread($stream, $fileNameLength); $name = fread($stream, $unpack['fileNameLength']);
$createdOS = ($versionMadeBy & 0xFF00) >> 8; $createdOS = ($unpack['versionMadeBy'] & 0xFF00) >> 8;
$softwareVersion = $versionMadeBy & 0x00FF; $softwareVersion = $unpack['versionMadeBy'] & 0x00FF;
$extractedOS = ($unpack['versionNeededToExtract'] & 0xFF00) >> 8;
$extractVersion = $unpack['versionNeededToExtract'] & 0x00FF;
$dosTime = $unpack['lastModFile'];
$extractedOS = ($versionNeededToExtract & 0xFF00) >> 8;
$extractVersion = $versionNeededToExtract & 0x00FF;
$comment = null; $comment = null;
if ($fileCommentLength > 0) { if ($unpack['fileCommentLength'] > 0) {
$comment = fread($stream, $fileCommentLength); $comment = fread($stream, $unpack['fileCommentLength']);
} }
// decode code page names // decode code page names
@@ -485,23 +484,24 @@ class ZipReader
$extractedOS, $extractedOS,
$softwareVersion, $softwareVersion,
$extractVersion, $extractVersion,
$compressionMethod, $unpack['compressionMethod'],
$generalPurposeBitFlags, $generalPurposeBitFlags,
$dosTime, $dosTime,
$crc, $unpack['crc'],
$compressedSize, $unpack['compressedSize'],
$uncompressedSize, $unpack['uncompressedSize'],
$internalFileAttributes, $unpack['internalFileAttributes'],
$externalFileAttributes, $unpack['externalFileAttributes'],
$offsetLocalHeader, $unpack['offsetLocalHeader'],
$comment, $comment,
$fallbackCharset $fallbackCharset
); );
if ($extraFieldLength > 0) { if ($unpack['extraFieldLength'] > 0) {
$this->parseExtraFields( $this->parseExtraFields(
fread($stream, $extraFieldLength), fread($stream, $unpack['extraFieldLength']),
$zipEntry $zipEntry,
false
); );
/** @var Zip64ExtraField|null $extraZip64 */ /** @var Zip64ExtraField|null $extraZip64 */
@@ -519,7 +519,14 @@ class ZipReader
return $zipEntry; return $zipEntry;
} }
protected function parseExtraFields(string $buffer, ZipEntry $zipEntry, bool $local = false): ExtraFieldsCollection /**
* @param string $buffer
* @param ZipEntry $zipEntry
* @param bool $local
*
* @return ExtraFieldsCollection
*/
protected function parseExtraFields($buffer, ZipEntry $zipEntry, $local = false)
{ {
$collection = $local $collection = $local
? $zipEntry->getLocalExtraFields() ? $zipEntry->getLocalExtraFields()
@@ -530,16 +537,15 @@ class ZipReader
$endPos = \strlen($buffer); $endPos = \strlen($buffer);
while ($endPos - $pos >= 4) { while ($endPos - $pos >= 4) {
[ /** @var int[] $data */
'headerId' => $headerId, $data = unpack('vheaderId/vdataSize', substr($buffer, $pos, 4));
'dataSize' => $dataSize,
] = unpack('vheaderId/vdataSize', substr($buffer, $pos, 4));
$pos += 4; $pos += 4;
if ($endPos - $pos - $dataSize < 0) { if ($endPos - $pos - $data['dataSize'] < 0) {
break; break;
} }
$bufferData = substr($buffer, $pos, $dataSize); $bufferData = substr($buffer, $pos, $data['dataSize']);
$headerId = $data['headerId'];
/** @var string|ZipExtraField|null $className */ /** @var string|ZipExtraField|null $className */
$className = ZipExtraDriver::getClassNameOrNull($headerId); $className = ZipExtraDriver::getClassNameOrNull($headerId);
@@ -548,8 +554,8 @@ class ZipReader
if ($className !== null) { if ($className !== null) {
try { try {
$extraField = $local $extraField = $local
? $className::unpackLocalFileData($bufferData, $zipEntry) ? \call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry)
: $className::unpackCentralDirData($bufferData, $zipEntry); : \call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
} catch (\Throwable $e) { } catch (\Throwable $e) {
// skip errors while parsing invalid data // skip errors while parsing invalid data
continue; continue;
@@ -559,7 +565,7 @@ class ZipReader
} }
$collection->add($extraField); $collection->add($extraField);
} finally { } finally {
$pos += $dataSize; $pos += $data['dataSize'];
} }
} }
} }
@@ -567,7 +573,11 @@ class ZipReader
return $collection; return $collection;
} }
protected function handleZip64Extra(Zip64ExtraField $extraZip64, ZipEntry $zipEntry): void /**
* @param Zip64ExtraField $extraZip64
* @param ZipEntry $zipEntry
*/
protected function handleZip64Extra(Zip64ExtraField $extraZip64, ZipEntry $zipEntry)
{ {
$uncompressedSize = $extraZip64->getUncompressedSize(); $uncompressedSize = $extraZip64->getUncompressedSize();
$compressedSize = $extraZip64->getCompressedSize(); $compressedSize = $extraZip64->getCompressedSize();
@@ -603,9 +613,11 @@ class ZipReader
* file name (variable size) * file name (variable size)
* extra field (variable size) * extra field (variable size)
* *
* @param ZipEntry $entry
*
* @throws ZipException * @throws ZipException
*/ */
protected function loadLocalExtraFields(ZipEntry $entry): void protected function loadLocalExtraFields(ZipEntry $entry)
{ {
$offsetLocalHeader = $entry->getLocalHeaderOffset(); $offsetLocalHeader = $entry->getLocalHeaderOffset();
@@ -616,16 +628,16 @@ class ZipReader
} }
fseek($this->inStream, $offsetLocalHeader + ZipConstants::LFH_FILENAME_LENGTH_POS); fseek($this->inStream, $offsetLocalHeader + ZipConstants::LFH_FILENAME_LENGTH_POS);
[ $unpack = unpack('vfileNameLength/vextraFieldLength', fread($this->inStream, 4));
'fileNameLength' => $fileNameLength, $offsetData = ftell($this->inStream)
'extraFieldLength' => $extraFieldLength, + $unpack['fileNameLength']
] = unpack('vfileNameLength/vextraFieldLength', fread($this->inStream, 4)); + $unpack['extraFieldLength'];
$offsetData = ftell($this->inStream) + $fileNameLength + $extraFieldLength;
fseek($this->inStream, $fileNameLength, \SEEK_CUR);
if ($extraFieldLength > 0) { fseek($this->inStream, $unpack['fileNameLength'], \SEEK_CUR);
if ($unpack['extraFieldLength'] > 0) {
$this->parseExtraFields( $this->parseExtraFields(
fread($this->inStream, $extraFieldLength), fread($this->inStream, $unpack['extraFieldLength']),
$entry, $entry,
true true
); );
@@ -636,9 +648,11 @@ class ZipReader
} }
/** /**
* @param ZipEntry $zipEntry
*
* @throws ZipException * @throws ZipException
*/ */
private function handleExtraEncryptionFields(ZipEntry $zipEntry): void private function handleExtraEncryptionFields(ZipEntry $zipEntry)
{ {
if ($zipEntry->isEncrypted()) { if ($zipEntry->isEncrypted()) {
if ($zipEntry->getCompressionMethod() === ZipCompressionMethod::WINZIP_AES) { if ($zipEntry->getCompressionMethod() === ZipCompressionMethod::WINZIP_AES) {
@@ -667,12 +681,16 @@ class ZipReader
* *
* This is a special method in which you can process ExtraField * This is a special method in which you can process ExtraField
* and make changes to ZipEntry. * and make changes to ZipEntry.
*
* @param ZipEntry $zipEntry
*/ */
protected function handleExtraFields(ZipEntry $zipEntry): void protected function handleExtraFields(ZipEntry $zipEntry)
{ {
} }
/** /**
* @param ZipSourceFileData $zipFileData
*
* @throws ZipException * @throws ZipException
* @throws Crc32Exception * @throws Crc32Exception
* *
@@ -688,12 +706,13 @@ class ZipReader
} }
/** /**
* @param resource $outStream * @param ZipSourceFileData $zipFileData
* @param resource $outStream
* *
* @throws Crc32Exception * @throws Crc32Exception
* @throws ZipException * @throws ZipException
*/ */
public function copyUncompressedDataToStream(ZipSourceFileData $zipFileData, $outStream): void public function copyUncompressedDataToStream(ZipSourceFileData $zipFileData, $outStream)
{ {
if (!\is_resource($outStream)) { if (!\is_resource($outStream)) {
throw new InvalidArgumentException('outStream is not resource'); throw new InvalidArgumentException('outStream is not resource');
@@ -851,9 +870,10 @@ class ZipReader
} }
/** /**
* @param resource $outStream * @param ZipSourceFileData $zipData
* @param resource $outStream
*/ */
public function copyCompressedDataToStream(ZipSourceFileData $zipData, $outStream): void public function copyCompressedDataToStream(ZipSourceFileData $zipData, $outStream)
{ {
if ($zipData->getCompressedSize() > 0) { if ($zipData->getCompressedSize() > 0) {
fseek($this->inStream, $zipData->getOffset()); fseek($this->inStream, $zipData->getOffset());
@@ -861,23 +881,18 @@ class ZipReader
} }
} }
protected function isZip64Support(): bool /**
* @return bool
*/
protected function isZip64Support()
{ {
return \PHP_INT_SIZE === 8; // true for 64bit system return \PHP_INT_SIZE === 8; // true for 64bit system
} }
/** public function close()
* @psalm-suppress InvalidPropertyAssignmentValue
*/
public function close(): void
{ {
if (\is_resource($this->inStream)) { if (\is_resource($this->inStream)) {
fclose($this->inStream); fclose($this->inStream);
} }
} }
public function __destruct()
{
$this->close();
}
} }

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Data; namespace PhpZip\Model\Data;
use PhpZip\Model\ZipData; use PhpZip\Model\ZipData;
@@ -26,16 +17,19 @@ class ZipNewData implements ZipData
* *
* @var array<int, int> array of resource ids and the number of class clones * @var array<int, int> array of resource ids and the number of class clones
*/ */
private static array $guardClonedStream = []; private static $guardClonedStream = [];
private ZipEntry $zipEntry; /** @var ZipEntry */
private $zipEntry;
/** @var resource */ /** @var resource */
private $stream; private $stream;
/** /**
* @param string|resource $data Raw string data or resource * ZipStringData constructor.
* @noinspection PhpMissingParamTypeInspection *
* @param ZipEntry $zipEntry
* @param string|resource $data
*/ */
public function __construct(ZipEntry $zipEntry, $data) public function __construct(ZipEntry $zipEntry, $data)
{ {
@@ -63,57 +57,6 @@ class ZipNewData implements ZipData
: 0; : 0;
} }
/**
* @return resource returns stream data
*/
public function getDataAsStream()
{
if (!\is_resource($this->stream)) {
throw new \LogicException(sprintf('Resource has been closed (entry=%s).', $this->zipEntry->getName()));
}
return $this->stream;
}
/**
* @return string returns data as string
*/
public function getDataAsString(): string
{
$stream = $this->getDataAsStream();
$pos = ftell($stream);
try {
rewind($stream);
return stream_get_contents($stream);
} finally {
fseek($stream, $pos);
}
}
/**
* @param resource $outStream
*/
public function copyDataToStream($outStream): void
{
$stream = $this->getDataAsStream();
rewind($stream);
stream_copy_to_stream($stream, $outStream);
}
/**
* @see https://php.net/manual/en/language.oop5.cloning.php
*/
public function __clone()
{
$resourceId = (int) $this->stream;
self::$guardClonedStream[$resourceId]
= isset(self::$guardClonedStream[$resourceId])
? self::$guardClonedStream[$resourceId] + 1
: 1;
}
/** /**
* The stream will be closed when closing the zip archive. * The stream will be closed when closing the zip archive.
* *
@@ -135,4 +78,55 @@ class ZipNewData implements ZipData
fclose($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;
}
/**
* @return resource returns stream data
*/
public function getDataAsStream()
{
if (!\is_resource($this->stream)) {
throw new \LogicException(sprintf('Resource has been closed (entry=%s).', $this->zipEntry->getName()));
}
return $this->stream;
}
/**
* @return string returns data as string
*/
public function getDataAsString()
{
$stream = $this->getDataAsStream();
$pos = ftell($stream);
try {
rewind($stream);
return stream_get_contents($stream);
} finally {
fseek($stream, $pos);
}
}
/**
* @param resource $outStream
*/
public function copyDataToStream($outStream)
{
$stream = $this->getDataAsStream();
rewind($stream);
stream_copy_to_stream($stream, $outStream);
}
} }

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra; namespace PhpZip\Model\Extra;
/** /**
@@ -25,230 +16,12 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
* *
* @var ZipExtraField[] * @var ZipExtraField[]
*/ */
protected array $collection = []; protected $collection = [];
/** /**
* Returns the number of Extra Fields in this collection. * @return string
*/ */
public function count(): int public function __toString()
{
return \count($this->collection);
}
/**
* Returns the Extra Field with the given Header ID or null
* if no such Extra Field exists.
*
* @param int $headerId the requested Header ID
*
* @return ZipExtraField|null the Extra Field with the given Header ID or
* if no such Extra Field exists
*/
public function get(int $headerId): ?ZipExtraField
{
$this->validateHeaderId($headerId);
return $this->collection[$headerId] ?? null;
}
private function validateHeaderId(int $headerId): void
{
if ($headerId < 0 || $headerId > 0xFFFF) {
throw new \InvalidArgumentException('$headerId out of range');
}
}
/**
* Stores the given Extra Field in this collection.
*
* @param ZipExtraField $extraField the Extra Field to store in this collection
*
* @return ZipExtraField the Extra Field previously associated with the Header ID of
* of the given Extra Field or null if no such Extra Field existed
*/
public function add(ZipExtraField $extraField): ZipExtraField
{
$headerId = $extraField->getHeaderId();
$this->validateHeaderId($headerId);
$this->collection[$headerId] = $extraField;
return $extraField;
}
/**
* @param ZipExtraField[] $extraFields
*/
public function addAll(array $extraFields): void
{
foreach ($extraFields as $extraField) {
$this->add($extraField);
}
}
/**
* @param ExtraFieldsCollection $collection
*/
public function addCollection(self $collection): void
{
$this->addAll($collection->collection);
}
/**
* @return ZipExtraField[]
*/
public function getAll(): array
{
return $this->collection;
}
/**
* Returns Extra Field exists.
*
* @param int $headerId the requested Header ID
*/
public function has(int $headerId): bool
{
return isset($this->collection[$headerId]);
}
/**
* Removes the Extra Field with the given Header ID.
*
* @param int $headerId the requested Header ID
*
* @return ZipExtraField|null the Extra Field with the given Header ID or null
* if no such Extra Field exists
*/
public function remove(int $headerId): ?ZipExtraField
{
$this->validateHeaderId($headerId);
if (isset($this->collection[$headerId])) {
$ef = $this->collection[$headerId];
unset($this->collection[$headerId]);
return $ef;
}
return null;
}
/**
* Whether a offset exists.
*
* @see http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset an offset to check for
*
* @return bool true on success or false on failure
*/
public function offsetExists($offset): bool
{
return isset($this->collection[(int) $offset]);
}
/**
* Offset to retrieve.
*
* @see http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param mixed $offset the offset to retrieve
*/
public function offsetGet($offset): ?ZipExtraField
{
return $this->collection[(int) $offset] ?? null;
}
/**
* Offset to set.
*
* @see http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset the offset to assign the value to
* @param mixed $value the value to set
*/
public function offsetSet($offset, $value): void
{
if (!$value instanceof ZipExtraField) {
throw new \InvalidArgumentException('value is not instanceof ' . ZipExtraField::class);
}
$this->add($value);
}
/**
* Offset to unset.
*
* @see http://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset the offset to unset
*/
public function offsetUnset($offset): void
{
$this->remove($offset);
}
/**
* Return the current element.
*
* @see http://php.net/manual/en/iterator.current.php
*/
public function current(): ZipExtraField
{
return current($this->collection);
}
/**
* Move forward to next element.
*
* @see http://php.net/manual/en/iterator.next.php
*/
public function next(): void
{
next($this->collection);
}
/**
* Return the key of the current element.
*
* @see http://php.net/manual/en/iterator.key.php
*
* @return int scalar on success, or null on failure
*/
public function key(): int
{
return key($this->collection);
}
/**
* Checks if current position is valid.
*
* @see http://php.net/manual/en/iterator.valid.php
*
* @return bool The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid(): bool
{
return key($this->collection) !== null;
}
/**
* Rewind the Iterator to the first element.
*
* @see http://php.net/manual/en/iterator.rewind.php
*/
public function rewind(): void
{
reset($this->collection);
}
public function clear(): void
{
$this->collection = [];
}
public function __toString(): string
{ {
$formats = []; $formats = [];
@@ -268,4 +41,234 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
$this->collection[$k] = clone $v; $this->collection[$k] = clone $v;
} }
} }
/**
* Returns the number of Extra Fields in this collection.
*
* @return int
*/
public function count()
{
return \count($this->collection);
}
/**
* Returns the Extra Field with the given Header ID or null
* if no such Extra Field exists.
*
* @param int $headerId the requested Header ID
*
* @return ZipExtraField|null the Extra Field with the given Header ID or
* if no such Extra Field exists
*/
public function get($headerId)
{
$this->validateHeaderId($headerId);
return $this->collection[$headerId] ?? null;
}
/**
* @param int $headerId
*/
private function validateHeaderId($headerId)
{
if ($headerId < 0 || $headerId > 0xffff) {
throw new \InvalidArgumentException('$headerId out of range');
}
}
/**
* Stores the given Extra Field in this collection.
*
* @param ZipExtraField $extraField the Extra Field to store in this collection
*
* @return ZipExtraField the Extra Field previously associated with the Header ID of
* of the given Extra Field or null if no such Extra Field existed
*/
public function add(ZipExtraField $extraField)
{
$headerId = $extraField->getHeaderId();
$this->validateHeaderId($headerId);
$this->collection[$headerId] = $extraField;
return $extraField;
}
/**
* @param ZipExtraField[] $extraFields
*/
public function addAll(array $extraFields)
{
foreach ($extraFields as $extraField) {
$this->add($extraField);
}
}
/**
* @param ExtraFieldsCollection $collection
*/
public function addCollection(self $collection)
{
$this->addAll($collection->collection);
}
/**
* @return ZipExtraField[]
*/
public function getAll()
{
return $this->collection;
}
/**
* Returns Extra Field exists.
*
* @param int $headerId the requested Header ID
*
* @return bool
*/
public function has($headerId)
{
return isset($this->collection[$headerId]);
}
/**
* Removes the Extra Field with the given Header ID.
*
* @param int $headerId the requested Header ID
*
* @return ZipExtraField|null the Extra Field with the given Header ID or null
* if no such Extra Field exists
*/
public function remove($headerId)
{
$this->validateHeaderId($headerId);
if (isset($this->collection[$headerId])) {
$ef = $this->collection[$headerId];
unset($this->collection[$headerId]);
return $ef;
}
}
/**
* Whether a offset exists.
*
* @see http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param int $offset an offset to check for
*
* @return bool true on success or false on failure
*/
public function offsetExists($offset)
{
return isset($this->collection[(int) $offset]);
}
/**
* Offset to retrieve.
*
* @see http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param int $offset the offset to retrieve
*
* @return ZipExtraField|null
*/
public function offsetGet($offset)
{
return $this->collection[$offset] ?? null;
}
/**
* Offset to set.
*
* @see http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset the offset to assign the value to
* @param ZipExtraField $value the value to set
*/
public function offsetSet($offset, $value)
{
if (!$value instanceof ZipExtraField) {
throw new \InvalidArgumentException('value is not instanceof ' . ZipExtraField::class);
}
$this->add($value);
}
/**
* Offset to unset.
*
* @see http://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset the offset to unset
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}
/**
* Return the current element.
*
* @see http://php.net/manual/en/iterator.current.php
*
* @return ZipExtraField
*/
public function current()
{
return current($this->collection);
}
/**
* Move forward to next element.
*
* @see http://php.net/manual/en/iterator.next.php
*/
public function next()
{
next($this->collection);
}
/**
* Return the key of the current element.
*
* @see http://php.net/manual/en/iterator.key.php
*
* @return int scalar on success, or null on failure
*/
public function key()
{
return key($this->collection);
}
/**
* Checks if current position is valid.
*
* @see http://php.net/manual/en/iterator.valid.php
*
* @return bool The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
*/
public function valid()
{
return key($this->collection) !== null;
}
/**
* Rewind the Iterator to the first element.
*
* @see http://php.net/manual/en/iterator.rewind.php
*/
public function rewind()
{
reset($this->collection);
}
public function clear()
{
$this->collection = [];
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,36 +1,30 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields; namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\InvalidArgumentException; use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\ZipException; use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\ZipExtraField; use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry; use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/** /**
* NTFS Extra Field. * NTFS Extra Field.
* *
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification * @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*
* @license MIT
*/ */
final class NtfsExtraField implements ZipExtraField class NtfsExtraField implements ZipExtraField
{ {
/** @var int Header id */ /** @var int Header id */
public const HEADER_ID = 0x000A; const HEADER_ID = 0x000a;
/** @var int Tag ID */ /** @var int Tag ID */
public const TIME_ATTR_TAG = 0x0001; const TIME_ATTR_TAG = 0x0001;
/** @var int Attribute size */ /** @var int Attribute size */
public const TIME_ATTR_SIZE = 24; // 3 * 8 const TIME_ATTR_SIZE = 24; // 3 * 8
/** /**
* @var int A file time is a 64-bit value that represents the number of * @var int A file time is a 64-bit value that represents the number of
@@ -38,231 +32,33 @@ final class NtfsExtraField implements ZipExtraField
* A.M. January 1, 1601 Coordinated Universal Time (UTC). * A.M. January 1, 1601 Coordinated Universal Time (UTC).
* this is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals. * this is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
*/ */
public const EPOCH_OFFSET = -116444736000000000; const EPOCH_OFFSET = -116444736000000000;
/** @var int Modify ntfs time */ /** @var int Modify ntfs time */
private int $modifyNtfsTime; private $modifyNtfsTime;
/** @var int Access ntfs time */ /** @var int Access ntfs time */
private int $accessNtfsTime; private $accessNtfsTime;
/** @var int Create ntfs time */ /** @var int Create ntfs time */
private int $createNtfsTime; private $createNtfsTime;
public function __construct(int $modifyNtfsTime, int $accessNtfsTime, int $createNtfsTime) /**
* @param int $modifyNtfsTime
* @param int $accessNtfsTime
* @param int $createNtfsTime
*/
public function __construct($modifyNtfsTime, $accessNtfsTime, $createNtfsTime)
{ {
$this->modifyNtfsTime = $modifyNtfsTime; $this->modifyNtfsTime = (int) $modifyNtfsTime;
$this->accessNtfsTime = $accessNtfsTime; $this->accessNtfsTime = (int) $accessNtfsTime;
$this->createNtfsTime = $createNtfsTime; $this->createNtfsTime = (int) $createNtfsTime;
} }
/** /**
* @return NtfsExtraField * @return string
*/ */
public static function create( public function __toString()
\DateTimeInterface $modifyDateTime,
\DateTimeInterface $accessDateTime,
\DateTimeInterface $createNtfsTime
): self {
return new self(
self::dateTimeToNtfsTime($modifyDateTime),
self::dateTimeToNtfsTime($accessDateTime),
self::dateTimeToNtfsTime($createNtfsTime)
);
}
/**
* 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.
*/
public function getHeaderId(): int
{
return self::HEADER_ID;
}
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
if (\PHP_INT_SIZE === 4) {
throw new ZipException('not supported for php-32bit');
}
$buffer = substr($buffer, 4);
$modifyTime = 0;
$accessTime = 0;
$createTime = 0;
while ($buffer || $buffer !== '') {
[
'tag' => $tag,
'sizeAttr' => $sizeAttr,
] = unpack('vtag/vsizeAttr', $buffer);
if ($tag === self::TIME_ATTR_TAG && $sizeAttr === self::TIME_ATTR_SIZE) {
[
'modifyTime' => $modifyTime,
'accessTime' => $accessTime,
'createTime' => $createTime,
] = unpack('PmodifyTime/PaccessTime/PcreateTime', substr($buffer, 4, 24));
break;
}
$buffer = substr($buffer, 4 + $sizeAttr);
}
return new self($modifyTime, $accessTime, $createTime);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
return self::unpackLocalFileData($buffer, $entry);
}
/**
* The actual data to put into local file data - without Header-ID
* or length specifier.
*
* @return string the data
*/
public function packLocalFileData(): string
{
return pack(
'VvvPPP',
0,
self::TIME_ATTR_TAG,
self::TIME_ATTR_SIZE,
$this->modifyNtfsTime,
$this->accessNtfsTime,
$this->createNtfsTime
);
}
public function getModifyNtfsTime(): int
{
return $this->modifyNtfsTime;
}
public function setModifyNtfsTime(int $modifyNtfsTime): void
{
$this->modifyNtfsTime = $modifyNtfsTime;
}
public function getAccessNtfsTime(): int
{
return $this->accessNtfsTime;
}
public function setAccessNtfsTime(int $accessNtfsTime): void
{
$this->accessNtfsTime = $accessNtfsTime;
}
public function getCreateNtfsTime(): int
{
return $this->createNtfsTime;
}
public function setCreateNtfsTime(int $createNtfsTime): void
{
$this->createNtfsTime = $createNtfsTime;
}
/**
* The actual data to put into central directory - without Header-ID or
* length specifier.
*
* @return string the data
*/
public function packCentralDirData(): string
{
return $this->packLocalFileData();
}
public function getModifyDateTime(): \DateTimeInterface
{
return self::ntfsTimeToDateTime($this->modifyNtfsTime);
}
public function setModifyDateTime(\DateTimeInterface $modifyTime): void
{
$this->modifyNtfsTime = self::dateTimeToNtfsTime($modifyTime);
}
public function getAccessDateTime(): \DateTimeInterface
{
return self::ntfsTimeToDateTime($this->accessNtfsTime);
}
public function setAccessDateTime(\DateTimeInterface $accessTime): void
{
$this->accessNtfsTime = self::dateTimeToNtfsTime($accessTime);
}
public function getCreateDateTime(): \DateTimeInterface
{
return self::ntfsTimeToDateTime($this->createNtfsTime);
}
public function setCreateDateTime(\DateTimeInterface $createTime): void
{
$this->createNtfsTime = self::dateTimeToNtfsTime($createTime);
}
/**
* @param float $timestamp Float timestamp
*/
public static function timestampToNtfsTime(float $timestamp): int
{
return (int) (($timestamp * 10000000) - self::EPOCH_OFFSET);
}
public static function dateTimeToNtfsTime(\DateTimeInterface $dateTime): int
{
return self::timestampToNtfsTime((float) $dateTime->format('U.u'));
}
/**
* @return float Float unix timestamp
*/
public static function ntfsTimeToTimestamp(int $ntfsTime): float
{
return (float) (($ntfsTime + self::EPOCH_OFFSET) / 10000000);
}
public static function ntfsTimeToDateTime(int $ntfsTime): \DateTimeInterface
{
$timestamp = self::ntfsTimeToTimestamp($ntfsTime);
$dateTime = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6f', $timestamp));
if ($dateTime === false) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp);
}
return $dateTime;
}
public function __toString(): string
{ {
$args = [self::HEADER_ID]; $args = [self::HEADER_ID];
$format = '0x%04x NtfsExtra:'; $format = '0x%04x NtfsExtra:';
@@ -284,4 +80,260 @@ final class NtfsExtraField implements ZipExtraField
return vsprintf($format, $args); return vsprintf($format, $args);
} }
/**
* @param \DateTimeInterface $modifyDateTime
* @param \DateTimeInterface $accessDateTime
* @param \DateTimeInterface $createNtfsTime
*
* @return NtfsExtraField
*/
public static function create(
\DateTimeInterface $modifyDateTime,
\DateTimeInterface $accessDateTime,
\DateTimeInterface $createNtfsTime
) {
return new self(
self::dateTimeToNtfsTime($modifyDateTime),
self::dateTimeToNtfsTime($accessDateTime),
self::dateTimeToNtfsTime($createNtfsTime)
);
}
/**
* 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;
}
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
if (\PHP_INT_SIZE === 4) {
throw new ZipException('not supported for php-32bit');
}
$buffer = substr($buffer, 4);
$modifyTime = 0;
$accessTime = 0;
$createTime = 0;
while ($buffer || $buffer !== '') {
$unpack = unpack('vtag/vsizeAttr', $buffer);
if ($unpack['tag'] === self::TIME_ATTR_TAG && $unpack['sizeAttr'] === self::TIME_ATTR_SIZE) {
// refactoring will be needed when php 5.5 support ends
$modifyTime = PackUtil::unpackLongLE(substr($buffer, 4, 8));
$accessTime = PackUtil::unpackLongLE(substr($buffer, 12, 8));
$createTime = PackUtil::unpackLongLE(substr($buffer, 20, 8));
break;
}
$buffer = substr($buffer, 4 + $unpack['sizeAttr']);
}
return new self($modifyTime, $accessTime, $createTime);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
{
return self::unpackLocalFileData($buffer, $entry);
}
/**
* The actual data to put into local file data - without Header-ID
* or length specifier.
*
* @return string the data
*/
public function packLocalFileData()
{
$data = pack('Vvv', 0, self::TIME_ATTR_TAG, self::TIME_ATTR_SIZE);
// refactoring will be needed when php 5.5 support ends
$data .= PackUtil::packLongLE($this->modifyNtfsTime);
$data .= PackUtil::packLongLE($this->accessNtfsTime);
$data .= PackUtil::packLongLE($this->createNtfsTime);
return $data;
}
/**
* @return int
*/
public function getModifyNtfsTime()
{
return $this->modifyNtfsTime;
}
/**
* @param int $modifyNtfsTime
*/
public function setModifyNtfsTime($modifyNtfsTime)
{
$this->modifyNtfsTime = (int) $modifyNtfsTime;
}
/**
* @return int
*/
public function getAccessNtfsTime()
{
return $this->accessNtfsTime;
}
/**
* @param int $accessNtfsTime
*/
public function setAccessNtfsTime($accessNtfsTime)
{
$this->accessNtfsTime = (int) $accessNtfsTime;
}
/**
* @return int
*/
public function getCreateNtfsTime()
{
return $this->createNtfsTime;
}
/**
* @param int $createNtfsTime
*/
public function setCreateNtfsTime($createNtfsTime)
{
$this->createNtfsTime = (int) $createNtfsTime;
}
/**
* The actual data to put into central directory - without Header-ID or
* length specifier.
*
* @return string the data
*/
public function packCentralDirData()
{
return $this->packLocalFileData();
}
/**
* @return \DateTimeInterface
*/
public function getModifyDateTime()
{
return self::ntfsTimeToDateTime($this->modifyNtfsTime);
}
/**
* @param \DateTimeInterface $modifyTime
*/
public function setModifyDateTime(\DateTimeInterface $modifyTime)
{
$this->modifyNtfsTime = self::dateTimeToNtfsTime($modifyTime);
}
/**
* @return \DateTimeInterface
*/
public function getAccessDateTime()
{
return self::ntfsTimeToDateTime($this->accessNtfsTime);
}
/**
* @param \DateTimeInterface $accessTime
*/
public function setAccessDateTime(\DateTimeInterface $accessTime)
{
$this->accessNtfsTime = self::dateTimeToNtfsTime($accessTime);
}
/**
* @return \DateTimeInterface
*/
public function getCreateDateTime()
{
return self::ntfsTimeToDateTime($this->createNtfsTime);
}
/**
* @param \DateTimeInterface $createTime
*/
public function setCreateDateTime(\DateTimeInterface $createTime)
{
$this->createNtfsTime = self::dateTimeToNtfsTime($createTime);
}
/**
* @param float $timestamp Float timestamp
*
* @return int
*/
public static function timestampToNtfsTime($timestamp)
{
return (int) (((float) $timestamp * 10000000) - self::EPOCH_OFFSET);
}
/**
* @param \DateTimeInterface $dateTime
*
* @return int
*/
public static function dateTimeToNtfsTime(\DateTimeInterface $dateTime)
{
return self::timestampToNtfsTime((float) $dateTime->format('U.u'));
}
/**
* @param int $ntfsTime
*
* @return float Float unix timestamp
*/
public static function ntfsTimeToTimestamp($ntfsTime)
{
return (float) (($ntfsTime + self::EPOCH_OFFSET) / 10000000);
}
/**
* @param int $ntfsTime
*
* @return \DateTimeInterface
*/
public static function ntfsTimeToDateTime($ntfsTime)
{
$timestamp = self::ntfsTimeToTimestamp($ntfsTime);
$dateTime = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6f', $timestamp));
if ($dateTime === false) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp);
}
return $dateTime;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields; namespace PhpZip\Model\Extra\Fields;
use PhpZip\Constants\ZipConstants; use PhpZip\Constants\ZipConstants;
@@ -16,30 +7,43 @@ use PhpZip\Exception\RuntimeException;
use PhpZip\Exception\ZipException; use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\ZipExtraField; use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry; use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/** /**
* ZIP64 Extra Field. * ZIP64 Extra Field.
* *
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification * @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*/ */
final class Zip64ExtraField implements ZipExtraField class Zip64ExtraField implements ZipExtraField
{ {
/** @var int The Header ID for a ZIP64 Extended Information Extra Field. */ /** @var int The Header ID for a ZIP64 Extended Information Extra Field. */
public const HEADER_ID = 0x0001; const HEADER_ID = 0x0001;
private ?int $uncompressedSize; /** @var int|null */
private $uncompressedSize;
private ?int $compressedSize; /** @var int|null */
private $compressedSize;
private ?int $localHeaderOffset; /** @var int|null */
private $localHeaderOffset;
private ?int $diskStart; /** @var int|null */
private $diskStart;
/**
* Zip64ExtraField constructor.
*
* @param int|null $uncompressedSize
* @param int|null $compressedSize
* @param int|null $localHeaderOffset
* @param int|null $diskStart
*/
public function __construct( public function __construct(
?int $uncompressedSize = null, $uncompressedSize = null,
?int $compressedSize = null, $compressedSize = null,
?int $localHeaderOffset = null, $localHeaderOffset = null,
?int $diskStart = null $diskStart = null
) { ) {
$this->uncompressedSize = $uncompressedSize; $this->uncompressedSize = $uncompressedSize;
$this->compressedSize = $compressedSize; $this->compressedSize = $compressedSize;
@@ -48,204 +52,9 @@ final class Zip64ExtraField implements ZipExtraField
} }
/** /**
* Returns the Header ID (type) of this Extra Field. * @return string
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*/ */
public function getHeaderId(): int public function __toString()
{
return self::HEADER_ID;
}
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
*
* @throws ZipException on error
*
* @return Zip64ExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
{
$length = \strlen($buffer);
if ($length === 0) {
// no local file data at all, may happen if an archive
// only holds a ZIP64 extended information extra field
// inside the central directory but not inside the local
// file header
return new self();
}
if ($length < 16) {
throw new ZipException(
'Zip64 extended information must contain both size values in the local file header.'
);
}
[
'uncompressedSize' => $uncompressedSize,
'compressedSize' => $compressedSize,
] = unpack('PuncompressedSize/PcompressedSize', substr($buffer, 0, 16));
return new self($uncompressedSize, $compressedSize);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
*
* @throws ZipException
*
* @return Zip64ExtraField
*/
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
{
if ($entry === null) {
throw new RuntimeException('zipEntry is null');
}
$length = \strlen($buffer);
$remaining = $length;
$uncompressedSize = null;
$compressedSize = null;
$localHeaderOffset = null;
$diskStart = null;
if ($entry->getUncompressedSize() === ZipConstants::ZIP64_MAGIC) {
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no uncompressed size).');
}
$uncompressedSize = unpack('P', substr($buffer, $length - $remaining, 8))[1];
$remaining -= 8;
}
if ($entry->getCompressedSize() === ZipConstants::ZIP64_MAGIC) {
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no compressed size).');
}
$compressedSize = unpack('P', substr($buffer, $length - $remaining, 8))[1];
$remaining -= 8;
}
if ($entry->getLocalHeaderOffset() === ZipConstants::ZIP64_MAGIC) {
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no relative local header offset).');
}
$localHeaderOffset = unpack('P', substr($buffer, $length - $remaining, 8))[1];
$remaining -= 8;
}
if ($remaining === 4) {
$diskStart = unpack('V', substr($buffer, $length - $remaining, 4))[1];
}
return new self($uncompressedSize, $compressedSize, $localHeaderOffset, $diskStart);
}
/**
* The actual data to put into local file data - without Header-ID
* or length specifier.
*
* @return string the data
*/
public function packLocalFileData(): string
{
if ($this->uncompressedSize !== null || $this->compressedSize !== null) {
if ($this->uncompressedSize === null || $this->compressedSize === null) {
throw new \InvalidArgumentException(
'Zip64 extended information must contain both size values in the local file header.'
);
}
return $this->packSizes();
}
return '';
}
private function packSizes(): string
{
$data = '';
if ($this->uncompressedSize !== null) {
$data .= pack('P', $this->uncompressedSize);
}
if ($this->compressedSize !== null) {
$data .= pack('P', $this->compressedSize);
}
return $data;
}
/**
* The actual data to put into central directory - without Header-ID or
* length specifier.
*
* @return string the data
*/
public function packCentralDirData(): string
{
$data = $this->packSizes();
if ($this->localHeaderOffset !== null) {
$data .= pack('P', $this->localHeaderOffset);
}
if ($this->diskStart !== null) {
$data .= pack('V', $this->diskStart);
}
return $data;
}
public function getUncompressedSize(): ?int
{
return $this->uncompressedSize;
}
public function setUncompressedSize(?int $uncompressedSize): void
{
$this->uncompressedSize = $uncompressedSize;
}
public function getCompressedSize(): ?int
{
return $this->compressedSize;
}
public function setCompressedSize(?int $compressedSize): void
{
$this->compressedSize = $compressedSize;
}
public function getLocalHeaderOffset(): ?int
{
return $this->localHeaderOffset;
}
public function setLocalHeaderOffset(?int $localHeaderOffset): void
{
$this->localHeaderOffset = $localHeaderOffset;
}
public function getDiskStart(): ?int
{
return $this->diskStart;
}
public function setDiskStart(?int $diskStart): void
{
$this->diskStart = $diskStart;
}
public function __toString(): string
{ {
$args = [self::HEADER_ID]; $args = [self::HEADER_ID];
$format = '0x%04x ZIP64: '; $format = '0x%04x ZIP64: ';
@@ -274,4 +83,229 @@ final class Zip64ExtraField implements ZipExtraField
return vsprintf($format, $args); return vsprintf($format, $args);
} }
/**
* 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;
}
/**
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException on error
*
* @return Zip64ExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
$length = \strlen($buffer);
if ($length === 0) {
// no local file data at all, may happen if an archive
// only holds a ZIP64 extended information extra field
// inside the central directory but not inside the local
// file header
return new self();
}
if ($length < 16) {
throw new ZipException(
'Zip64 extended information must contain both size values in the local file header.'
);
}
$uncompressedSize = PackUtil::unpackLongLE(substr($buffer, 0, 8));
$compressedSize = PackUtil::unpackLongLE(substr($buffer, 8, 8));
return new self($uncompressedSize, $compressedSize);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @return Zip64ExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
{
if ($entry === null) {
throw new RuntimeException('zipEntry is null');
}
$length = \strlen($buffer);
$remaining = $length;
$uncompressedSize = null;
$compressedSize = null;
$localHeaderOffset = null;
$diskStart = null;
if ($entry->getUncompressedSize() === ZipConstants::ZIP64_MAGIC) {
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no uncompressed size).');
}
$uncompressedSize = PackUtil::unpackLongLE(substr($buffer, $length - $remaining, 8));
$remaining -= 8;
}
if ($entry->getCompressedSize() === ZipConstants::ZIP64_MAGIC) {
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no compressed size).');
}
$compressedSize = PackUtil::unpackLongLE(substr($buffer, $length - $remaining, 8));
$remaining -= 8;
}
if ($entry->getLocalHeaderOffset() === ZipConstants::ZIP64_MAGIC) {
if ($remaining < 8) {
throw new ZipException('ZIP64 extension corrupt (no relative local header offset).');
}
$localHeaderOffset = PackUtil::unpackLongLE(substr($buffer, $length - $remaining, 8));
$remaining -= 8;
}
if ($remaining === 4) {
$diskStart = unpack('V', substr($buffer, $length - $remaining, 4))[1];
}
return new self($uncompressedSize, $compressedSize, $localHeaderOffset, $diskStart);
}
/**
* The actual data to put into local file data - without Header-ID
* or length specifier.
*
* @return string the data
*/
public function packLocalFileData()
{
if ($this->uncompressedSize !== null || $this->compressedSize !== null) {
if ($this->uncompressedSize === null || $this->compressedSize === null) {
throw new \InvalidArgumentException(
'Zip64 extended information must contain both size values in the local file header.'
);
}
return $this->packSizes();
}
return '';
}
/**
* @return string
*/
private function packSizes()
{
$data = '';
if ($this->uncompressedSize !== null) {
$data .= PackUtil::packLongLE($this->uncompressedSize);
}
if ($this->compressedSize !== null) {
$data .= PackUtil::packLongLE($this->compressedSize);
}
return $data;
}
/**
* The actual data to put into central directory - without Header-ID or
* length specifier.
*
* @return string the data
*/
public function packCentralDirData()
{
$data = $this->packSizes();
if ($this->localHeaderOffset !== null) {
$data .= PackUtil::packLongLE($this->localHeaderOffset);
}
if ($this->diskStart !== null) {
$data .= pack('V', $this->diskStart);
}
return $data;
}
/**
* @return int|null
*/
public function getUncompressedSize()
{
return $this->uncompressedSize;
}
/**
* @param int|null $uncompressedSize
*/
public function setUncompressedSize($uncompressedSize)
{
$this->uncompressedSize = $uncompressedSize;
}
/**
* @return int|null
*/
public function getCompressedSize()
{
return $this->compressedSize;
}
/**
* @param int|null $compressedSize
*/
public function setCompressedSize($compressedSize)
{
$this->compressedSize = $compressedSize;
}
/**
* @return int|null
*/
public function getLocalHeaderOffset()
{
return $this->localHeaderOffset;
}
/**
* @param int|null $localHeaderOffset
*/
public function setLocalHeaderOffset($localHeaderOffset)
{
$this->localHeaderOffset = $localHeaderOffset;
}
/**
* @return int|null
*/
public function getDiskStart()
{
return $this->diskStart;
}
/**
* @param int|null $diskStart
*/
public function setDiskStart($diskStart)
{
$this->diskStart = $diskStart;
}
} }

View File

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

View File

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

View File

@@ -1,60 +1,30 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model; namespace PhpZip\Model;
/**
* Class ImmutableZipContainer.
*/
class ImmutableZipContainer implements \Countable class ImmutableZipContainer implements \Countable
{ {
/** @var ZipEntry[] */ /** @var ZipEntry[] */
protected array $entries; protected $entries;
/** @var string|null Archive comment */ /** @var string|null Archive comment */
protected ?string $archiveComment; protected $archiveComment;
/** /**
* @param ZipEntry[] $entries * ZipContainer constructor.
* @param ?string $archiveComment *
* @param ZipEntry[] $entries
* @param string|null $archiveComment
*/ */
public function __construct(array $entries, ?string $archiveComment = null) public function __construct(array $entries, $archiveComment)
{ {
$this->entries = $entries; $this->entries = $entries;
$this->archiveComment = $archiveComment; $this->archiveComment = $archiveComment;
} }
/**
* @return ZipEntry[]
*/
public function &getEntries(): array
{
return $this->entries;
}
public function getArchiveComment(): ?string
{
return $this->archiveComment;
}
/**
* Count elements of an object.
*
* @see https://php.net/manual/en/countable.count.php
*
* @return int The custom count as an integer.
* The return value is cast to an integer.
*/
public function count(): int
{
return \count($this->entries);
}
/** /**
* When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. * 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. * Any properties that are references to other variables, will remain references.
@@ -70,4 +40,33 @@ class ImmutableZipContainer implements \Countable
$this->entries[$key] = clone $value; $this->entries[$key] = clone $value;
} }
} }
/**
* @return ZipEntry[]
*/
public function &getEntries()
{
return $this->entries;
}
/**
* @return string|null
*/
public function getArchiveComment()
{
return $this->archiveComment;
}
/**
* Count elements of an object.
*
* @see https://php.net/manual/en/countable.count.php
*
* @return int The custom count as an integer.
* The return value is cast to an integer.
*/
public function count()
{
return \count($this->entries);
}
} }

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

266
src/Model/ZipInfo.php Normal file
View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util; namespace PhpZip\Util;
use PhpZip\Util\Iterator\IgnoreFilesFilterIterator; use PhpZip\Util\Iterator\IgnoreFilesFilterIterator;
@@ -17,6 +8,9 @@ use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
/** /**
* Files util. * Files util.
* *
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @internal * @internal
*/ */
final class FilesUtil final class FilesUtil
@@ -25,8 +19,10 @@ final class FilesUtil
* Is empty directory. * Is empty directory.
* *
* @param string $dir Directory * @param string $dir Directory
*
* @return bool
*/ */
public static function isEmptyDir(string $dir): bool public static function isEmptyDir($dir)
{ {
if (!is_readable($dir)) { if (!is_readable($dir)) {
return false; return false;
@@ -40,7 +36,7 @@ final class FilesUtil
* *
* @param string $dir directory path * @param string $dir directory path
*/ */
public static function removeDir(string $dir): void public static function removeDir($dir)
{ {
$files = new \RecursiveIteratorIterator( $files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
@@ -57,8 +53,12 @@ final class FilesUtil
/** /**
* Convert glob pattern to regex pattern. * Convert glob pattern to regex pattern.
*
* @param string $globPattern
*
* @return string
*/ */
public static function convertGlobToRegEx(string $globPattern): string public static function convertGlobToRegEx($globPattern)
{ {
// Remove beginning and ending * globs because they're useless // Remove beginning and ending * globs because they're useless
$globPattern = trim($globPattern, '*'); $globPattern = trim($globPattern, '*');
@@ -72,11 +72,13 @@ final class FilesUtil
case '*': case '*':
$regexPattern .= ($escaping ? '\\*' : '.*'); $regexPattern .= ($escaping ? '\\*' : '.*');
$escaping = false; $escaping = false;
break; break;
case '?': case '?':
$regexPattern .= ($escaping ? '\\?' : '.'); $regexPattern .= ($escaping ? '\\?' : '.');
$escaping = false; $escaping = false;
break; break;
case '.': case '.':
@@ -90,6 +92,7 @@ final class FilesUtil
case '%': case '%':
$regexPattern .= '\\' . $currentChar; $regexPattern .= '\\' . $currentChar;
$escaping = false; $escaping = false;
break; break;
case '\\': case '\\':
@@ -99,6 +102,7 @@ final class FilesUtil
} else { } else {
$escaping = true; $escaping = true;
} }
break; break;
case '{': case '{':
@@ -109,6 +113,7 @@ final class FilesUtil
$inCurrent++; $inCurrent++;
} }
$escaping = false; $escaping = false;
break; break;
case '}': case '}':
@@ -121,6 +126,7 @@ final class FilesUtil
$regexPattern = '}'; $regexPattern = '}';
} }
$escaping = false; $escaping = false;
break; break;
case ',': case ',':
@@ -131,6 +137,7 @@ final class FilesUtil
} else { } else {
$regexPattern = ','; $regexPattern = ',';
} }
break; break;
default: default:
$escaping = false; $escaping = false;
@@ -144,9 +151,13 @@ final class FilesUtil
/** /**
* Search files. * Search files.
* *
* @param string $inputDir
* @param bool $recursive
* @param array $ignoreFiles
*
* @return array Searched file list * @return array Searched file list
*/ */
public static function fileSearchWithIgnore(string $inputDir, bool $recursive = true, array $ignoreFiles = []): array public static function fileSearchWithIgnore($inputDir, $recursive = true, array $ignoreFiles = [])
{ {
if ($recursive) { if ($recursive) {
$directoryIterator = new \RecursiveDirectoryIterator($inputDir); $directoryIterator = new \RecursiveDirectoryIterator($inputDir);
@@ -178,10 +189,16 @@ final class FilesUtil
/** /**
* Search files from glob pattern. * Search files from glob pattern.
* *
* @param string $globPattern
* @param int $flags
* @param bool $recursive
*
* @return array Searched file list * @return array Searched file list
*/ */
public static function globFileSearch(string $globPattern, int $flags = 0, bool $recursive = true): array public static function globFileSearch($globPattern, $flags = 0, $recursive = true)
{ {
$flags = (int) $flags;
$recursive = (bool) $recursive;
$files = glob($globPattern, $flags); $files = glob($globPattern, $flags);
if (!$recursive) { if (!$recursive) {
@@ -200,9 +217,13 @@ final class FilesUtil
/** /**
* Search files from regex pattern. * Search files from regex pattern.
* *
* @param string $folder
* @param string $pattern
* @param bool $recursive
*
* @return array Searched file list * @return array Searched file list
*/ */
public static function regexFileSearch(string $folder, string $pattern, bool $recursive = true): array public static function regexFileSearch($folder, $pattern, $recursive = true)
{ {
if ($recursive) { if ($recursive) {
$directoryIterator = new \RecursiveDirectoryIterator($folder); $directoryIterator = new \RecursiveDirectoryIterator($folder);
@@ -229,8 +250,10 @@ final class FilesUtil
* *
* @param int $size Size bytes * @param int $size Size bytes
* @param string|null $unit Unit support 'GB', 'MB', 'KB' * @param string|null $unit Unit support 'GB', 'MB', 'KB'
*
* @return string
*/ */
public static function humanSize(int $size, ?string $unit = null): string public static function humanSize($size, $unit = null)
{ {
if (($unit === null && $size >= 1 << 30) || $unit === 'GB') { if (($unit === null && $size >= 1 << 30) || $unit === 'GB') {
return number_format($size / (1 << 30), 2) . 'GB'; return number_format($size / (1 << 30), 2) . 'GB';
@@ -251,14 +274,18 @@ final class FilesUtil
* Normalizes zip path. * Normalizes zip path.
* *
* @param string $path Zip path * @param string $path Zip path
*
* @return string
*/ */
public static function normalizeZipPath(string $path): string public static function normalizeZipPath($path)
{ {
return implode( return implode(
\DIRECTORY_SEPARATOR, \DIRECTORY_SEPARATOR,
array_filter( array_filter(
explode('/', $path), explode('/', (string) $path),
static fn ($part) => $part !== '.' && $part !== '..' static function ($part) {
return $part !== '.' && $part !== '..';
}
) )
); );
} }
@@ -268,9 +295,11 @@ final class FilesUtil
* *
* @param string $file A file path * @param string $file A file path
* *
* @return bool
*
* @see source symfony filesystem component * @see source symfony filesystem component
*/ */
public static function isAbsolutePath(string $file): bool public static function isAbsolutePath($file)
{ {
return strspn($file, '/\\', 0, 1) return strspn($file, '/\\', 0, 1)
|| ( || (
@@ -281,7 +310,14 @@ final class FilesUtil
|| parse_url($file, \PHP_URL_SCHEME) !== null; || parse_url($file, \PHP_URL_SCHEME) !== null;
} }
public static function symlink(string $target, string $path, bool $allowSymlink): bool /**
* @param string $target
* @param string $path
* @param bool $allowSymlink
*
* @return bool
*/
public static function symlink($target, $path, $allowSymlink)
{ {
if (\DIRECTORY_SEPARATOR === '\\' || !$allowSymlink) { if (\DIRECTORY_SEPARATOR === '\\' || !$allowSymlink) {
return file_put_contents($path, $target) !== false; return file_put_contents($path, $target) !== false;
@@ -290,7 +326,12 @@ final class FilesUtil
return symlink($target, $path); return symlink($target, $path);
} }
public static function isBadCompressionFile(string $file): bool /**
* @param string $file
*
* @return bool
*/
public static function isBadCompressionFile($file)
{ {
$badCompressFileExt = [ $badCompressFileExt = [
'dic', 'dic',
@@ -316,7 +357,12 @@ final class FilesUtil
return self::isBadCompressionMimeType($mimeType); return self::isBadCompressionMimeType($mimeType);
} }
public static function isBadCompressionMimeType(string $mimeType): bool /**
* @param string $mimeType
*
* @return bool
*/
public static function isBadCompressionMimeType($mimeType)
{ {
static $badDeflateCompMimeTypes = [ static $badDeflateCompMimeTypes = [
'application/epub+zip', 'application/epub+zip',
@@ -367,13 +413,19 @@ final class FilesUtil
'x-epoc/x-sisx-app', 'x-epoc/x-sisx-app',
]; ];
return \in_array($mimeType, $badDeflateCompMimeTypes, true); return (bool) (\in_array($mimeType, $badDeflateCompMimeTypes, true))
;
} }
/** /**
* @param string $file
*
* @return string
*
* @noinspection PhpComposerExtensionStubsInspection * @noinspection PhpComposerExtensionStubsInspection
*/ */
public static function getMimeTypeFromFile(string $file): string public static function getMimeTypeFromFile($file)
{ {
if (\function_exists('mime_content_type')) { if (\function_exists('mime_content_type')) {
return mime_content_type($file); return mime_content_type($file);
@@ -383,10 +435,14 @@ final class FilesUtil
} }
/** /**
* @param string $contents
*
* @return string
* @noinspection PhpComposerExtensionStubsInspection * @noinspection PhpComposerExtensionStubsInspection
*/ */
public static function getMimeTypeFromString(string $contents): string public static function getMimeTypeFromString($contents)
{ {
$contents = (string) $contents;
$finfo = new \finfo(\FILEINFO_MIME); $finfo = new \finfo(\FILEINFO_MIME);
$mimeType = $finfo->buffer($contents); $mimeType = $finfo->buffer($contents);

View File

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

View File

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

View File

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

69
src/Util/PackUtil.php Normal file
View File

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

View File

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

File diff suppressed because it is too large Load Diff

902
src/ZipFileInterface.php Normal file
View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests; namespace PhpZip\Tests;
use PhpZip\Exception\ZipEntryNotFoundException; use PhpZip\Exception\ZipEntryNotFoundException;
@@ -35,7 +26,7 @@ final class CustomZipFormatTest extends ZipTestCase
* *
* @see http://www.epubtest.org/test-books source epub files * @see http://www.epubtest.org/test-books source epub files
*/ */
public function testEpub(): void public function testEpub()
{ {
$epubFile = new EpubFile(); $epubFile = new EpubFile();
$epubFile->openFile(__DIR__ . '/resources/Advanced-v1.0.0.epub'); $epubFile->openFile(__DIR__ . '/resources/Advanced-v1.0.0.epub');
@@ -76,7 +67,7 @@ final class CustomZipFormatTest extends ZipTestCase
/** /**
* @throws \Exception * @throws \Exception
*/ */
public function testBeforeSaveInZipWriter(): void public function testBeforeSaveInZipWriter()
{ {
$zipFile = new ZipFileCustomWriter(); $zipFile = new ZipFileCustomWriter();
for ($i = 0; $i < 10; $i++) { for ($i = 0; $i < 10; $i++) {
@@ -97,7 +88,7 @@ final class CustomZipFormatTest extends ZipTestCase
/** /**
* @throws \Exception * @throws \Exception
*/ */
public function testBeforeSaveInZipFile(): void public function testBeforeSaveInZipFile()
{ {
$zipFile = new ZipFileWithBeforeSave(); $zipFile = new ZipFileWithBeforeSave();
for ($i = 0; $i < 10; $i++) { for ($i = 0; $i < 10; $i++) {
@@ -115,7 +106,11 @@ final class CustomZipFormatTest extends ZipTestCase
$zipFile->close(); $zipFile->close();
} }
private function existsExtraFields(ZipFile $zipFile, bool $exists): void /**
* @param ZipFile $zipFile
* @param bool $exists
*/
private function existsExtraFields(ZipFile $zipFile, $exists)
{ {
foreach ($zipFile->getEntries() as $entry) { foreach ($zipFile->getEntries() as $entry) {
$localExtras = $entry->getLocalExtraFields(); $localExtras = $entry->getLocalExtraFields();

View File

@@ -1,13 +1,6 @@
<?php <?php
declare(strict_types=1); /** @noinspection PhpUndefinedMethodInspection */
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
@@ -15,22 +8,32 @@ use PHPUnit\Framework\TestCase;
use PhpZip\Exception\ZipException; use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\AbstractUnicodeExtraField; use PhpZip\Model\Extra\Fields\AbstractUnicodeExtraField;
/**
* Class AbstractUnicodeExtraFieldTest.
*/
abstract class AbstractUnicodeExtraFieldTest extends TestCase abstract class AbstractUnicodeExtraFieldTest extends TestCase
{ {
/** /**
* @return string|AbstractUnicodeExtraField * @return string|AbstractUnicodeExtraField
* *
* @psalm-var class-string<AbstractUnicodeExtraField> * @psalm-var class-string<\PhpZip\Model\Extra\Fields\AbstractUnicodeExtraField>
*/ */
abstract protected function getUnicodeExtraFieldClassName(); abstract protected function getUnicodeExtraFieldClassName();
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @param int $crc32
* @param string $unicodePath
* @param string $originalPath
* @param string $binaryData
*
* @throws ZipException * @throws ZipException
*/ */
public function testExtraField(int $crc32, string $unicodePath, string $originalPath, string $binaryData): void public function testExtraField($crc32, $unicodePath, $originalPath, $binaryData)
{ {
$crc32 = (int) $crc32; // for php 32-bit
$className = $this->getUnicodeExtraFieldClassName(); $className = $this->getUnicodeExtraFieldClassName();
/** @var AbstractUnicodeExtraField $extraField */ /** @var AbstractUnicodeExtraField $extraField */
@@ -45,9 +48,12 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
static::assertEquals($className::unpackCentralDirData($binaryData), $extraField); static::assertEquals($className::unpackCentralDirData($binaryData), $extraField);
} }
abstract public function provideExtraField(): array; /**
* @return array
*/
abstract public function provideExtraField();
public function testSetter(): void public function testSetter()
{ {
$className = $this->getUnicodeExtraFieldClassName(); $className = $this->getUnicodeExtraFieldClassName();
$entryName = '11111'; $entryName = '11111';
@@ -68,10 +74,14 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
/** /**
* @throws ZipException * @throws ZipException
*/ */
public function testUnicodeErrorParse(): void public function testUnicodeErrorParse()
{ {
$this->expectException(ZipException::class); $this->expectException(
$this->expectExceptionMessage('Unicode path extra data must have at least 5 bytes.'); ZipException::class
);
$this->expectExceptionMessage(
'Unicode path extra data must have at least 5 bytes.'
);
$className = $this->getUnicodeExtraFieldClassName(); $className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData(''); $className::unpackLocalFileData('');
@@ -80,10 +90,14 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
/** /**
* @throws ZipException * @throws ZipException
*/ */
public function testUnknownVersionParse(): void public function testUnknownVersionParse()
{ {
$this->expectException(ZipException::class); $this->expectException(
$this->expectExceptionMessage('Unsupported version [2] for Unicode path extra data.'); ZipException::class
);
$this->expectExceptionMessage(
'Unsupported version [2] for Unicode path extra data.'
);
$className = $this->getUnicodeExtraFieldClassName(); $className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData("\x02\x04a\xD28\xC3\xA4\\\xC3\xBC.txt"); $className::unpackLocalFileData("\x02\x04a\xD28\xC3\xA4\\\xC3\xBC.txt");

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -25,14 +16,19 @@ final class ApkAlignmentExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @param int $multiple
* @param int $padding
* @param string $binaryData
* @param string $toString
*
* @throws ZipException * @throws ZipException
*/ */
public function testExtraField( public function testExtraField(
int $multiple, $multiple,
int $padding, $padding,
string $binaryData, $binaryData,
string $toString $toString
): void { ) {
$extraField = new ApkAlignmentExtraField($multiple, $padding); $extraField = new ApkAlignmentExtraField($multiple, $padding);
self::assertSame($extraField->getHeaderId(), ApkAlignmentExtraField::HEADER_ID); self::assertSame($extraField->getHeaderId(), ApkAlignmentExtraField::HEADER_ID);
self::assertSame($extraField->getMultiple(), $multiple); self::assertSame($extraField->getMultiple(), $multiple);
@@ -47,7 +43,10 @@ final class ApkAlignmentExtraFieldTest extends TestCase
self::assertSame((string) $extraField, $toString); self::assertSame((string) $extraField, $toString);
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -83,7 +82,7 @@ final class ApkAlignmentExtraFieldTest extends TestCase
]; ];
} }
public function testSetter(): void public function testSetter()
{ {
$extraField = new ApkAlignmentExtraField(ApkAlignmentExtraField::ALIGNMENT_BYTES, 3); $extraField = new ApkAlignmentExtraField(ApkAlignmentExtraField::ALIGNMENT_BYTES, 3);
$extraField->setMultiple(ApkAlignmentExtraField::COMMON_PAGE_ALIGNMENT_BYTES); $extraField->setMultiple(ApkAlignmentExtraField::COMMON_PAGE_ALIGNMENT_BYTES);
@@ -95,10 +94,14 @@ final class ApkAlignmentExtraFieldTest extends TestCase
/** /**
* @throws ZipException * @throws ZipException
*/ */
public function testInvalidParse(): void public function testInvalidParse()
{ {
$this->expectException(ZipException::class); $this->expectException(
$this->expectExceptionMessage('Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.'); ZipException::class
);
$this->expectExceptionMessage(
'Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.'
);
ApkAlignmentExtraField::unpackLocalFileData("\x04"); ApkAlignmentExtraField::unpackLocalFileData("\x04");
} }

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -26,9 +17,15 @@ final class AsiExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @param int $mode
* @param int $uid
* @param int $gid
* @param string $link
* @param string $binaryData
*
* @throws ZipException * @throws ZipException
*/ */
public function testExtraField(int $mode, int $uid, int $gid, string $link, string $binaryData): void public function testExtraField($mode, $uid, $gid, $link, $binaryData)
{ {
$asiExtraField = new AsiExtraField($mode, $uid, $gid, $link); $asiExtraField = new AsiExtraField($mode, $uid, $gid, $link);
self::assertSame($asiExtraField->getHeaderId(), AsiExtraField::HEADER_ID); self::assertSame($asiExtraField->getHeaderId(), AsiExtraField::HEADER_ID);
@@ -45,7 +42,10 @@ final class AsiExtraFieldTest extends TestCase
self::assertEquals(AsiExtraField::unpackCentralDirData($binaryData), $asiExtraField); self::assertEquals(AsiExtraField::unpackCentralDirData($binaryData), $asiExtraField);
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -65,7 +65,7 @@ final class AsiExtraFieldTest extends TestCase
]; ];
} }
public function testSetter(): void public function testSetter()
{ {
$extraField = new AsiExtraField(0777); $extraField = new AsiExtraField(0777);
$extraField->setMode(0100666); $extraField->setMode(0100666);
@@ -89,10 +89,14 @@ final class AsiExtraFieldTest extends TestCase
/** /**
* @throws Crc32Exception * @throws Crc32Exception
*/ */
public function testInvalidParse(): void public function testInvalidParse()
{ {
$this->expectException(Crc32Exception::class); $this->expectException(
$this->expectExceptionMessage('Asi Unix Extra Filed Data (expected CRC32 value'); Crc32Exception::class
);
$this->expectExceptionMessage(
'Asi Unix Extra Filed Data (expected CRC32 value'
);
AsiExtraField::unpackLocalFileData("\x01\x06\\\xF6\xEDA\x00\x00\x00\x00\xE8\x03\xE8\x03"); AsiExtraField::unpackLocalFileData("\x01\x06\\\xF6\xEDA\x00\x00\x00\x00\xE8\x03\xE8\x03");
} }

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -26,20 +17,23 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @noinspection PhpTooManyParametersInspection * @param int $flags
* @param int|null $modifyTime
* @param int|null $accessTime
* @param int|null $createTime
* @param string $localData
* @param string $cdData
* *
* @param ?int $modifyTime * @noinspection PhpTooManyParametersInspection
* @param ?int $accessTime
* @param ?int $createTime
*/ */
public function testExtraField( public function testExtraField(
int $flags, $flags,
?int $modifyTime, $modifyTime,
?int $accessTime, $accessTime,
?int $createTime, $createTime,
string $localData, $localData,
string $cdData $cdData
): void { ) {
$localExtraField = new ExtendedTimestampExtraField($flags, $modifyTime, $accessTime, $createTime); $localExtraField = new ExtendedTimestampExtraField($flags, $modifyTime, $accessTime, $createTime);
self::assertSame($localExtraField->getHeaderId(), ExtendedTimestampExtraField::HEADER_ID); self::assertSame($localExtraField->getHeaderId(), ExtendedTimestampExtraField::HEADER_ID);
self::assertSame($localExtraField->getFlags(), $flags); self::assertSame($localExtraField->getFlags(), $flags);
@@ -65,7 +59,10 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
self::assertSame($localExtraField->packCentralDirData(), $cdData); self::assertSame($localExtraField->packCentralDirData(), $cdData);
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -101,7 +98,7 @@ final class ExtendedTimestampExtraFieldTest extends TestCase
/** /**
* @throws \Exception * @throws \Exception
*/ */
public function testSetter(): void public function testSetter()
{ {
$mtime = time(); $mtime = time();
$atime = null; $atime = null;

View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -27,9 +18,14 @@ final class NewUnixExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @param int $version
* @param int $uid
* @param int $gid
* @param string $binaryData
*
* @throws ZipException * @throws ZipException
*/ */
public function testExtraField(int $version, int $uid, int $gid, string $binaryData): void public function testExtraField($version, $uid, $gid, $binaryData)
{ {
$extraField = new NewUnixExtraField($version, $uid, $gid); $extraField = new NewUnixExtraField($version, $uid, $gid);
self::assertSame($extraField->getHeaderId(), NewUnixExtraField::HEADER_ID); self::assertSame($extraField->getHeaderId(), NewUnixExtraField::HEADER_ID);
@@ -44,7 +40,10 @@ final class NewUnixExtraFieldTest extends TestCase
self::assertSame($extraField->packCentralDirData(), $binaryData); self::assertSame($extraField->packCentralDirData(), $binaryData);
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -80,7 +79,7 @@ final class NewUnixExtraFieldTest extends TestCase
]; ];
} }
public function testSetter(): void public function testSetter()
{ {
$extraField = new NewUnixExtraField(1, 1000, 1000); $extraField = new NewUnixExtraField(1, 1000, 1000);
self::assertSame(1, $extraField->getVersion()); self::assertSame(1, $extraField->getVersion());

View File

@@ -1,20 +1,13 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PhpZip\Model\Extra\Fields\NtfsExtraField; use PhpZip\Model\Extra\Fields\NtfsExtraField;
/** /**
* Class NtfsExtraFieldTest.
*
* @internal * @internal
* *
* @small * @small
@@ -23,6 +16,8 @@ final class NtfsExtraFieldTest extends TestCase
{ {
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp();
if (\PHP_INT_SIZE === 4) { if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test'); self::markTestSkipped('only 64 bit test');
} }
@@ -31,19 +26,27 @@ final class NtfsExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @param int $modifyNtfsTime
* @param int $accessNtfsTime
* @param int $createNtfsTime
* @param float $modifyTimestamp
* @param float $accessTimestamp
* @param float $createTimestamp
* @param string $binaryData
*
* @throws \Exception * @throws \Exception
* *
* @noinspection PhpTooManyParametersInspection * @noinspection PhpTooManyParametersInspection
*/ */
public function testExtraField( public function testExtraField(
int $modifyNtfsTime, $modifyNtfsTime,
int $accessNtfsTime, $accessNtfsTime,
int $createNtfsTime, $createNtfsTime,
float $modifyTimestamp, $modifyTimestamp,
float $accessTimestamp, $accessTimestamp,
float $createTimestamp, $createTimestamp,
string $binaryData $binaryData
): void { ) {
$extraField = new NtfsExtraField($modifyNtfsTime, $accessNtfsTime, $createNtfsTime); $extraField = new NtfsExtraField($modifyNtfsTime, $accessNtfsTime, $createNtfsTime);
self::assertSame($extraField->getHeaderId(), NtfsExtraField::HEADER_ID); self::assertSame($extraField->getHeaderId(), NtfsExtraField::HEADER_ID);
@@ -63,24 +66,15 @@ final class NtfsExtraFieldTest extends TestCase
$extraField->getCreateDateTime() $extraField->getCreateDateTime()
); );
self::assertEqualsIntegerWithDelta( self::assertEqualsIntegerWithDelta($extraFieldFromDateTime->getModifyNtfsTime(), $extraField->getModifyNtfsTime(), 100);
$extraFieldFromDateTime->getModifyNtfsTime(), self::assertEqualsIntegerWithDelta($extraFieldFromDateTime->getAccessNtfsTime(), $extraField->getAccessNtfsTime(), 100);
$extraField->getModifyNtfsTime(), self::assertEqualsIntegerWithDelta($extraFieldFromDateTime->getCreateNtfsTime(), $extraField->getCreateNtfsTime(), 100);
100
);
self::assertEqualsIntegerWithDelta(
$extraFieldFromDateTime->getAccessNtfsTime(),
$extraField->getAccessNtfsTime(),
100
);
self::assertEqualsIntegerWithDelta(
$extraFieldFromDateTime->getCreateNtfsTime(),
$extraField->getCreateNtfsTime(),
100
);
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -113,12 +107,18 @@ final class NtfsExtraFieldTest extends TestCase
]; ];
} }
/**
* @param int $expected
* @param int $actual
* @param int $delta
* @param string $message
*/
private static function assertEqualsIntegerWithDelta( private static function assertEqualsIntegerWithDelta(
int $expected, $expected,
int $actual, $actual,
int $delta, $delta,
string $message = '' $message = ''
): void { ) {
self::assertSame( self::assertSame(
self::roundInt($expected, $delta), self::roundInt($expected, $delta),
self::roundInt($actual, $delta), self::roundInt($actual, $delta),
@@ -126,7 +126,13 @@ final class NtfsExtraFieldTest extends TestCase
); );
} }
private static function roundInt(int $number, int $delta): int /**
* @param int $number
* @param int $delta
*
* @return int
*/
private static function roundInt($number, $delta)
{ {
return (int) (floor($number / $delta) * $delta); return (int) (floor($number / $delta) * $delta);
} }
@@ -134,16 +140,24 @@ final class NtfsExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @param int $mtimeNtfs
* @param int $atimeNtfs
* @param int $ctimeNtfs
* @param float $mtimeTimestamp
* @param float $atimeTimestamp
* @param float $ctimeTimestamp
*
* @noinspection PhpTooManyParametersInspection * @noinspection PhpTooManyParametersInspection
* @noinspection PhpUnitDeprecationsInspection
*/ */
public function testConverter( public function testConverter(
int $mtimeNtfs, $mtimeNtfs,
int $atimeNtfs, $atimeNtfs,
int $ctimeNtfs, $ctimeNtfs,
float $mtimeTimestamp, $mtimeTimestamp,
float $atimeTimestamp, $atimeTimestamp,
float $ctimeTimestamp $ctimeTimestamp
): void { ) {
self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($mtimeNtfs), $mtimeTimestamp, 0.00001); self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($mtimeNtfs), $mtimeTimestamp, 0.00001);
self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($atimeNtfs), $atimeTimestamp, 0.00001); self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($atimeNtfs), $atimeTimestamp, 0.00001);
self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($ctimeNtfs), $ctimeTimestamp, 0.00001); self::assertEqualsWithDelta(NtfsExtraField::ntfsTimeToTimestamp($ctimeNtfs), $ctimeTimestamp, 0.00001);
@@ -156,7 +170,7 @@ final class NtfsExtraFieldTest extends TestCase
/** /**
* @throws \Exception * @throws \Exception
*/ */
public function testSetter(): void public function testSetter()
{ {
$timeZone = new \DateTimeZone('UTC'); $timeZone = new \DateTimeZone('UTC');
$initDateTime = new \DateTimeImmutable('-1 min', $timeZone); $initDateTime = new \DateTimeImmutable('-1 min', $timeZone);

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -26,23 +17,25 @@ final class OldUnixExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @noinspection PhpTooManyParametersInspection * @param int|null $accessTime
* @param int|null $modifyTime
* @param int|null $uid
* @param int|null $gid
* @param string $localBinaryData
* @param string $cdBinaryData
* *
* @param ?int $accessTime * @noinspection PhpTooManyParametersInspection
* @param ?int $modifyTime
* @param ?int $uid
* @param ?int $gid
* *
* @throws \Exception * @throws \Exception
*/ */
public function testExtraField( public function testExtraField(
?int $accessTime, $accessTime,
?int $modifyTime, $modifyTime,
?int $uid, $uid,
?int $gid, $gid,
string $localBinaryData, $localBinaryData,
string $cdBinaryData $cdBinaryData
): void { ) {
$extraField = new OldUnixExtraField($accessTime, $modifyTime, $uid, $gid); $extraField = new OldUnixExtraField($accessTime, $modifyTime, $uid, $gid);
self::assertSame($extraField->getHeaderId(), OldUnixExtraField::HEADER_ID); self::assertSame($extraField->getHeaderId(), OldUnixExtraField::HEADER_ID);
@@ -96,7 +89,10 @@ final class OldUnixExtraFieldTest extends TestCase
self::assertSame($extraField->packCentralDirData(), $cdBinaryData); self::assertSame($extraField->packCentralDirData(), $cdBinaryData);
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -134,7 +130,7 @@ final class OldUnixExtraFieldTest extends TestCase
]; ];
} }
public function testSetter(): void public function testSetter()
{ {
$extraField = new OldUnixExtraField(null, null, null, null); $extraField = new OldUnixExtraField(null, null, null, null);

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PhpZip\Model\Extra\Fields\UnicodeCommentExtraField; use PhpZip\Model\Extra\Fields\UnicodeCommentExtraField;
@@ -28,7 +19,10 @@ final class UnicodeCommentExtraFieldTest extends AbstractUnicodeExtraFieldTest
return UnicodeCommentExtraField::class; return UnicodeCommentExtraField::class;
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PhpZip\Model\Extra\Fields\UnicodePathExtraField; use PhpZip\Model\Extra\Fields\UnicodePathExtraField;
@@ -30,7 +21,10 @@ final class UnicodePathExtraFieldTest extends AbstractUnicodeExtraFieldTest
return UnicodePathExtraField::class; return UnicodePathExtraField::class;
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [

View File

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

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -29,16 +20,22 @@ final class WinZipAesExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @param int $vendorVersion
* @param int $keyStrength
* @param int $compressionMethod
* @param int $saltSize
* @param string $binaryData
*
* @throws ZipException * @throws ZipException
* @throws ZipUnsupportMethodException * @throws ZipUnsupportMethodException
*/ */
public function testExtraField( public function testExtraField(
int $vendorVersion, $vendorVersion,
int $keyStrength, $keyStrength,
int $compressionMethod, $compressionMethod,
int $saltSize, $saltSize,
string $binaryData $binaryData
): void { ) {
$extraField = new WinZipAesExtraField($vendorVersion, $keyStrength, $compressionMethod); $extraField = new WinZipAesExtraField($vendorVersion, $keyStrength, $compressionMethod);
self::assertSame($extraField->getHeaderId(), WinZipAesExtraField::HEADER_ID); self::assertSame($extraField->getHeaderId(), WinZipAesExtraField::HEADER_ID);
self::assertSame($extraField->getVendorVersion(), $vendorVersion); self::assertSame($extraField->getVendorVersion(), $vendorVersion);
@@ -53,7 +50,10 @@ final class WinZipAesExtraFieldTest extends TestCase
self::assertEquals(WinZipAesExtraField::unpackCentralDirData($binaryData), $extraField); self::assertEquals(WinZipAesExtraField::unpackCentralDirData($binaryData), $extraField);
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -104,7 +104,7 @@ final class WinZipAesExtraFieldTest extends TestCase
/** /**
* @throws ZipUnsupportMethodException * @throws ZipUnsupportMethodException
*/ */
public function testSetter(): void public function testSetter()
{ {
$extraField = new WinZipAesExtraField( $extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1, WinZipAesExtraField::VERSION_AE1,
@@ -155,7 +155,7 @@ final class WinZipAesExtraFieldTest extends TestCase
/** /**
* @throws ZipUnsupportMethodException * @throws ZipUnsupportMethodException
*/ */
public function testConstructUnsupportVendorVersion(): void public function testConstructUnsupportVendorVersion()
{ {
$this->expectException(InvalidArgumentException::class); $this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3'); $this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3');
@@ -167,7 +167,10 @@ final class WinZipAesExtraFieldTest extends TestCase
); );
} }
public function testSetterUnsupportVendorVersion(): void /**
* @throws ZipUnsupportMethodException
*/
public function testSetterUnsupportVendorVersion()
{ {
$this->expectException(InvalidArgumentException::class); $this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3'); $this->expectExceptionMessage('Unsupport WinZip AES vendor version: 3');
@@ -180,7 +183,10 @@ final class WinZipAesExtraFieldTest extends TestCase
$extraField->setVendorVersion(3); $extraField->setVendorVersion(3);
} }
public function testConstructUnsupportCompressionMethod(): void /**
* @throws ZipUnsupportMethodException
*/
public function testConstructUnsupportCompressionMethod()
{ {
$this->expectException(ZipUnsupportMethodException::class); $this->expectException(ZipUnsupportMethodException::class);
$this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.'); $this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.');
@@ -192,7 +198,10 @@ final class WinZipAesExtraFieldTest extends TestCase
); );
} }
public function testSetterUnsupportCompressionMethod(): void /**
* @throws ZipUnsupportMethodException
*/
public function testSetterUnsupportCompressionMethod()
{ {
$this->expectException(ZipUnsupportMethodException::class); $this->expectException(ZipUnsupportMethodException::class);
$this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.'); $this->expectExceptionMessage('Compression method 3 (Reduced compression factor 2) is not supported.');
@@ -208,7 +217,7 @@ final class WinZipAesExtraFieldTest extends TestCase
/** /**
* @throws ZipUnsupportMethodException * @throws ZipUnsupportMethodException
*/ */
public function testConstructUnsupportKeyStrength(): void public function testConstructUnsupportKeyStrength()
{ {
$this->expectException(InvalidArgumentException::class); $this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3'); $this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3');
@@ -221,9 +230,9 @@ final class WinZipAesExtraFieldTest extends TestCase
} }
/** /**
* @throws ZipException * @throws ZipUnsupportMethodException
*/ */
public function testSetterUnsupportKeyStrength(): void public function testSetterUnsupportKeyStrength()
{ {
$this->expectException(InvalidArgumentException::class); $this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3'); $this->expectExceptionMessage('Key strength 16 not support value. Allow values: 1, 2, 3');

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Extra\Fields; namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@@ -26,6 +17,8 @@ final class Zip64ExtraFieldTest extends TestCase
{ {
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp();
if (\PHP_INT_SIZE === 4) { if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test'); self::markTestSkipped('only 64 bit test');
} }
@@ -34,25 +27,25 @@ final class Zip64ExtraFieldTest extends TestCase
/** /**
* @dataProvider provideExtraField * @dataProvider provideExtraField
* *
* @noinspection PhpTooManyParametersInspection * @param int|null $uncompressedSize
* * @param int|null $compressedSize
* @param ?int $uncompressedSize * @param int|null $localHeaderOffset
* @param ?int $compressedSize * @param int|null $diskStart
* @param ?int $localHeaderOffset * @param string|null $localBinaryData
* @param ?int $diskStart * @param string|null $cdBinaryData
* @param ?string $localBinaryData
* @param ?string $cdBinaryData
* *
* @throws ZipException * @throws ZipException
*
* @noinspection PhpTooManyParametersInspection
*/ */
public function testExtraField( public function testExtraField(
?int $uncompressedSize, $uncompressedSize,
?int $compressedSize, $compressedSize,
?int $localHeaderOffset, $localHeaderOffset,
?int $diskStart, $diskStart,
?string $localBinaryData, $localBinaryData,
?string $cdBinaryData $cdBinaryData
): void { ) {
$extraField = new Zip64ExtraField( $extraField = new Zip64ExtraField(
$uncompressedSize, $uncompressedSize,
$compressedSize, $compressedSize,
@@ -66,9 +59,9 @@ final class Zip64ExtraFieldTest extends TestCase
self::assertSame($extraField->getDiskStart(), $diskStart); self::assertSame($extraField->getDiskStart(), $diskStart);
$zipEntry = new ZipEntry('entry'); $zipEntry = new ZipEntry('entry');
$zipEntry->setUncompressedSize($uncompressedSize !== null ? ZipConstants::ZIP64_MAGIC : 0xFFFFF); $zipEntry->setUncompressedSize($uncompressedSize !== null ? ZipConstants::ZIP64_MAGIC : 0xfffff);
$zipEntry->setCompressedSize($compressedSize !== null ? ZipConstants::ZIP64_MAGIC : 0xFFFF); $zipEntry->setCompressedSize($compressedSize !== null ? ZipConstants::ZIP64_MAGIC : 0xffff);
$zipEntry->setLocalHeaderOffset($localHeaderOffset !== null ? ZipConstants::ZIP64_MAGIC : 0xFFF); $zipEntry->setLocalHeaderOffset($localHeaderOffset !== null ? ZipConstants::ZIP64_MAGIC : 0xfff);
if ($localBinaryData !== null) { if ($localBinaryData !== null) {
self::assertSame($localBinaryData, $extraField->packLocalFileData()); self::assertSame($localBinaryData, $extraField->packLocalFileData());
@@ -81,7 +74,10 @@ final class Zip64ExtraFieldTest extends TestCase
} }
} }
public function provideExtraField(): array /**
* @return array
*/
public function provideExtraField()
{ {
return [ return [
[ [
@@ -111,7 +107,7 @@ final class Zip64ExtraFieldTest extends TestCase
]; ];
} }
public function testSetter(): void public function testSetter()
{ {
$extraField = new Zip64ExtraField(); $extraField = new Zip64ExtraField();
self::assertNull($extraField->getUncompressedSize()); self::assertNull($extraField->getUncompressedSize());

View File

@@ -1,23 +1,30 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\CustomZip; namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\IO\ZipWriter; use PhpZip\IO\ZipWriter;
use PhpZip\Model\Extra\Fields\NewUnixExtraField; use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField; use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\Model\ZipContainer;
/**
* Class CustomZipWriter.
*/
class CustomZipWriter extends ZipWriter class CustomZipWriter extends ZipWriter
{ {
protected function beforeWrite(): void /**
* ZipWriter constructor.
*
* @param ZipContainer $container
*/
public function __construct(ZipContainer $container)
{
// dump($container);
parent::__construct($container);
// dd($this->zipContainer);
}
protected function beforeWrite()
{ {
parent::beforeWrite(); parent::beforeWrite();
$now = new \DateTimeImmutable(); $now = new \DateTimeImmutable();

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,6 @@
<?php <?php
declare(strict_types=1); /** @noinspection PhpComposerExtensionStubsInspection */
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\Epub; namespace PhpZip\Tests\Internal\Epub;
@@ -28,25 +21,39 @@ use PhpZip\ZipFile;
*/ */
class EpubFile extends ZipFile class EpubFile extends ZipFile
{ {
protected function createZipWriter(): ZipWriter /**
* @return ZipWriter
*/
protected function createZipWriter()
{ {
return new EpubWriter($this->zipContainer); return new EpubWriter($this->zipContainer);
} }
/** /**
* @param resource $inputStream * @param resource $inputStream
* @param array $options
*
* @return ZipReader
*/ */
protected function createZipReader($inputStream, array $options = []): ZipReader protected function createZipReader($inputStream, array $options = [])
{ {
return new EpubReader($inputStream, $options); return new EpubReader($inputStream, $options);
} }
protected function createZipContainer(?ImmutableZipContainer $sourceContainer = null): ZipContainer /**
* @param ImmutableZipContainer|null $sourceContainer
*
* @return ZipContainer
*/
protected function createZipContainer(ImmutableZipContainer $sourceContainer = null)
{ {
return new EpubZipContainer($sourceContainer); return new EpubZipContainer($sourceContainer);
} }
protected function addZipEntry(ZipEntry $zipEntry): void /**
* @param ZipEntry $zipEntry
*/
protected function addZipEntry(ZipEntry $zipEntry)
{ {
$zipEntry->setCreatedOS(ZipPlatform::OS_DOS); $zipEntry->setCreatedOS(ZipPlatform::OS_DOS);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX); $zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
@@ -55,25 +62,25 @@ class EpubFile extends ZipFile
/** /**
* @throws ZipEntryNotFoundException * @throws ZipEntryNotFoundException
*
* @return string
*/ */
public function getMimeType(): string public function getMimeType()
{ {
return $this->zipContainer->getMimeType(); return $this->zipContainer->getMimeType();
} }
/** public function getEpubInfo()
* @throws ZipException
* @throws ZipEntryNotFoundException
*/
public function getEpubInfo(): EpubInfo
{ {
return new EpubInfo($this->getEntryContents($this->getRootFile())); return new EpubInfo($this->getEntryContents($this->getRootFile()));
} }
/** /**
* @throws ZipException * @throws ZipException
*
* @return string
*/ */
public function getRootFile(): string public function getRootFile()
{ {
$entryName = 'META-INF/container.xml'; $entryName = 'META-INF/container.xml';
$contents = $this->getEntryContents($entryName); $contents = $this->getEntryContents($entryName);

View File

@@ -1,13 +1,6 @@
<?php <?php
declare(strict_types=1); /** @noinspection PhpComposerExtensionStubsInspection */
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\Epub; namespace PhpZip\Tests\Internal\Epub;
@@ -20,21 +13,29 @@ use PhpZip\Exception\ZipException;
*/ */
class EpubInfo class EpubInfo
{ {
private ?string $title; /** @var string|null */
private $title;
private ?string $creator; /** @var string|null */
private $creator;
private ?string $language; /** @var string|null */
private $language;
private ?string $publisher; /** @var string|null */
private $publisher;
private ?string $description; /** @var string|null */
private $description;
private ?string $rights; /** @var string|null */
private $rights;
private ?string $date; /** @var string|null */
private $date;
private ?string $subject; /** @var string|null */
private $subject;
/** /**
* EpubInfo constructor. * EpubInfo constructor.
@@ -75,47 +76,74 @@ class EpubInfo
$this->subject = empty($subject) ? null : $subject; $this->subject = empty($subject) ? null : $subject;
} }
public function getTitle(): ?string /**
* @return string|null
*/
public function getTitle()
{ {
return $this->title; return $this->title;
} }
public function getCreator(): ?string /**
* @return string|null
*/
public function getCreator()
{ {
return $this->creator; return $this->creator;
} }
public function getLanguage(): ?string /**
* @return string|null
*/
public function getLanguage()
{ {
return $this->language; return $this->language;
} }
public function getPublisher(): ?string /**
* @return string|null
*/
public function getPublisher()
{ {
return $this->publisher; return $this->publisher;
} }
public function getDescription(): ?string /**
* @return string|null
*/
public function getDescription()
{ {
return $this->description; return $this->description;
} }
public function getRights(): ?string /**
* @return string|null
*/
public function getRights()
{ {
return $this->rights; return $this->rights;
} }
public function getDate(): ?string /**
* @return string|null
*/
public function getDate()
{ {
return $this->date; return $this->date;
} }
public function getSubject(): ?string /**
* @return string|null
*/
public function getSubject()
{ {
return $this->subject; return $this->subject;
} }
public function toArray(): array /**
* @return array
*/
public function toArray()
{ {
return [ return [
'title' => $this->title, 'title' => $this->title,

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\Epub; namespace PhpZip\Tests\Internal\Epub;
use PhpZip\IO\ZipReader; use PhpZip\IO\ZipReader;
@@ -19,9 +10,11 @@ use PhpZip\IO\ZipReader;
class EpubReader extends ZipReader class EpubReader extends ZipReader
{ {
/** /**
* @return bool
*
* @see https://github.com/w3c/epubcheck/issues/334 * @see https://github.com/w3c/epubcheck/issues/334
*/ */
protected function isZip64Support(): bool protected function isZip64Support()
{ {
return false; return false;
} }

View File

@@ -1,14 +1,5 @@
<?php <?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\Epub; namespace PhpZip\Tests\Internal\Epub;
use PhpZip\Constants\ZipCompressionMethod; use PhpZip\Constants\ZipCompressionMethod;
@@ -28,7 +19,7 @@ class EpubWriter extends ZipWriter
/** /**
* @throws ZipUnsupportMethodException * @throws ZipUnsupportMethodException
*/ */
protected function beforeWrite(): void protected function beforeWrite()
{ {
parent::beforeWrite(); parent::beforeWrite();
@@ -44,7 +35,7 @@ class EpubWriter extends ZipWriter
$this->sortEntries(); $this->sortEntries();
} }
private function sortEntries(): void private function sortEntries()
{ {
$this->zipContainer->sortByEntry( $this->zipContainer->sortByEntry(
static function (ZipEntry $a, ZipEntry $b) { static function (ZipEntry $a, ZipEntry $b) {

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