1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-08-24 07:52:48 +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
.github export-ignore
.gitignore export-ignore
.php_cs export-ignore
.php-cs-fixer.php export-ignore
bootstrap.php export-ignore
logo.svg export-ignore
phpunit.xml export-ignore
phpunit.xml.dist export-ignore
psalm.xml export-ignore
psalm.xml.dist export-ignore
tests export-ignore

View File

@@ -9,8 +9,8 @@ jobs:
name: PHP ${{ matrix.php }} Test on ${{ matrix.os }}
env:
extensions: bz2, dom, iconv, fileinfo, openssl, xml, zlib
key: cache-v1
extensions: bz2, dom, iconv, fileinfo, openssl, xml, zlib, gd
key: cache-v2
PHPUNIT_COVERAGE: 0
PHP_INI: date.timezone='UTC', memory_limit=-1, opcache.enable=1, opcache.enable_cli=1
@@ -21,9 +21,11 @@ jobs:
- windows-latest
- macos-latest
php:
- '7.1'
- '7.2'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
runs-on: ${{ matrix.os }}
@@ -35,7 +37,7 @@ jobs:
-
name: Install linux dependencies
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
@@ -49,12 +51,12 @@ jobs:
-
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
-
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
-
@@ -120,10 +122,6 @@ jobs:
if: env.PHPUNIT_COVERAGE == 1
run: vendor/bin/phpunit -v --coverage-clover=coverage.clover --testsuite "all_tests" --group small,medium,large
-
name: Static analysis
run: vendor/bin/psalm --shepherd --stats --output-format=checkstyle | cs2pr --graceful-warnings --colorize
-
name: Upload code coverage scrutinizer
if: env.PHPUNIT_COVERAGE == 1

9
.gitignore vendored
View File

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

View File

@@ -2,27 +2,17 @@
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
* changes that will be made.
* - `php-cs-fixer fix --config='.php-cs-fixer.php' --dry-run`
* - `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:
* - `php-cs-fixer fix --config='.php-cs-fixer.php'
* - `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/
*/
@@ -49,9 +39,6 @@ $rules = [
'syntax' => 'short',
],
// Use the null coalescing assignment operator `??=` where possible.
'assign_null_coalescing_to_coalesce_equal' => true,
/*
* Converts backtick operators to `shell_exec` calls.
*
@@ -74,11 +61,7 @@ $rules = [
'blank_line_after_opening_tag' => true,
// An empty line feed must precede any configured statement.
'blank_line_before_statement' => [
'statements' => [
'return',
],
],
'blank_line_before_statement' => true,
/*
* The body of each structure MUST be enclosed by braces. Braces
@@ -86,7 +69,6 @@ $rules = [
* indented.
*/
'braces' => [
'allow_single_line_anonymous_class_with_empty_body' => true,
'allow_single_line_closure' => true,
],
@@ -103,7 +85,12 @@ $rules = [
* Whitespace around the keywords of a class, trait or interfaces
* 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.
'clean_namespace' => true,
@@ -155,12 +142,6 @@ $rules = [
*/
'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`.
*
@@ -176,16 +157,13 @@ $rules = [
*/
'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.
*
* Risky!
* 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__`
@@ -200,7 +178,9 @@ $rules = [
* Doctrine annotations must use configured operator for assignment
* in arrays.
*/
'doctrine_annotation_array_assignment' => true,
'doctrine_annotation_array_assignment' => [
'operator' => ':',
],
/*
* Doctrine annotations without arguments must use the configured
@@ -219,17 +199,15 @@ $rules = [
* space around named arguments assignment operator; there must be
* one space around array assignment operator.
*/
'doctrine_annotation_spaces' => true,
'doctrine_annotation_spaces' => [
'before_array_assignments_colon' => false,
],
/*
* Replaces short-echo `<?=` with long format `<?php echo`/`<?php
* print` syntax, or vice-versa.
*/
'echo_tag_syntax' => [
'format' => 'short',
'long_function' => 'echo',
'shorten_simple_statements_only' => true,
],
'echo_tag_syntax' => true,
/*
* The keyword `elseif` should be used instead of `else if` so that
@@ -237,14 +215,6 @@ $rules = [
*/
'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).
'encoding' => true,
@@ -411,27 +381,13 @@ $rules = [
* Risky when any of the configured functions to replace are
* overridden.
*/
'function_to_constant' => [
'functions' => [
'get_called_class',
'get_class',
'get_class_this',
'php_sapi_name',
'phpversion',
'pi',
],
],
'function_to_constant' => true,
// Ensure single space between function's argument and its typehint.
'function_typehint_space' => true,
// Configured annotations should be omitted from PHPDoc.
'general_phpdoc_annotation_remove' => [
'annotations' => [
'author',
'license',
],
],
'general_phpdoc_annotation_remove' => true,
// Renames PHPDoc tags.
'general_phpdoc_tag_rename' => [
@@ -451,21 +407,13 @@ $rules = [
'group_import' => false,
// Add, replace or remove header comment.
'header_comment' => [
'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',
],
'header_comment' => false,
/*
* Heredoc/nowdoc content must be properly indented. Requires PHP >=
* 7.3.
*/
'heredoc_indentation' => true,
'heredoc_indentation' => false,
// Convert `heredoc` to `nowdoc` where possible.
'heredoc_to_nowdoc' => true,
@@ -494,9 +442,6 @@ $rules = [
// Code MUST use configured indentation type.
'indentation_type' => true,
// Integer literals must be in correct case.
'integer_literal_case' => true,
/*
* Replaces `is_null($var)` expression with `null === $var`.
*
@@ -518,9 +463,7 @@ $rules = [
* List (`array` destructuring) assignment should be declared using
* the configured syntax. Requires PHP >= 7.1.
*/
'list_syntax' => [
'syntax' => 'short',
],
'list_syntax' => false,
/*
* Use `&&` and `||` logical operators instead of `and` and `or`.
@@ -557,8 +500,7 @@ $rules = [
* function.
*
* Risky!
* Risky when any of the functions are overridden, or when relying
* on the string byte size rather than its length in characters.
* Risky when any of the functions are overridden.
*/
'mb_str_functions' => false,
@@ -572,7 +514,6 @@ $rules = [
*/
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
'after_heredoc' => true,
],
/*
@@ -581,16 +522,6 @@ $rules = [
*/
'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
* `boolval` function calls with according type casting operator.
@@ -718,20 +649,7 @@ $rules = [
* Removes extra blank lines and/or blank lines following
* configuration.
*/
'no_extra_blank_lines' => [
'tokens' => [
'case',
'continue',
'curly_brace_block',
'default',
'extra',
'parenthesis_brace_block',
'square_brace_block',
'switch',
'throw',
'use',
],
],
'no_extra_blank_lines' => true,
/*
* Replace accidental usage of homoglyphs (non ascii characters) in
@@ -782,12 +700,6 @@ $rules = [
// Single-line whitespace before closing semicolon are prohibited.
'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
* 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
* any useful information.
*/
'no_superfluous_phpdoc_tags' => [
'allow_mixed' => true,
'allow_unused_params' => true,
],
'no_superfluous_phpdoc_tags' => false,
// Remove trailing commas in list function calls.
'no_trailing_comma_in_list_call' => true,
@@ -834,7 +743,7 @@ $rules = [
* Changing the whitespaces in strings might affect string
* comparisons and outputs.
*/
'no_trailing_whitespace_in_string' => true,
'no_trailing_whitespace_in_string' => false,
// Removes unneeded parentheses around control statements.
'no_unneeded_control_parentheses' => [
@@ -854,7 +763,9 @@ $rules = [
* Removes unneeded curly braces that are superfluous and aren't
* part of a control structure's body.
*/
'no_unneeded_curly_braces' => true,
'no_unneeded_curly_braces' => [
'namespaces' => true,
],
/*
* A `final` class must not have `final` methods and `private`
@@ -874,7 +785,7 @@ $rules = [
* systems (such as some Symfony components) that rely on those (for
* 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.
'no_unset_cast' => true,
@@ -890,7 +801,7 @@ $rules = [
* PHP 7.4, this rule might introduce `null` assignments to
* properties whose type declaration does not allow it.
*/
'no_unset_on_property' => false,
'no_unset_on_property' => true,
// Unused `use` statements must be removed.
'no_unused_imports' => true,
@@ -916,9 +827,7 @@ $rules = [
* In array declaration, there MUST NOT be a whitespace before each
* comma.
*/
'no_whitespace_before_comma_in_array' => [
'after_heredoc' => true,
],
'no_whitespace_before_comma_in_array' => true,
// Remove trailing whitespace at the end of blank lines.
'no_whitespace_in_blank_line' => true,
@@ -950,7 +859,7 @@ $rules = [
*
* 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 `->`
@@ -958,9 +867,6 @@ $rules = [
*/
'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
* at the end of the line.
@@ -971,6 +877,16 @@ $rules = [
'ordered_class_elements' => [
'order' => [
'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 when depending on order of the imports.
*/
'ordered_traits' => true,
'ordered_traits' => false,
/*
* PHPUnit assertion method calls like `->assertSame(true, $foo)`
@@ -1022,9 +938,7 @@ $rules = [
* Fixer could be risky if one is overriding PHPUnit's native
* methods.
*/
'php_unit_dedicate_assert' => [
'target' => 'newest',
],
'php_unit_dedicate_assert' => true,
/*
* PHPUnit assertions like `assertIsArray` should be used over
@@ -1044,7 +958,9 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or
* 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.
'php_unit_fqcn_annotation' => true,
@@ -1109,7 +1025,9 @@ $rules = [
* Risky when PHPUnit classes are overridden or not accessible, or
* 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()`
@@ -1177,18 +1095,7 @@ $rules = [
* All items of the given phpdoc tags must be either left-aligned or
* (by default) aligned vertically.
*/
'phpdoc_align' => [
'tags' => [
'return',
'throws',
'type',
'var',
'property',
'method',
'param',
],
'align' => 'vertical',
],
'phpdoc_align' => true,
// PHPDoc annotation descriptions should not be a sentence.
'phpdoc_annotation_without_dot' => true,
@@ -1241,7 +1148,15 @@ $rules = [
'phpdoc_order' => true,
// 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
@@ -1296,7 +1211,7 @@ $rules = [
* docblock, inheritdocs) will not be fixed. [3] Manual actions are
* 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
@@ -1322,7 +1237,7 @@ $rules = [
* docblock, inheritdocs) will not be fixed. [3] Manual actions are
* 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
@@ -1355,7 +1270,7 @@ $rules = [
* `@var` and `@type` annotations of classy properties should not
* contain the name.
*/
'phpdoc_var_without_name' => true,
'phpdoc_var_without_name' => false,
/*
* Converts `pow` to the `**` operator.
@@ -1384,11 +1299,10 @@ $rules = [
/*
* Replaces `rand`, `srand`, `getrandmax` functions calls with their
* `mt_*` analogs or `random_int`.
* `mt_*` analogs.
*
* Risky!
* Risky when the configured functions are overridden. Or when
* relying on the seed based generating of the numbers.
* Risky when the configured functions are overridden.
*/
'random_api_migration' => [
'replacements' => [
@@ -1477,7 +1391,7 @@ $rules = [
* A return statement wishing to return `void` should not return
* `null`.
*/
'simplified_null_return' => true,
'simplified_null_return' => false,
/*
* A PHP file without end tag must always end with a single empty
@@ -1551,7 +1465,7 @@ $rules = [
* Risky!
* Changing comparisons to strict might change code behavior.
*/
'strict_comparison' => true,
'strict_comparison' => false,
/*
* 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
* on non-strict usage.
*/
'strict_param' => true,
/*
* 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,
'strict_param' => false,
/*
* 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
* trailing comma.
*/
'trailing_comma_in_multiline' => [
'after_heredoc' => true,
],
'trailing_comma_in_multiline' => true,
/*
* Arrays should be formatted like function/method arguments,
@@ -1627,9 +1528,6 @@ $rules = [
*/
'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_operator_spaces' => true,
@@ -1641,7 +1539,7 @@ $rules = [
* Risky when using `isset()` on outside variables that are not
* imported with `use ()`.
*/
'use_arrow_functions' => true,
'use_arrow_functions' => false,
/*
* Visibility MUST be declared on all properties and methods;
@@ -1650,7 +1548,6 @@ $rules = [
*/
'visibility_required' => [
'elements' => [
'const',
'method',
'property',
],
@@ -1664,7 +1561,7 @@ $rules = [
* Risky!
* Modifies the signature of functions.
*/
'void_return' => true,
'void_return' => false,
/*
* In array declaration, there MUST be a whitespace after each
@@ -1686,51 +1583,19 @@ $rules = [
];
if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) {
$which = static function ($program, $default = null) {
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';
$binFixer = __DIR__ . '/vendor/bin/php-cs-fixer';
if (!is_file($binFixer)) {
$binFixer = 'php-cs-fixer';
}
$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);
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, " 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,
sprintf(
" \e[1;40;92m%s %s --force\e[K\n\e[0m\n",
basename($phpBin),
$_SERVER['argv'][0]
)
);
fwrite(\STDOUT, " \e[1;40;92mphp {$_SERVER['argv'][0]} --force\e[K\n\e[0m\n");
} 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);

View File

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

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

465
README.md
View File

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

View File

@@ -1,14 +1,5 @@
<?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')) {
define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/vendor/autoload.php');
}

View File

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

View File

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

View File

@@ -1,52 +1,48 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/** @noinspection PhpComposerExtensionStubsInspection */
namespace PhpZip\Constants;
/**
* 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[] */
private const CP_CHARSETS = [
private static $CP_CHARSETS = [
self::CP_LATIN_US,
self::CP_GREEK,
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);
@@ -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);
@@ -96,8 +98,8 @@ final class DosCodePage
/**
* @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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* General purpose bit flag constants.
*/
interface GeneralPurposeBitFlag
{
/**
* General Purpose Bit Flag mask for encrypted data.
* Bit 0: If set, indicates that the file is encrypted.
*/
public const ENCRYPTION = 1 << 0;
const ENCRYPTION = 1; // 1 << 0
/**
* Compression Flag Bit 1 for method Deflating.
@@ -30,7 +24,7 @@ interface GeneralPurposeBitFlag
*
* @see GeneralPurposeBitFlag::COMPRESSION_FLAG2
*/
public const COMPRESSION_FLAG1 = 1 << 1;
const COMPRESSION_FLAG1 = 2; // 1 << 1
/**
* Compression Flag Bit 2 for method Deflating.
@@ -43,7 +37,7 @@ interface GeneralPurposeBitFlag
*
* @see GeneralPurposeBitFlag::COMPRESSION_FLAG1
*/
public const COMPRESSION_FLAG2 = 1 << 2;
const COMPRESSION_FLAG2 = 4; // 1 << 2
/**
* General Purpose Bit Flag mask for data descriptor.
@@ -53,7 +47,7 @@ interface GeneralPurposeBitFlag
* local header. The correct values are put in the 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.
@@ -64,7 +58,7 @@ interface GeneralPurposeBitFlag
* If AES encryption is used, the version needed to extract value
* 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.
@@ -73,5 +67,5 @@ interface GeneralPurposeBitFlag
* If this bit is set, the filename and comment fields
* for this file MUST be encoded using UTF-8. (see APPENDIX D)
*/
public const UTF8 = 1 << 11;
const UTF8 = 2048; // 1 << 11
}

View File

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

View File

@@ -1,14 +1,5 @@
<?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;
/**
@@ -36,28 +27,28 @@ namespace PhpZip\Constants;
interface ZipCompressionLevel
{
/** @var int Compression level for super fast compression. */
public const SUPER_FAST = 1;
const SUPER_FAST = 1;
/** @var int compression level for fast compression */
public const FAST = 2;
const FAST = 2;
/** @var int compression level for normal compression */
public const NORMAL = 5;
const NORMAL = 5;
/** @var int compression level for maximum compression */
public const MAXIMUM = 9;
const MAXIMUM = 9;
/**
* @var int int Minimum compression level
*
* @internal
*/
public const LEVEL_MIN = self::SUPER_FAST;
const LEVEL_MIN = self::SUPER_FAST;
/**
* @var int int Maximum compression level
*
* @internal
*/
public const LEVEL_MAX = self::MAXIMUM;
const LEVEL_MAX = self::MAXIMUM;
}

View File

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

View File

@@ -1,44 +1,38 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Zip Constants.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
interface ZipConstants
{
/** @var int End Of Central Directory Record signature. */
public const END_CD = 0x06054B50; // "PK\005\006"
const END_CD = 0x06054B50; // "PK\005\006"
/** @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. */
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. */
public const CENTRAL_FILE_HEADER = 0x02014B50; // "PK\001\002"
const CENTRAL_FILE_HEADER = 0x02014B50; // "PK\001\002"
/** @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. */
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
* if ZIP64 extensions are used
*/
public const ZIP64_MAGIC = 0xFFFFFFFF;
const ZIP64_MAGIC = 0xFFFFFFFF;
/**
* Local File Header signature 4
@@ -53,7 +47,7 @@ interface ZipConstants
*
* @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.
@@ -70,13 +64,13 @@ interface ZipConstants
* file name 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 */
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 */
public const END_CD_MIN_LEN = 22;
const END_CD_MIN_LEN = 22;
/**
* 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
*/
public const ZIP64_END_OF_CD_LEN = 56;
const ZIP64_END_OF_CD_LEN = 56;
}

View File

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

View File

@@ -1,19 +1,13 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
use PhpZip\IO\ZipReader;
use PhpZip\ZipFile;
/**
* Interface ZipOptions.
*/
interface ZipOptions
{
/**
@@ -21,7 +15,7 @@ interface ZipOptions
*
* @see ZipFile::addFromFinder()
*/
public const STORE_ONLY_FILES = 'only_files';
const STORE_ONLY_FILES = 'only_files';
/**
* Uses the specified compression method.
@@ -29,7 +23,7 @@ interface ZipOptions
* @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile()
*/
public const COMPRESSION_METHOD = 'compression_method';
const COMPRESSION_METHOD = 'compression_method';
/**
* Set the specified record modification time.
@@ -39,7 +33,7 @@ interface ZipOptions
* @see ZipFile::addFromFinder()
* @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
@@ -54,7 +48,7 @@ interface ZipOptions
* @see ZipReader::getDefaultOptions()
* @see DosCodePage::getCodePages()
*/
public const CHARSET = 'charset';
const CHARSET = 'charset';
/**
* 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
*/
public const EXTRACT_SYMLINKS = 'extract_symlinks';
const EXTRACT_SYMLINKS = 'extract_symlinks';
}

View File

@@ -1,109 +1,53 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Class ZipPlatform.
*/
final class ZipPlatform
{
/** @var int MS-DOS OS */
public const OS_DOS = 0;
/** @var int Amica OS */
public const OS_AMIGA = 1;
/** @var int OpenVMS */
public const OS_OPENVMS = 2;
const OS_DOS = 0;
/** @var int Unix OS */
public const OS_UNIX = 3;
const OS_UNIX = 3;
/** @var int VM/CMS */
public const OS_VM_CMS = 4;
/** @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;
/** MacOS platform */
const OS_MAC_OSX = 19;
/** @var array Zip Platforms */
private const PLATFORMS = [
private static $platforms = [
self::OS_DOS => 'MS-DOS',
self::OS_AMIGA => 'Amiga',
self::OS_OPENVMS => 'OpenVMS',
1 => 'Amiga',
2 => 'OpenVMS',
self::OS_UNIX => 'Unix',
self::OS_VM_CMS => 'VM/CMS',
self::OS_ATARI_ST => 'Atari ST',
self::OS_OS_2 => 'HPFS (OS/2, NT 3.x)',
self::OS_MACINTOSH => 'Macintosh',
self::OS_Z_SYSTEM => 'Z-System',
self::OS_CPM => 'CP/M',
self::OS_WINDOWS_NTFS => 'Windows NTFS or TOPS-20',
self::OS_MVS => 'MVS or NTFS',
self::OS_VSE => 'VSE or SMS/QDOS',
self::OS_ACORN_RISC => 'Acorn RISC OS',
self::OS_VFAT => 'VFAT',
self::OS_ALTERNATE_MVS => 'alternate MVS',
self::OS_BEOS => 'BeOS',
self::OS_TANDEM => 'Tandem',
self::OS_OS_400 => 'OS/400',
4 => 'VM/CMS',
5 => 'Atari ST',
6 => 'HPFS (OS/2, NT 3.x)',
7 => 'Macintosh',
8 => 'Z-System',
9 => 'CP/M',
10 => 'Windows NTFS or TOPS-20',
11 => 'MVS or NTFS',
12 => 'VSE or SMS/QDOS',
13 => 'Acorn RISC OS',
14 => 'VFAT',
15 => 'alternate MVS',
16 => 'BeOS',
17 => 'Tandem',
18 => 'OS/400',
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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Constants;
/**
* Version needed to extract or software version.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT Section 4.4.3
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
interface ZipVersion
{
/** @var int 1.0 - Default value */
public const v10_DEFAULT_MIN = 10;
const v10_DEFAULT_MIN = 10;
/** @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)
@@ -31,22 +25,22 @@ interface ZipVersion
*
* @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) */
public const v21_DEFLATED64 = 21;
const v21_DEFLATED64 = 21;
/** @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 */
public const v27_PATCH_DATA = 27;
const v27_PATCH_DATA = 27;
/** @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 */
public const v46_BZIP2 = 46;
const v46_BZIP2 = 46;
/**
* 5.0 - File is encrypted using DES
@@ -56,7 +50,7 @@ interface ZipVersion
*
* @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
@@ -64,16 +58,16 @@ interface ZipVersion
*
* @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** */
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*** */
public const v61_ENCR_NON_OAE_KEY_WRAP = 61;
const v61_ENCR_NON_OAE_KEY_WRAP = 61;
/** @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
@@ -83,5 +77,5 @@ interface ZipVersion
*
* @var int
*/
public const v63_LZMA_PPMD_BLOWFISH_TWOFISH = 63;
const v63_LZMA_PPMD_BLOWFISH_TWOFISH = 63;
}

View File

@@ -1,14 +1,5 @@
<?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;
/**
@@ -17,16 +8,34 @@ namespace PhpZip\Exception;
* and the computed value from the decompressed data.
*
* The exception detail message is the name of the ZIP entry.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class Crc32Exception extends ZipException
{
/** Expected crc. */
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(
sprintf(
@@ -42,16 +51,20 @@ class Crc32Exception extends ZipException
/**
* Returns expected crc.
*
* @return int
*/
public function getExpectedCrc(): int
public function getExpectedCrc()
{
return $this->expectedCrc;
}
/**
* Returns actual crc.
*
* @return int
*/
public function getActualCrc(): int
public function getActualCrc()
{
return $this->actualCrc;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,17 +1,8 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\Pkware;
use PhpZip\Exception\ZipAuthenticationException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\ZipEntry;
/**
@@ -19,27 +10,37 @@ use PhpZip\Model\ZipEntry;
*/
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__);
}
/**
* @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'])) {
return false;
@@ -63,9 +64,9 @@ class PKDecryptionStreamFilter extends \php_user_filter
// init check byte
if ($entry->isDataDescriptorEnabled()) {
$this->checkByte = ($entry->getDosTime() >> 8) & 0xFF;
$this->checkByte = ($entry->getDosTime() >> 8) & 0xff;
} else {
$this->checkByte = ($entry->getCrc() >> 24) & 0xFF;
$this->checkByte = ($entry->getCrc() >> 24) & 0xff;
}
$this->readLength = 0;
@@ -77,17 +78,18 @@ class PKDecryptionStreamFilter extends \php_user_filter
/**
* Decryption filter.
*
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
*
* @throws ZipException
*
* @return int
*
* @todo USE FFI in php 7.4
* @noinspection PhpDocSignatureInspection
*
* @param mixed $in
* @param mixed $out
* @param mixed $consumed
* @param mixed $closing
*
* @throws ZipAuthenticationException
*/
public function filter($in, $out, &$consumed, $closing): int
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
$buffer = $bucket->data;

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\Pkware;
use PhpZip\Exception\RuntimeException;
@@ -19,27 +10,37 @@ use PhpZip\Model\ZipEntry;
*/
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__);
}
/**
* @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) {
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);
$crc = $entry->isDataDescriptorRequired() || $entry->getCrc() === ZipEntry::UNKNOWN
? ($entry->getDosTime() & 0x0000FFFF) << 16
? ($entry->getDosTime() & 0x0000ffff) << 16
: $entry->getCrc();
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);
}
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 1] = pack('c', ($crc >> 24) & 0xFF);
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 2] = pack('c', ($crc >> 16) & 0xFF);
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 1] = pack('c', ($crc >> 24) & 0xff);
$headerBytes[PKCryptContext::STD_DEC_HDR_SIZE - 2] = pack('c', ($crc >> 16) & 0xff);
$this->headerBytes = $headerBytes;
$this->writeLength = 0;
@@ -88,16 +89,16 @@ class PKEncryptionStreamFilter extends \php_user_filter
/**
* Encryption filter.
*
* @param resource $in
* @param resource $out
* @param int $consumed
* @param bool $closing
*
* @return int
*
* @todo USE FFI in php 7.4
*
* @noinspection PhpDocSignatureInspection
*
* @param mixed $in
* @param mixed $out
* @param mixed $consumed
* @param mixed $closing
*/
public function filter($in, $out, &$consumed, $closing): int
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
$buffer = $bucket->data;

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Filter\Cipher\WinZipAes;
use PhpZip\Exception\RuntimeException;
@@ -20,27 +11,37 @@ use PhpZip\Model\ZipEntry;
*/
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 bool
*
* @noinspection DuplicatedCode
*/
public function onCreate(): bool
public function onCreate()
{
if (!isset($this->params['entry'])) {
return false;
@@ -66,7 +67,15 @@ class WinZipAesEncryptionStreamFilter extends \php_user_filter
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)) {
$this->buffer .= $bucket->data;
@@ -89,7 +98,6 @@ class WinZipAesEncryptionStreamFilter extends \php_user_filter
if ($winZipExtra === null) {
throw new RuntimeException('$winZipExtra is null');
}
/** @psalm-var positive-int $saltSize */
$saltSize = $winZipExtra->getSaltSize();
try {

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\IO\Stream;
use Psr\Http\Message\StreamInterface;
@@ -19,7 +10,7 @@ use Psr\Http\Message\StreamInterface;
class ResponseStream implements StreamInterface
{
/** @var array */
private const READ_WRITE_MAP = [
private static $readWriteHash = [
'read' => [
'r' => true,
'w+' => true,
@@ -59,18 +50,23 @@ class ResponseStream implements StreamInterface
],
];
/** @var resource|null */
/** @var resource */
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
@@ -85,24 +81,17 @@ class ResponseStream implements StreamInterface
$this->stream = $stream;
$meta = stream_get_meta_data($this->stream);
$this->seekable = $meta['seekable'];
$this->readable = isset(self::READ_WRITE_MAP['read'][$meta['mode']]);
$this->writable = isset(self::READ_WRITE_MAP['write'][$meta['mode']]);
$this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
$this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
$this->uri = $this->getMetadata('uri');
}
/**
* {@inheritDoc}
*
* @noinspection PhpMissingReturnTypeInspection
* Closes the stream when the destructed.
*/
public function getMetadata($key = null)
public function __destruct()
{
if ($this->stream === null) {
return $key ? null : [];
}
$meta = stream_get_meta_data($this->stream);
return $meta[$key] ?? null;
$this->close();
}
/**
@@ -117,8 +106,10 @@ class ResponseStream implements StreamInterface
* string casting operations.
*
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
*
* @return string
*/
public function __toString(): string
public function __toString()
{
if (!$this->stream) {
return '';
@@ -128,6 +119,30 @@ class ResponseStream implements StreamInterface
return (string) stream_get_contents($this->stream);
}
/**
* Get stream metadata as an associative array or retrieve a specific key.
*
* The keys returned are identical to the keys returned from PHP's
* stream_get_meta_data() function.
*
* @see http://php.net/manual/en/function.stream-get-meta-data.php
*
* @param string $key specific metadata to retrieve
*
* @return array|mixed|null Returns an associative array if no key is
* provided. Returns a specific key value if a key is provided and the
* value is found, or null if the key is not found.
*/
public function getMetadata($key = null)
{
if (!$this->stream) {
return $key ? null : [];
}
$meta = stream_get_meta_data($this->stream);
return $meta[$key] ?? null;
}
/**
* Seek to the beginning of the stream.
*
@@ -139,9 +154,9 @@ class ResponseStream implements StreamInterface
* @see http://www.php.net/manual/en/function.fseek.php
* @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
*/
public function getSize(): ?int
public function getSize()
{
if ($this->size !== null) {
return $this->size;
}
if (!$this->stream) {
return null;
return;
}
// Clear the stat cache if the stream has a URI
if ($this->uri !== null) {
@@ -169,10 +184,15 @@ class ResponseStream implements StreamInterface
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()
{
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.
*
* @return bool
*/
public function eof(): bool
public function eof()
{
return !$this->stream || feof($this->stream);
}
/**
* Returns whether or not the stream is seekable.
*
* @return bool
*/
public function isSeekable(): bool
public function isSeekable()
{
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.
*
* @return bool
*/
public function isWritable(): bool
public function isWritable()
{
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)
{
$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.
*
* @return bool
*/
public function isReadable(): bool
public function isReadable()
{
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
* reading
*
* @return string
*/
public function getContents(): string
public function getContents()
{
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.
*
* @psalm-suppress InvalidPropertyAssignmentValue
*/
public function close(): void
public function close()
{
if (\is_resource($this->stream)) {
fclose($this->stream);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?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;
/**
@@ -25,230 +16,12 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
*
* @var ZipExtraField[]
*/
protected array $collection = [];
protected $collection = [];
/**
* Returns the number of Extra Fields in this collection.
* @return string
*/
public function count(): int
{
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
public function __toString()
{
$formats = [];
@@ -268,4 +41,234 @@ class ExtraFieldsCollection implements \ArrayAccess, \Countable, \Iterator
$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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -20,33 +11,45 @@ use PhpZip\Model\ZipEntry;
*/
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->unicodeValue = $unicodeValue;
$this->crc32 = (int) $crc32;
$this->unicodeValue = (string) $unicodeValue;
}
/**
* @return int the CRC32 checksum of the filename or comment as
* encoded in the central directory of the zip file
*/
public function getCrc32(): int
public function getCrc32()
{
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;
}
@@ -54,7 +57,7 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
/**
* @param string $unicodeValue the UTF-8 encoded name to set
*/
public function setUnicodeValue(string $unicodeValue): void
public function setUnicodeValue($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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException on error
*
* @return static
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
if (\strlen($buffer) < 5) {
throw new ZipException('Unicode path extra data must have at least 5 bytes.');
}
[
'version' => $version,
'crc32' => $crc32,
] = unpack('Cversion/Vcrc32', $buffer);
$data = unpack('Cversion/Vcrc32', $buffer);
if ($version !== self::DEFAULT_VERSION) {
throw new ZipException(sprintf('Unsupported version [%d] for Unicode path extra data.', $version));
if ($data['version'] !== self::DEFAULT_VERSION) {
throw new ZipException(sprintf('Unsupported version [%d] for Unicode path extra data.', $data['version']));
}
$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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException on error
*
* @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);
}
@@ -110,7 +110,7 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
return pack(
'CV',
@@ -126,7 +126,7 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
return $this->packLocalFileData();
}

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -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://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
@@ -29,65 +20,104 @@ final class ApkAlignmentExtraField implements ZipExtraField
* well as for aligning the entries's data. See ZIP
* 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 */
public const ALIGNMENT_BYTES = 4;
const ALIGNMENT_BYTES = 4;
/** @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->padding = $padding;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x APK Alignment: Multiple=%d Padding=%d',
self::HEADER_ID,
$this->multiple,
$this->padding
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId(): int
public function getHeaderId()
{
return self::HEADER_ID;
}
public function getMultiple(): int
/**
* @return int
*/
public function getMultiple()
{
return $this->multiple;
}
public function getPadding(): int
/**
* @return int
*/
public function getPadding()
{
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @return ApkAlignmentExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
$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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException on error
*
* @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);
}
@@ -128,7 +158,7 @@ final class ApkAlignmentExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
return pack('vx' . $this->padding, $this->multiple);
}
@@ -139,18 +169,8 @@ final class ApkAlignmentExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Constants\UnixStat;
@@ -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
*/
final class AsiExtraField implements ZipExtraField
class AsiExtraField implements ZipExtraField
{
/** @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). */
public const PERM_MASK = 07777;
const PERM_MASK = 07777;
/** @var int Standard Unix stat(2) file mode. */
private int $mode;
private $mode;
/** @var int User ID. */
private int $uid;
private $uid;
/** @var int Group ID. */
private int $gid;
private $gid;
/**
* @var string File this entry points to, if it is 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->uid = $uid;
@@ -86,12 +85,29 @@ final class AsiExtraField implements ZipExtraField
$this->link = $link;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x ASI: Mode=%o UID=%d GID=%d Link="%s',
self::HEADER_ID,
$this->mode,
$this->uid,
$this->gid,
$this->link
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId(): int
public function getHeaderId()
{
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @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];
$buffer = substr($buffer, 4);
@@ -116,32 +132,27 @@ final class AsiExtraField implements ZipExtraField
throw new Crc32Exception('Asi Unix Extra Filed Data', $givenChecksum, $realChecksum);
}
[
'mode' => $mode,
'linkSize' => $linkSize,
'uid' => $uid,
'gid' => $gid,
] = unpack('vmode/VlinkSize/vuid/vgid', $buffer);
$data = unpack('vmode/VlinkSize/vuid/vgid', $buffer);
$link = '';
if ($linkSize > 0) {
if ($data['linkSize'] > 0) {
$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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws Crc32Exception
*
* @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);
}
@@ -152,7 +163,7 @@ final class AsiExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
$data = pack(
'vVvv',
@@ -171,7 +182,7 @@ final class AsiExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
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
* symbolic link, the empty string otherwise
*/
public function getLink(): string
public function getLink()
{
return $this->link;
}
@@ -193,9 +204,9 @@ final class AsiExtraField implements ZipExtraField
* @param string $link name of the file this entry links to, empty
* string if it is not a symbolic link
*/
public function setLink(string $link): void
public function setLink($link)
{
$this->link = $link;
$this->link = (string) $link;
$this->mode = $this->getPermissionsMode($this->mode);
}
@@ -204,7 +215,7 @@ final class AsiExtraField implements ZipExtraField
*
* @return bool true if this is a symbolic link
*/
public function isLink(): bool
public function isLink()
{
return !empty($this->link);
}
@@ -216,7 +227,7 @@ final class AsiExtraField implements ZipExtraField
*
* @return int the type with the mode
*/
private function getPermissionsMode(int $mode): int
protected function getPermissionsMode($mode)
{
$type = 0;
@@ -236,50 +247,56 @@ final class AsiExtraField implements ZipExtraField
*
* @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();
}
public function getMode(): int
/**
* @return int
*/
public function getMode()
{
return $this->mode;
}
public function setMode(int $mode): void
/**
* @param int $mode
*/
public function setMode($mode)
{
$this->mode = $this->getPermissionsMode($mode);
}
public function getUserId(): int
/**
* @return int
*/
public function getUserId()
{
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;
}
public function setGroupId(int $gid): void
/**
* @param int $gid
*/
public function setGroupId($gid)
{
$this->gid = $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
);
$this->gid = (int) $gid;
}
}

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Model\Extra\ZipExtraField;
@@ -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
*/
final class ExtendedTimestampExtraField implements ZipExtraField
class ExtendedTimestampExtraField implements ZipExtraField
{
/** @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
* 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
* 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
* 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
* are ignored according to the current version of the spec (December 2012).
*/
private int $flags;
private $flags;
/** @var int|null Modify time */
private ?int $modifyTime;
private $modifyTime;
/** @var int|null Access time */
private ?int $accessTime;
private $accessTime;
/** @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->accessTime = $accessTime;
$this->createTime = $createTime;
}
/**
* @param ?int $modifyTime
* @param ?int $accessTime
* @param ?int $createTime
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x ExtendedTimestamp:';
if ($this->modifyTime !== null) {
$format .= ' Modify:[%s]';
$args[] = date(\DATE_W3C, $this->modifyTime);
}
if ($this->accessTime !== null) {
$format .= ' Access:[%s]';
$args[] = date(\DATE_W3C, $this->accessTime);
}
if ($this->createTime !== null) {
$format .= ' Create:[%s]';
$args[] = date(\DATE_W3C, $this->createTime);
}
return vsprintf($format, $args);
}
/**
* @param int|null $modifyTime
* @param int|null $accessTime
* @param int|null $createTime
*
* @return ExtendedTimestampExtraField
*/
public static function create(?int $modifyTime, ?int $accessTime, ?int $createTime): self
public static function create($modifyTime, $accessTime, $createTime)
{
$flags = 0;
if ($modifyTime !== null) {
$modifyTime = (int) $modifyTime;
$flags |= self::MODIFY_TIME_BIT;
}
if ($accessTime !== null) {
$accessTime = (int) $accessTime;
$flags |= self::ACCESS_TIME_BIT;
}
if ($createTime !== null) {
$createTime = (int) $createTime;
$flags |= self::CREATE_TIME_BIT;
}
@@ -149,8 +175,10 @@ final class ExtendedTimestampExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId(): int
public function getHeaderId()
{
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @return ExtendedTimestampExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
$length = \strlen($buffer);
$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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @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);
}
@@ -211,7 +239,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
$data = '';
@@ -239,7 +267,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
$cdLength = 1 + ($this->modifyTime !== null ? 4 : 0);
@@ -261,7 +289,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
* @return int flags byte indicating which of the
* three datestamp fields are present
*/
public function getFlags(): int
public function getFlags()
{
return $this->flags;
}
@@ -272,7 +300,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return int|null modify time (seconds since epoch) or null
*/
public function getModifyTime(): ?int
public function getModifyTime()
{
return $this->modifyTime;
}
@@ -283,7 +311,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return int|null access time (seconds since epoch) or null
*/
public function getAccessTime(): ?int
public function getAccessTime()
{
return $this->accessTime;
}
@@ -299,7 +327,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return int|null create time (seconds since epoch) or null
*/
public function getCreateTime(): ?int
public function getCreateTime()
{
return $this->createTime;
}
@@ -312,7 +340,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return \DateTimeInterface|null modify time as \DateTimeInterface or null
*/
public function getModifyDateTime(): ?\DateTimeInterface
public function getModifyDateTime()
{
return self::timestampToDateTime($this->modifyTime);
}
@@ -325,7 +353,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return \DateTimeInterface|null access time as \DateTimeInterface or null
*/
public function getAccessDateTime(): ?\DateTimeInterface
public function getAccessDateTime()
{
return self::timestampToDateTime($this->accessTime);
}
@@ -343,7 +371,7 @@ final class ExtendedTimestampExtraField implements ZipExtraField
*
* @return \DateTimeInterface|null create time as \DateTimeInterface or null
*/
public function getCreateDateTime(): ?\DateTimeInterface
public function getCreateDateTime()
{
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
*/
public function setModifyTime(?int $unixTime): void
public function setModifyTime($unixTime)
{
$this->modifyTime = $unixTime;
$this->updateFlags();
}
private function updateFlags(): void
private function updateFlags()
{
$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
*/
public function setAccessTime(?int $unixTime): void
public function setAccessTime($unixTime)
{
$this->accessTime = $unixTime;
$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
*/
public function setCreateTime(?int $unixTime): void
public function setCreateTime($unixTime)
{
$this->createTime = $unixTime;
$this->updateFlags();
}
private static function timestampToDateTime(?int $timestamp): ?\DateTimeInterface
/**
* @param int|null $timestamp
*
* @return \DateTimeInterface|null
*/
private static function timestampToDateTime($timestamp)
{
try {
return $timestamp !== null ? new \DateTimeImmutable('@' . $timestamp) : null;
} 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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -23,13 +14,26 @@ use PhpZip\Model\ZipEntry;
* by the extra field in the first file, which is hexadecimal in the 0xCAFE bytes series.
* If this extra field is added as the very first extra field of
* the archive, Solaris will consider it an executable jar file.
*
* @license MIT
*/
final class JarMarkerExtraField implements ZipExtraField
class JarMarkerExtraField implements ZipExtraField
{
/** @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();
@@ -48,8 +52,10 @@ final class JarMarkerExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId(): int
public function getHeaderId()
{
return self::HEADER_ID;
}
@@ -60,7 +66,7 @@ final class JarMarkerExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
return '';
}
@@ -71,7 +77,7 @@ final class JarMarkerExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
return '';
}
@@ -80,13 +86,13 @@ final class JarMarkerExtraField implements ZipExtraField
* Populate data from this array as if it was in local file data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException on error
*
* @return JarMarkerExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
if (!empty($buffer)) {
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException on error
*
* @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);
}
public function __toString(): string
{
return sprintf('0x%04x Jar Marker', self::HEADER_ID);
}
}

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\ZipException;
@@ -51,36 +42,58 @@ use PhpZip\Model\ZipEntry;
* and this extra field are present, the values in this extra field
* supercede the values in that extra field.
*/
final class NewUnixExtraField implements ZipExtraField
class NewUnixExtraField implements ZipExtraField
{
/** @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. */
public const USER_GID_PID = 1000;
const USER_GID_PID = 1000;
/** @var int version of this extra field, currently 1 */
private int $version;
private $version = 1;
/** @var int User id */
private int $uid;
private $uid;
/** @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->uid = $uid;
$this->gid = $gid;
$this->version = (int) $version;
$this->uid = (int) $uid;
$this->gid = (int) $gid;
}
/**
* @return string
*/
public function __toString()
{
return sprintf(
'0x%04x NewUnix: UID=%d GID=%d',
self::HEADER_ID,
$this->uid,
$this->gid
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId(): int
public function getHeaderId()
{
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @return NewUnixExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
$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));
}
$offset = 0;
[
'version' => $version,
'uidSize' => $uidSize,
] = unpack('Cversion/CuidSize', $buffer);
$data = unpack('Cversion/CuidSize', $buffer);
$offset += 2;
$uidSize = $data['uidSize'];
$gid = self::readSizeIntegerLE(substr($buffer, $offset, $uidSize), $uidSize);
$offset += $uidSize;
$gidSize = unpack('C', $buffer[$offset])[1];
$offset++;
$uid = self::readSizeIntegerLE(substr($buffer, $offset, $gidSize), $gidSize);
return new self($version, $gid, $uid);
return new self($data['version'], $gid, $uid);
}
/**
* Populate data from this array as if it was in central directory data.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @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);
}
@@ -138,7 +149,7 @@ final class NewUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
return pack(
'CCVCV',
@@ -156,15 +167,20 @@ final class NewUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
return $this->packLocalFileData();
}
/**
* @param string $data
* @param int $size
*
* @throws ZipException
*
* @return int
*/
private static function readSizeIntegerLE(string $data, int $size): int
private static function readSizeIntegerLE($data, $size)
{
$format = [
1 => 'C', // unsigned byte
@@ -179,38 +195,43 @@ final class NewUnixExtraField implements ZipExtraField
return unpack($format[$size], $data)[1];
}
public function getUid(): int
/**
* @return int
*/
public function getUid()
{
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;
}
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;
}
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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/**
* NTFS Extra Field.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
* @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 */
public const HEADER_ID = 0x000A;
const HEADER_ID = 0x000a;
/** @var int Tag ID */
public const TIME_ATTR_TAG = 0x0001;
const TIME_ATTR_TAG = 0x0001;
/** @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
@@ -38,231 +32,33 @@ final class NtfsExtraField implements ZipExtraField
* A.M. January 1, 1601 Coordinated Universal Time (UTC).
* this is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
*/
public const EPOCH_OFFSET = -116444736000000000;
const EPOCH_OFFSET = -116444736000000000;
/** @var int Modify ntfs time */
private int $modifyNtfsTime;
private $modifyNtfsTime;
/** @var int Access ntfs time */
private int $accessNtfsTime;
private $accessNtfsTime;
/** @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->accessNtfsTime = $accessNtfsTime;
$this->createNtfsTime = $createNtfsTime;
$this->modifyNtfsTime = (int) $modifyNtfsTime;
$this->accessNtfsTime = (int) $accessNtfsTime;
$this->createNtfsTime = (int) $createNtfsTime;
}
/**
* @return NtfsExtraField
* @return string
*/
public static function create(
\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
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x NtfsExtra:';
@@ -284,4 +80,260 @@ final class NtfsExtraField implements ZipExtraField
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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Model\Extra\ZipExtraField;
@@ -62,24 +53,30 @@ use PhpZip\Model\ZipEntry;
* mid-1994. Therefore future archiving software should continue to
* support it.
*/
final class OldUnixExtraField implements ZipExtraField
class OldUnixExtraField implements ZipExtraField
{
/** @var int Header id */
public const HEADER_ID = 0x5855;
const HEADER_ID = 0x5855;
/** @var int|null Access timestamp */
private ?int $accessTime;
private $accessTime;
/** @var int|null Modify timestamp */
private ?int $modifyTime;
private $modifyTime;
/** @var int|null User id */
private ?int $uid;
private $uid;
/** @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->modifyTime = $modifyTime;
@@ -87,12 +84,45 @@ final class OldUnixExtraField implements ZipExtraField
$this->gid = $gid;
}
/**
* @return string
*/
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x OldUnix:';
if (($modifyTime = $this->getModifyDateTime()) !== null) {
$format .= ' Modify:[%s]';
$args[] = $modifyTime->format(\DATE_ATOM);
}
if (($accessTime = $this->getAccessDateTime()) !== null) {
$format .= ' Access:[%s]';
$args[] = $accessTime->format(\DATE_ATOM);
}
if ($this->uid !== null) {
$format .= ' UID=%d';
$args[] = $this->uid;
}
if ($this->gid !== null) {
$format .= ' GID=%d';
$args[] = $this->gid;
}
return vsprintf($format, $args);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId(): int
public function getHeaderId()
{
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @return OldUnixExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
$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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
* @param ZipEntry|null $entry
*
* @return OldUnixExtraField
*/
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
{
$length = \strlen($buffer);
@@ -161,7 +191,7 @@ final class OldUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
$data = '';
@@ -190,7 +220,7 @@ final class OldUnixExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
$data = '';
@@ -205,91 +235,93 @@ final class OldUnixExtraField implements ZipExtraField
return $data;
}
public function getAccessTime(): ?int
/**
* @return int|null
*/
public function getAccessTime()
{
return $this->accessTime;
}
public function setAccessTime(?int $accessTime): void
/**
* @param int|null $accessTime
*/
public function setAccessTime($accessTime)
{
$this->accessTime = $accessTime;
}
public function getAccessDateTime(): ?\DateTimeInterface
/**
* @return \DateTimeInterface|null
*/
public function getAccessDateTime()
{
try {
return $this->accessTime === null ? null
: new \DateTimeImmutable('@' . $this->accessTime);
} catch (\Exception $e) {
return null;
return;
}
}
public function getModifyTime(): ?int
/**
* @return int|null
*/
public function getModifyTime()
{
return $this->modifyTime;
}
public function setModifyTime(?int $modifyTime): void
/**
* @param int|null $modifyTime
*/
public function setModifyTime($modifyTime)
{
$this->modifyTime = $modifyTime;
}
public function getModifyDateTime(): ?\DateTimeInterface
/**
* @return \DateTimeInterface|null
*/
public function getModifyDateTime()
{
try {
return $this->modifyTime === null ? null
: new \DateTimeImmutable('@' . $this->modifyTime);
} catch (\Exception $e) {
return null;
return;
}
}
public function getUid(): ?int
/**
* @return int|null
*/
public function getUid()
{
return $this->uid;
}
public function setUid(?int $uid): void
/**
* @param int|null $uid
*/
public function setUid($uid)
{
$this->uid = $uid;
}
public function getGid(): ?int
/**
* @return int|null
*/
public function getGid()
{
return $this->gid;
}
public function setGid(?int $gid): void
/**
* @param int|null $gid
*/
public function setGid($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
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;
/**
@@ -55,21 +46,14 @@ namespace PhpZip\Model\Extra\Fields;
*
* @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.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
* @return string
*/
public function getHeaderId(): int
{
return self::HEADER_ID;
}
public function __toString(): string
public function __toString()
{
return sprintf(
'0x%04x UnicodeComment: "%s"',
@@ -77,4 +61,16 @@ final class UnicodeCommentExtraField extends AbstractUnicodeExtraField
$this->getUnicodeValue()
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
{
return self::HEADER_ID;
}
}

View File

@@ -1,14 +1,5 @@
<?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;
/**
@@ -56,21 +47,14 @@ namespace PhpZip\Model\Extra\Fields;
*
* @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.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
* @return string
*/
public function getHeaderId(): int
{
return self::HEADER_ID;
}
public function __toString(): string
public function __toString()
{
return sprintf(
'0x%04x UnicodePath: "%s"',
@@ -78,4 +62,16 @@ final class UnicodePathExtraField extends AbstractUnicodeExtraField
$this->getUnicodeValue()
);
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId()
{
return self::HEADER_ID;
}
}

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\RuntimeException;
@@ -18,20 +9,41 @@ use PhpZip\Model\ZipEntry;
/**
* 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 */
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->data = $data;
$this->headerId = (int) $headerId;
$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;
}
@@ -40,8 +52,10 @@ final class UnrecognizedExtraField implements ZipExtraField
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
*
* @return int
*/
public function getHeaderId(): int
public function getHeaderId()
{
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
*
* @return UnrecognizedExtraField
* @param ZipEntry|null $entry
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
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.
*
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry optional zip entry
*
* @return UnrecognizedExtraField
* @param ZipEntry|null $entry
*/
public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
{
throw new RuntimeException('Unsupport parse');
}
@@ -75,7 +85,7 @@ final class UnrecognizedExtraField implements ZipExtraField
/**
* {@inheritDoc}
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
return $this->data;
}
@@ -83,26 +93,24 @@ final class UnrecognizedExtraField implements ZipExtraField
/**
* {@inheritDoc}
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
return $this->data;
}
public function getData(): string
/**
* @return string
*/
public function getData()
{
return $this->data;
}
public function setData(string $data): void
/**
* @param string $data
*/
public function setData($data)
{
$this->data = $data;
}
public function __toString(): string
{
$args = [$this->headerId, $this->data];
$format = '0x%04x Unrecognized Extra Field: "%s"';
return vsprintf($format, $args);
$this->data = (string) $data;
}
}

View File

@@ -1,14 +1,5 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Constants\ZipCompressionMethod;
@@ -24,72 +15,72 @@ use PhpZip\Model\ZipEntry;
*
* @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 */
public const HEADER_ID = 0x9901;
const HEADER_ID = 0x9901;
/**
* @var int Data size (currently 7, but subject to possible increase
* 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
* 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.
* 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.
* 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 */
public const KEY_STRENGTH_128BIT = 0x01;
const KEY_STRENGTH_128BIT = 0x01;
/** @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 */
public const KEY_STRENGTH_256BIT = 0x03;
const KEY_STRENGTH_256BIT = 0x03;
/** @var int[] */
private const ALLOW_VENDOR_VERSIONS = [
private static $allowVendorVersions = [
self::VERSION_AE1,
self::VERSION_AE2,
];
/** @var array<int, int> */
private const ENCRYPTION_STRENGTHS = [
private static $encryptionStrengths = [
self::KEY_STRENGTH_128BIT => 128,
self::KEY_STRENGTH_192BIT => 192,
self::KEY_STRENGTH_256BIT => 256,
];
/** @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_192BIT => ZipEncryptionMethod::WINZIP_AES_192,
self::KEY_STRENGTH_256BIT => ZipEncryptionMethod::WINZIP_AES_256,
];
/** @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 */
private int $keyStrength = self::KEY_STRENGTH_256BIT;
private $keyStrength = self::KEY_STRENGTH_256BIT;
/** @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
@@ -98,7 +89,7 @@ final class WinZipAesExtraField implements ZipExtraField
*
* @throws ZipUnsupportMethodException
*/
public function __construct(int $vendorVersion, int $keyStrength, int $compressionMethod)
public function __construct($vendorVersion, $keyStrength, $compressionMethod)
{
$this->setVendorVersion($vendorVersion);
$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
*
* @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) {
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.
* 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(): int
public function getHeaderId()
{
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.
*
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException on error
*
* @return WinZipAesExtraField
*/
public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
$size = \strlen($buffer);
@@ -173,37 +182,36 @@ final class WinZipAesExtraField implements ZipExtraField
);
}
[
'vendorVersion' => $vendorVersion,
'vendorId' => $vendorId,
'keyStrength' => $keyStrength,
'compressionMethod' => $compressionMethod,
] = unpack('vvendorVersion/vvendorId/ckeyStrength/vcompressionMethod', $buffer);
$data = unpack('vvendorVersion/vvendorId/ckeyStrength/vcompressionMethod', $buffer);
if ($vendorId !== self::VENDOR_ID) {
if ($data['vendorId'] !== self::VENDOR_ID) {
throw new ZipException(
sprintf(
'Vendor id invalid: %d. Must be %d',
$vendorId,
$data['vendorId'],
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.
*
* @param string $buffer the buffer to read data from
* @param ?ZipEntry $entry
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @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);
}
@@ -214,7 +222,7 @@ final class WinZipAesExtraField implements ZipExtraField
*
* @return string the data
*/
public function packLocalFileData(): string
public function packLocalFileData()
{
return pack(
'vvcv',
@@ -231,7 +239,7 @@ final class WinZipAesExtraField implements ZipExtraField
*
* @return string the data
*/
public function packCentralDirData(): string
public function packCentralDirData()
{
return $this->packLocalFileData();
}
@@ -239,10 +247,12 @@ final class WinZipAesExtraField implements ZipExtraField
/**
* Returns the vendor version.
*
* @return int
*
* @see WinZipAesExtraField::VERSION_AE2
* @see WinZipAesExtraField::VERSION_AE1
*/
public function getVendorVersion(): int
public function getVendorVersion()
{
return $this->vendorVersion;
}
@@ -255,9 +265,11 @@ final class WinZipAesExtraField implements ZipExtraField
* @see WinZipAesExtraField::VERSION_AE2
* @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(
sprintf(
'Unsupport WinZip AES vendor version: %d',
@@ -270,87 +282,106 @@ final class WinZipAesExtraField implements ZipExtraField
/**
* Returns vendor id.
*
* @return int
*/
public function getVendorId(): int
public function getVendorId()
{
return self::VENDOR_ID;
}
public function getKeyStrength(): int
/**
* @return int
*/
public function getKeyStrength()
{
return $this->keyStrength;
}
/**
* 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(
sprintf(
'Key strength %d not support value. Allow values: %s',
$keyStrength,
implode(', ', array_keys(self::ENCRYPTION_STRENGTHS))
implode(', ', array_keys(self::$encryptionStrengths))
)
);
}
$this->keyStrength = $keyStrength;
}
public function getCompressionMethod(): int
/**
* @return int
*/
public function getCompressionMethod()
{
return $this->compressionMethod;
}
/**
* @param int $compressionMethod
*
* @throws ZipUnsupportMethodException
*/
public function setCompressionMethod(int $compressionMethod): void
public function setCompressionMethod($compressionMethod)
{
$compressionMethod = (int) $compressionMethod;
ZipCompressionMethod::checkSupport($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();
if (!isset(self::MAP_KEY_STRENGTH_METHODS[$keyStrength])) {
if (!isset(self::$MAP_KEY_STRENGTH_METHODS[$keyStrength])) {
throw new InvalidArgumentException('Invalid encryption method');
}
return self::MAP_KEY_STRENGTH_METHODS[$keyStrength];
return self::$MAP_KEY_STRENGTH_METHODS[$keyStrength];
}
public function isV1(): bool
/**
* @return bool
*/
public function isV1()
{
return $this->vendorVersion === self::VERSION_AE1;
}
public function isV2(): bool
/**
* @return bool
*/
public function isV2()
{
return $this->vendorVersion === self::VERSION_AE2;
}
public function getSaltSize(): int
/**
* @return int
*/
public function getSaltSize()
{
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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Constants\ZipConstants;
@@ -16,30 +7,43 @@ use PhpZip\Exception\RuntimeException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
/**
* ZIP64 Extra Field.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*/
final class Zip64ExtraField implements ZipExtraField
class Zip64ExtraField implements ZipExtraField
{
/** @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(
?int $uncompressedSize = null,
?int $compressedSize = null,
?int $localHeaderOffset = null,
?int $diskStart = null
$uncompressedSize = null,
$compressedSize = null,
$localHeaderOffset = null,
$diskStart = null
) {
$this->uncompressedSize = $uncompressedSize;
$this->compressedSize = $compressedSize;
@@ -48,204 +52,9 @@ final class Zip64ExtraField implements ZipExtraField
}
/**
* Returns the Header ID (type) of this Extra Field.
* The Header ID is an unsigned short integer (two bytes)
* which must be constant during the life cycle of this object.
* @return string
*/
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 $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
public function __toString()
{
$args = [self::HEADER_ID];
$format = '0x%04x ZIP64: ';
@@ -274,4 +83,229 @@ final class Zip64ExtraField implements ZipExtraField
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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Model\Extra;
use PhpZip\Exception\InvalidArgumentException;
@@ -31,9 +22,9 @@ final class ZipExtraDriver
{
/**
* @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,
AsiExtraField::HEADER_ID => AsiExtraField::class,
ExtendedTimestampExtraField::HEADER_ID => ExtendedTimestampExtraField::class,
@@ -54,7 +45,7 @@ final class ZipExtraDriver
/**
* @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)) {
throw new InvalidArgumentException(
@@ -70,8 +61,10 @@ final class ZipExtraDriver
/**
* @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;
@@ -92,12 +85,21 @@ final class ZipExtraDriver
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);
}
return self::$implementations[$headerId] ?? null;
if (isset(self::$implementations[$headerId])) {
return self::$implementations[$headerId];
}
}
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,14 +1,5 @@
<?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;
/**
@@ -29,6 +20,9 @@ namespace PhpZip\Util;
*
* @see https://docs.microsoft.com/ru-ru/windows/win32/api/winbase/nf-winbase-filetimetodosdatetime?redirectedfrom=MSDN
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*
* @internal
*/
class DateTimeConverter
@@ -36,18 +30,14 @@ class DateTimeConverter
/**
* Smallest supported DOS date/time value in a ZIP file,
* which is January 1st, 1980 AD 00:00:00 local time.
*
* @var int
*/
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,
* 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.
@@ -56,7 +46,7 @@ class DateTimeConverter
*
* @return int Unix timestamp
*/
public static function msDosToUnix(int $dosTime): int
public static function msDosToUnix($dosTime)
{
if ($dosTime <= self::MIN_DOS_TIME) {
$dosTime = 0;
@@ -65,12 +55,12 @@ class DateTimeConverter
}
// date_default_timezone_set('UTC');
return mktime(
(($dosTime >> 11) & 0x1F), // hours
(($dosTime >> 5) & 0x3F), // minutes
(($dosTime << 1) & 0x3E), // seconds
(($dosTime >> 21) & 0x0F), // month
(($dosTime >> 16) & 0x1F), // day
((($dosTime >> 25) & 0x7F) + 1980) // year
(($dosTime >> 11) & 0x1f), // hours
(($dosTime >> 5) & 0x3f), // minutes
(($dosTime << 1) & 0x3e), // seconds
(($dosTime >> 21) & 0x0f), // month
(($dosTime >> 16) & 0x1f), // day
((($dosTime >> 25) & 0x7f) + 1980) // year
);
}
@@ -84,7 +74,7 @@ class DateTimeConverter
* rounded down to even seconds
* and is in between DateTimeConverter::MIN_DOS_TIME and DateTimeConverter::MAX_DOS_TIME
*/
public static function unixToMsDos(int $unixTimestamp): int
public static function unixToMsDos($unixTimestamp)
{
if ($unixTimestamp < 0) {
throw new \InvalidArgumentException('Negative unix timestamp: ' . $unixTimestamp);

View File

@@ -1,29 +1,28 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util;
use PhpZip\Constants\DosAttrs;
use PhpZip\Constants\UnixStat;
/**
* Class FileAttribUtil.
*
* @internal
*/
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_ARCHIVE) === self::DOS_ARCHIVE) ? 'a' : '-';
$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 = '';
$permission = (int) $permission;
switch ($permission & self::UNX_IFMT) {
case self::UNX_IFDIR:
$mode .= 'd';
break;
case self::UNX_IFREG:
$mode .= '-';
break;
case self::UNX_IFLNK:
$mode .= 'l';
break;
case self::UNX_IFBLK:
$mode .= 'b';
break;
case self::UNX_IFCHR:
$mode .= 'c';
break;
case self::UNX_IFIFO:
$mode .= 'p';
break;
case self::UNX_IFSOCK:
$mode .= 's';
break;
default:
$mode .= '?';
break;
}
$mode .= ($permission & self::UNX_IRUSR) ? 'r' : '-';

View File

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

View File

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

View File

@@ -1,26 +1,28 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Util\Iterator;
use PhpZip\Util\StringUtil;
/**
* Recursive iterator for ignore files.
*
* @author Ne-Lexa alexey@nelexa.ru
* @license MIT
*/
class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
{
/** Ignore list files. */
private array $ignoreFiles = ['..'];
/**
* Ignore list files.
*
* @var array
*/
private $ignoreFiles = ['..'];
/**
* @param \RecursiveIterator $iterator
* @param array $ignoreFiles
*/
public function __construct(\RecursiveIterator $iterator, array $ignoreFiles)
{
parent::__construct($iterator);
@@ -33,8 +35,10 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
* @see http://php.net/manual/en/filteriterator.accept.php
*
* @return bool true if the current element is acceptable, otherwise false
*
* @since 5.1.0
*/
public function accept(): bool
public function accept()
{
/**
* @var \SplFileInfo $fileInfo
@@ -62,10 +66,8 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
/**
* @return IgnoreFilesRecursiveFilterIterator
* @psalm-suppress UndefinedInterfaceMethod
* @noinspection PhpPossiblePolymorphicInvocationInspection
*/
public function getChildren(): self
public function getChildren()
{
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
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;
/**
@@ -18,18 +9,40 @@ namespace PhpZip\Util;
*/
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;
}
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
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests;
use PhpZip\Exception\ZipEntryNotFoundException;
@@ -35,7 +26,7 @@ final class CustomZipFormatTest extends ZipTestCase
*
* @see http://www.epubtest.org/test-books source epub files
*/
public function testEpub(): void
public function testEpub()
{
$epubFile = new EpubFile();
$epubFile->openFile(__DIR__ . '/resources/Advanced-v1.0.0.epub');
@@ -76,7 +67,7 @@ final class CustomZipFormatTest extends ZipTestCase
/**
* @throws \Exception
*/
public function testBeforeSaveInZipWriter(): void
public function testBeforeSaveInZipWriter()
{
$zipFile = new ZipFileCustomWriter();
for ($i = 0; $i < 10; $i++) {
@@ -97,7 +88,7 @@ final class CustomZipFormatTest extends ZipTestCase
/**
* @throws \Exception
*/
public function testBeforeSaveInZipFile(): void
public function testBeforeSaveInZipFile()
{
$zipFile = new ZipFileWithBeforeSave();
for ($i = 0; $i < 10; $i++) {
@@ -115,7 +106,11 @@ final class CustomZipFormatTest extends ZipTestCase
$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) {
$localExtras = $entry->getLocalExtraFields();

View File

@@ -1,13 +1,6 @@
<?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.
*/
/** @noinspection PhpUndefinedMethodInspection */
namespace PhpZip\Tests\Extra\Fields;
@@ -15,22 +8,32 @@ use PHPUnit\Framework\TestCase;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\AbstractUnicodeExtraField;
/**
* Class AbstractUnicodeExtraFieldTest.
*/
abstract class AbstractUnicodeExtraFieldTest extends TestCase
{
/**
* @return string|AbstractUnicodeExtraField
*
* @psalm-var class-string<AbstractUnicodeExtraField>
* @psalm-var class-string<\PhpZip\Model\Extra\Fields\AbstractUnicodeExtraField>
*/
abstract protected function getUnicodeExtraFieldClassName();
/**
* @dataProvider provideExtraField
*
* @param int $crc32
* @param string $unicodePath
* @param string $originalPath
* @param string $binaryData
*
* @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();
/** @var AbstractUnicodeExtraField $extraField */
@@ -45,9 +48,12 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
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();
$entryName = '11111';
@@ -68,10 +74,14 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
/**
* @throws ZipException
*/
public function testUnicodeErrorParse(): void
public function testUnicodeErrorParse()
{
$this->expectException(ZipException::class);
$this->expectExceptionMessage('Unicode path extra data must have at least 5 bytes.');
$this->expectException(
ZipException::class
);
$this->expectExceptionMessage(
'Unicode path extra data must have at least 5 bytes.'
);
$className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData('');
@@ -80,10 +90,14 @@ abstract class AbstractUnicodeExtraFieldTest extends TestCase
/**
* @throws ZipException
*/
public function testUnknownVersionParse(): void
public function testUnknownVersionParse()
{
$this->expectException(ZipException::class);
$this->expectExceptionMessage('Unsupported version [2] for Unicode path extra data.');
$this->expectException(
ZipException::class
);
$this->expectExceptionMessage(
'Unsupported version [2] for Unicode path extra data.'
);
$className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData("\x02\x04a\xD28\xC3\xA4\\\xC3\xBC.txt");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,23 +1,30 @@
<?php
declare(strict_types=1);
/*
* This file is part of the nelexa/zip package.
* (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\IO\ZipWriter;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\Model\ZipContainer;
/**
* Class CustomZipWriter.
*/
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();
$now = new \DateTimeImmutable();

View File

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

View File

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

View File

@@ -1,14 +1,5 @@
<?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;
/**
@@ -27,24 +18,26 @@ class DummyFileSystemStream
* This method is called immediately after the wrapper is
* initialized (f.e. by {@see fopen()} and {@see file_get_contents()}).
*
* @param string $path specifies the URL that was passed to
* the original function
* @param string $mode the mode used to open the file, as detailed
* for {@see fopen()}
* @param int $options Holds additional flags set by the streams
* API. It can hold one or more of the
* following values OR'd together.
* @param string|null $opened_path if the path is opened successfully, and
* STREAM_USE_PATH is set in options,
* opened_path should be set to the
* full path of the file/resource that
* was actually opened
* @param string $path specifies the URL that was passed to
* the original function
* @param string $mode the mode used to open the file, as detailed
* for {@see fopen()}
* @param int $options Holds additional flags set by the streams
* API. It can hold one or more of the
* following values OR'd together.
* @param string $opened_path if the path is opened successfully, and
* STREAM_USE_PATH is set in options,
* opened_path should be set to the
* full path of the file/resource that
* was actually opened
*
* @return bool
*
* @see https://www.php.net/streamwrapper.stream-open
*
* @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);
$uri = substr($parsedUrl['path'], 1);
@@ -71,7 +64,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-read
*/
public function stream_read(int $count)
public function stream_read($count)
{
return fread($this->fp, $count);
}
@@ -93,7 +86,7 @@ class DummyFileSystemStream
*
* @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;
}
@@ -108,7 +101,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-tell
*/
public function stream_tell(): int
public function stream_tell()
{
$pos = ftell($this->fp);
@@ -130,7 +123,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-eof
*/
public function stream_eof(): bool
public function stream_eof()
{
return feof($this->fp);
}
@@ -140,11 +133,13 @@ class DummyFileSystemStream
*
* This method is called in response to {@see fstat()}.
*
* @return array
*
* @see https://www.php.net/streamwrapper.stream-stat
* @see https://www.php.net/stat
* @see https://www.php.net/fstat
*/
public function stream_stat(): array
public function stream_stat()
{
return fstat($this->fp);
}
@@ -164,7 +159,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-flush
*/
public function stream_flush(): bool
public function stream_flush()
{
return fflush($this->fp);
}
@@ -180,9 +175,9 @@ class DummyFileSystemStream
*
* @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
*/
public function stream_write(string $data): int
public function stream_write($data)
{
$bytes = fwrite($this->fp, $data);
@@ -214,7 +209,7 @@ class DummyFileSystemStream
*
* @see https://www.php.net/streamwrapper.stream-close
*/
public function stream_close(): void
public function stream_close()
{
fclose($this->fp);
}

View File

@@ -1,13 +1,6 @@
<?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.
*/
/** @noinspection PhpComposerExtensionStubsInspection */
namespace PhpZip\Tests\Internal\Epub;
@@ -28,25 +21,39 @@ use PhpZip\ZipFile;
*/
class EpubFile extends ZipFile
{
protected function createZipWriter(): ZipWriter
/**
* @return ZipWriter
*/
protected function createZipWriter()
{
return new EpubWriter($this->zipContainer);
}
/**
* @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);
}
protected function createZipContainer(?ImmutableZipContainer $sourceContainer = null): ZipContainer
/**
* @param ImmutableZipContainer|null $sourceContainer
*
* @return ZipContainer
*/
protected function createZipContainer(ImmutableZipContainer $sourceContainer = null)
{
return new EpubZipContainer($sourceContainer);
}
protected function addZipEntry(ZipEntry $zipEntry): void
/**
* @param ZipEntry $zipEntry
*/
protected function addZipEntry(ZipEntry $zipEntry)
{
$zipEntry->setCreatedOS(ZipPlatform::OS_DOS);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
@@ -55,25 +62,25 @@ class EpubFile extends ZipFile
/**
* @throws ZipEntryNotFoundException
*
* @return string
*/
public function getMimeType(): string
public function getMimeType()
{
return $this->zipContainer->getMimeType();
}
/**
* @throws ZipException
* @throws ZipEntryNotFoundException
*/
public function getEpubInfo(): EpubInfo
public function getEpubInfo()
{
return new EpubInfo($this->getEntryContents($this->getRootFile()));
}
/**
* @throws ZipException
*
* @return string
*/
public function getRootFile(): string
public function getRootFile()
{
$entryName = 'META-INF/container.xml';
$contents = $this->getEntryContents($entryName);

View File

@@ -1,13 +1,6 @@
<?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.
*/
/** @noinspection PhpComposerExtensionStubsInspection */
namespace PhpZip\Tests\Internal\Epub;
@@ -20,21 +13,29 @@ use PhpZip\Exception\ZipException;
*/
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.
@@ -75,47 +76,74 @@ class EpubInfo
$this->subject = empty($subject) ? null : $subject;
}
public function getTitle(): ?string
/**
* @return string|null
*/
public function getTitle()
{
return $this->title;
}
public function getCreator(): ?string
/**
* @return string|null
*/
public function getCreator()
{
return $this->creator;
}
public function getLanguage(): ?string
/**
* @return string|null
*/
public function getLanguage()
{
return $this->language;
}
public function getPublisher(): ?string
/**
* @return string|null
*/
public function getPublisher()
{
return $this->publisher;
}
public function getDescription(): ?string
/**
* @return string|null
*/
public function getDescription()
{
return $this->description;
}
public function getRights(): ?string
/**
* @return string|null
*/
public function getRights()
{
return $this->rights;
}
public function getDate(): ?string
/**
* @return string|null
*/
public function getDate()
{
return $this->date;
}
public function getSubject(): ?string
/**
* @return string|null
*/
public function getSubject()
{
return $this->subject;
}
public function toArray(): array
/**
* @return array
*/
public function toArray()
{
return [
'title' => $this->title,

View File

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

View File

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

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