1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-07-31 12:40:09 +02:00

Merge branch 'release/3.3.3'

This commit is contained in:
wapplay
2020-07-12 00:01:42 +03:00
14 changed files with 346 additions and 171 deletions

37
.php_cs
View File

@@ -1,7 +1,7 @@
<?php
/**
* PHP Code Style Fixer (config created for version 2.16.1 (Yellow Bird)).
* PHP Code Style Fixer (config created for version 2.16.4 (Yellow Bird)).
*
* Use one of the following console commands to just see the
* changes that will be made.
@@ -119,7 +119,7 @@ $rules = [
*
* Risky!
* Risky as new docblocks might mean more, e.g. a Doctrine entity
* might have a new column in database
* might have a new column in database.
*/
'comment_to_phpdoc' => [
'ignored_tags' => [
@@ -238,6 +238,7 @@ $rules = [
'iconv',
'mime_content_type',
'rename',
'rmdir',
'unlink',
],
],
@@ -283,7 +284,7 @@ $rules = [
* - Explicit syntax allows word concatenation inside strings, e.g.
* `"${var}IsAVar"`, implicit doesn't
* - Explicit syntax is easier to detect for IDE/editors and
* therefore has colors/hightlight with higher contrast, which is
* therefore has colors/highlight with higher contrast, which is
* easier to read
* Backtick operator is skipped because it is harder to handle; you
* can use `backtick_to_shell_exec` fixer to normalize backticks to
@@ -327,7 +328,7 @@ $rules = [
* want to override a method, use the Template method pattern.
*
* Risky!
* Risky when overriding `public` methods of `abstract` classes
* Risky when overriding `public` methods of `abstract` classes.
*/
'final_public_method_for_abstract_class' => false,
@@ -725,8 +726,8 @@ $rules = [
'no_superfluous_elseif' => true,
/*
* Removes `@param` and `@return` tags that don't provide any useful
* information.
* Removes `@param`, `@return` and `@var` tags that don't provide
* any useful information.
*/
'no_superfluous_phpdoc_tags' => false,
@@ -751,7 +752,13 @@ $rules = [
*/
'no_unneeded_curly_braces' => true,
// A final class must not have final methods.
/*
* A `final` class must not have `final` methods and `private`
* methods must not be `final`.
*
* Risky!
* Risky when child class overrides a `private` method.
*/
'no_unneeded_final_method' => true,
/*
@@ -1140,7 +1147,7 @@ $rules = [
* adjusts accordingly the function signature. Requires PHP >= 7.0.
*
* Risky!
* [1] This rule is EXPERIMENTAL and is not covered with backward
* This rule is EXPERIMENTAL and [1] is not covered with backward
* compatibility promise. [2] `@param` annotation is mandatory for
* the fixer to make changes, signatures of methods without it (no
* docblock, inheritdocs) will not be fixed. [3] Manual actions are
@@ -1153,7 +1160,7 @@ $rules = [
* adjusts accordingly the function signature. Requires PHP >= 7.0.
*
* Risky!
* [1] This rule is EXPERIMENTAL and is not covered with backward
* This rule is EXPERIMENTAL and [1] is not covered with backward
* compatibility promise. [2] `@return` annotation is mandatory for
* the fixer to make changes, signatures of methods without it (no
* docblock, inheritdocs) will not be fixed. [3] Manual actions are
@@ -1190,8 +1197,8 @@ $rules = [
'phpdoc_var_annotation_correct_order' => true,
/*
* `@var` and `@type` annotations should not contain the variable
* name.
* `@var` and `@type` annotations of classy properties should not
* contain the name.
*/
'phpdoc_var_without_name' => false,
@@ -1366,7 +1373,7 @@ $rules = [
* `static`.
*
* Risky!
* Risky when using "->bindTo" on lambdas without referencing to
* Risky when using `->bindTo` on lambdas without referencing to
* `$this`.
*/
'static_lambda' => true,
@@ -1464,12 +1471,14 @@ $rules = [
if (\PHP_SAPI === 'cli' && !class_exists(\PhpCsFixer\Config::class)) {
$binFixer = __DIR__ . '/vendor/bin/php-cs-fixer';
if (!is_file($binFixer)) {
$binFixer = 'php-cs-fixer';
}
$dryRun = !\in_array('--force', $_SERVER['argv'], true);
$dryRun = !in_array('--force', $_SERVER['argv'], true);
$command = escapeshellarg($binFixer) . ' fix --config ' . escapeshellarg(__FILE__) . ' --diff-format udiff --ansi -vv';
$command = escapeshellarg($binFixer) . ' fix --config ' . escapeshellarg(__FILE__) . ' --diff-format udiff --ansi';
if ($dryRun) {
$command .= ' --dry-run';
}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016-2020 Ne-Lexa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -354,7 +354,9 @@ class ZipReader
fseek($this->inStream, $cdOffset);
if (!($cdStream = fopen('php://temp', 'w+b'))) {
throw new ZipException('Temp resource can not open from write');
// @codeCoverageIgnoreStart
throw new ZipException('A temporary resource cannot be opened for writing.');
// @codeCoverageIgnoreEnd
}
stream_copy_to_stream($this->inStream, $cdStream, $endCD->getCdSize());
rewind($cdStream);

View File

@@ -39,7 +39,9 @@ class ZipNewData implements ZipData
$zipEntry->setUncompressedSize(\strlen($data));
if (!($handle = fopen('php://temp', 'w+b'))) {
throw new \RuntimeException('Temp resource can not open from write.');
// @codeCoverageIgnoreStart
throw new \RuntimeException('A temporary resource cannot be opened for writing.');
// @codeCoverageIgnoreEnd
}
fwrite($handle, $data);
rewind($handle);
@@ -61,7 +63,7 @@ class ZipNewData implements ZipData
public function getDataAsStream()
{
if (!\is_resource($this->stream)) {
throw new \LogicException(sprintf('Resource was closed (entry=%s).', $this->zipEntry->getName()));
throw new \LogicException(sprintf('Resource has been closed (entry=%s).', $this->zipEntry->getName()));
}
return $this->stream;

View File

@@ -48,7 +48,7 @@ final class FilesUtil
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
$function($fileInfo->getPathname());
}
rmdir($dir);
@rmdir($dir);
}
/**
@@ -198,10 +198,10 @@ final class FilesUtil
return $files;
}
foreach (glob(\dirname($globPattern) . '/*', \GLOB_ONLYDIR | \GLOB_NOSORT) as $dir) {
foreach (glob(\dirname($globPattern) . \DIRECTORY_SEPARATOR . '*', \GLOB_ONLYDIR | \GLOB_NOSORT) as $dir) {
// Unpacking the argument via ... is supported starting from php 5.6 only
/** @noinspection SlowArrayOperationsInLoopInspection */
$files = array_merge($files, self::globFileSearch($dir . '/' . basename($globPattern), $flags, $recursive));
$files = array_merge($files, self::globFileSearch($dir . \DIRECTORY_SEPARATOR . basename($globPattern), $flags, $recursive));
}
return $files;
@@ -273,7 +273,7 @@ final class FilesUtil
public static function normalizeZipPath($path)
{
return implode(
'/',
\DIRECTORY_SEPARATOR,
array_filter(
explode('/', (string) $path),
static function ($part) {

View File

@@ -1,9 +1,5 @@
<?php
/** @noinspection AdditionOperationOnArraysInspection */
/** @noinspection PhpUsageOfSilenceOperatorInspection */
namespace PhpZip;
use PhpZip\Constants\UnixStat;
@@ -143,7 +139,7 @@ class ZipFile implements ZipFileInterface
if (!($handle = fopen('php://temp', 'r+b'))) {
// @codeCoverageIgnoreStart
throw new ZipException("Can't open temp stream.");
throw new ZipException('A temporary resource cannot be opened for writing.');
// @codeCoverageIgnoreEnd
}
fwrite($handle, $data);
@@ -258,8 +254,8 @@ class ZipFile implements ZipFileInterface
*
* @param string $entryName
*
* @throws ZipException
* @throws ZipEntryNotFoundException
* @throws ZipException
*
* @return string
*/
@@ -274,8 +270,8 @@ class ZipFile implements ZipFileInterface
* @param string $entryName
* @param string|null $comment
*
* @throws ZipEntryNotFoundException
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return ZipFile
*/
@@ -291,8 +287,8 @@ class ZipFile implements ZipFileInterface
*
* @param string $entryName
*
* @throws ZipEntryNotFoundException
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return string
*/
@@ -310,8 +306,8 @@ class ZipFile implements ZipFileInterface
/**
* @param string $entryName
*
* @throws ZipEntryNotFoundException
* @throws ZipException
* @throws ZipEntryNotFoundException
*
* @return resource
*/
@@ -328,8 +324,8 @@ class ZipFile implements ZipFileInterface
*
* @param string|ZipEntry $entryName
*
* @throws ZipException
* @throws ZipEntryNotFoundException
* @throws ZipException
*
* @return ZipInfo
*/
@@ -411,6 +407,7 @@ class ZipFile implements ZipFileInterface
$defaultOptions = [
ZipOptions::EXTRACT_SYMLINKS => false,
];
/** @noinspection AdditionOperationOnArraysInspection */
$options += $defaultOptions;
$zipEntries = $this->zipContainer->getEntries();
@@ -443,9 +440,6 @@ class ZipFile implements ZipFileInterface
$entryName = FilesUtil::normalizeZipPath($entryName);
$file = $destDir . \DIRECTORY_SEPARATOR . $entryName;
if (\DIRECTORY_SEPARATOR === '\\') {
$file = str_replace('/', '\\', $file);
}
$extractedEntries[$file] = $entry;
$modifyTimestamp = $entry->getMTime()->getTimestamp();
$atime = $entry->getATime();
@@ -568,19 +562,12 @@ class ZipFile implements ZipFileInterface
*/
public function addFromString($entryName, $contents, $compressionMethod = null)
{
if ($entryName === null) {
throw new InvalidArgumentException('Entry name is null');
}
$entryName = $this->normalizeEntryName($entryName);
if ($contents === null) {
throw new InvalidArgumentException('Contents is null');
}
$entryName = ltrim((string) $entryName, '\\/');
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
$contents = (string) $contents;
$length = \strlen($contents);
@@ -609,6 +596,30 @@ class ZipFile implements ZipFileInterface
return $this;
}
/**
* @param string $entryName
*
* @return string
*/
protected function normalizeEntryName($entryName)
{
if ($entryName === null) {
throw new InvalidArgumentException('Entry name is null');
}
$entryName = ltrim((string) $entryName, '\\/');
if (\DIRECTORY_SEPARATOR === '\\') {
$entryName = str_replace('\\', '/', $entryName);
}
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
return $entryName;
}
/**
* @param Finder $finder
* @param array $options
@@ -624,6 +635,7 @@ class ZipFile implements ZipFileInterface
ZipOptions::COMPRESSION_METHOD => null,
ZipOptions::MODIFIED_TIME => null,
];
/** @noinspection AdditionOperationOnArraysInspection */
$options += $defaultOptions;
if ($options[ZipOptions::STORE_ONLY_FILES]) {
@@ -660,6 +672,7 @@ class ZipFile implements ZipFileInterface
ZipOptions::COMPRESSION_METHOD => null,
ZipOptions::MODIFIED_TIME => null,
];
/** @noinspection AdditionOperationOnArraysInspection */
$options += $defaultOptions;
if (!$file->isReadable()) {
@@ -674,12 +687,7 @@ class ZipFile implements ZipFileInterface
}
}
$entryName = ltrim((string) $entryName, '\\/');
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
$entryName = $this->normalizeEntryName($entryName);
$entryName = $file->isDir() ? rtrim($entryName, '/\\') . '/' : $entryName;
$zipEntry = new ZipEntry($entryName);
@@ -814,17 +822,9 @@ class ZipFile implements ZipFileInterface
throw new InvalidArgumentException('Stream is not resource');
}
if ($entryName === null) {
throw new InvalidArgumentException('Entry name is null');
}
$entryName = ltrim((string) $entryName, '\\/');
if ($entryName === '') {
throw new InvalidArgumentException('Empty entry name');
}
$fstat = fstat($stream);
$entryName = $this->normalizeEntryName($entryName);
$zipEntry = new ZipEntry($entryName);
$fstat = fstat($stream);
if ($fstat !== false) {
$unixMode = $fstat['mode'];
@@ -875,14 +875,7 @@ class ZipFile implements ZipFileInterface
*/
public function addEmptyDir($dirName)
{
if ($dirName === null) {
throw new InvalidArgumentException('Dir name is null');
}
$dirName = ltrim((string) $dirName, '\\/');
if ($dirName === '') {
throw new InvalidArgumentException('Empty dir name');
}
$dirName = $this->normalizeEntryName($dirName);
$dirName = rtrim($dirName, '\\/') . '/';
$zipEntry = new ZipEntry($dirName);
@@ -1077,6 +1070,7 @@ class ZipFile implements ZipFileInterface
* @throws ZipException
*
* @return ZipFile
*
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
*/
private function addGlob(
@@ -1604,19 +1598,39 @@ class ZipFile implements ZipFileInterface
{
$filename = (string) $filename;
$tempFilename = $filename . '.temp' . uniqid('', true);
$tempFilename = $filename . '.temp' . uniqid('', false);
if (!($handle = @fopen($tempFilename, 'w+b'))) {
throw new InvalidArgumentException('File ' . $tempFilename . ' can not open from write.');
throw new InvalidArgumentException(sprintf('Cannot open "%s" for writing.', $tempFilename));
}
$this->saveAsStream($handle);
$reopen = false;
if ($this->reader !== null) {
$meta = $this->reader->getStreamMetaData();
if ($meta['wrapper_type'] === 'plainfile' && isset($meta['uri'])) {
$readFilePath = realpath($meta['uri']);
$writeFilePath = realpath($filename);
if ($readFilePath !== false && $writeFilePath !== false && $readFilePath === $writeFilePath) {
$this->reader->close();
$reopen = true;
}
}
}
if (!@rename($tempFilename, $filename)) {
if (is_file($tempFilename)) {
unlink($tempFilename);
}
throw new ZipException('Can not move ' . $tempFilename . ' to ' . $filename);
throw new ZipException(sprintf('Cannot move %s to %s', $tempFilename, $filename));
}
if ($reopen) {
return $this->openFile($filename);
}
return $this;
@@ -1822,24 +1836,11 @@ class ZipFile implements ZipFileInterface
$meta = $this->reader->getStreamMetaData();
if ($meta['wrapper_type'] === 'plainfile' && isset($meta['uri'])) {
$this->saveAsFile($meta['uri']);
$this->close();
if (!($handle = @fopen($meta['uri'], 'rb'))) {
throw new ZipException("File {$meta['uri']} can't open.");
}
} else {
$handle = @fopen('php://temp', 'r+b');
if (!$handle) {
throw new ZipException('php://temp cannot open for write.');
}
$this->writeZipToStream($handle);
$this->close();
if ($meta['wrapper_type'] !== 'plainfile' || !isset($meta['uri'])) {
throw new ZipException('Overwrite is only supported for open local files.');
}
return $this->openFromStream($handle);
return $this->saveAsFile($meta['uri']);
}
/**

View File

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

View File

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

View File

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

View File

@@ -75,7 +75,7 @@ abstract class ZipFileSetTestCase extends ZipTestCase
$zipEntryName = $localPath . $file;
if (isset($actualResultFiles[$file])) {
static::assertTrue(isset($zipFile[$zipEntryName]));
static::assertTrue(isset($zipFile[$zipEntryName]), 'Not found entry name ' . $zipEntryName);
static::assertSame(
$zipFile[$zipEntryName],
$content,

View File

@@ -34,7 +34,7 @@ class ZipFileTest extends ZipTestCase
$this->setExpectedException(ZipException::class, 'does not exist');
$zipFile = new ZipFile();
$zipFile->openFile(uniqid('', true));
$zipFile->openFile(uniqid('', false));
}
/**
@@ -42,12 +42,16 @@ class ZipFileTest extends ZipTestCase
*/
public function testOpenFileCantOpen()
{
$this->setExpectedException(ZipException::class, 'can\'t open');
if (static::skipTestForWindows()) {
return;
}
if (static::skipTestForRootUser()) {
return;
}
$this->setExpectedException(ZipException::class, 'can\'t open');
static::assertNotFalse(file_put_contents($this->outputFilename, 'content'));
static::assertTrue(chmod($this->outputFilename, 0222));
@@ -1031,7 +1035,12 @@ class ZipFileTest extends ZipTestCase
$zipFile->extractTo($this->outputDirname, null, [], $extractedEntries);
foreach ($entries as $entryName => $contents) {
$fullExtractedFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . $entryName;
$name = $entryName;
if (\DIRECTORY_SEPARATOR === '\\') {
$name = str_replace('/', '\\', $name);
}
$fullExtractedFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . $name;
static::assertTrue(
isset($extractedEntries[$fullExtractedFilename]),
@@ -1393,14 +1402,18 @@ class ZipFileTest extends ZipTestCase
/**
* @throws ZipException
*/
public function testAddFileCantOpen()
public function testAddFileCannotOpen()
{
$this->setExpectedException(InvalidArgumentException::class, 'is not readable');
if (static::skipTestForWindows()) {
return;
}
if (static::skipTestForRootUser()) {
return;
}
$this->setExpectedException(InvalidArgumentException::class, 'is not readable');
static::assertNotFalse(file_put_contents($this->outputFilename, ''));
static::assertTrue(chmod($this->outputFilename, 0244));
@@ -1438,7 +1451,7 @@ class ZipFileTest extends ZipTestCase
$this->setExpectedException(InvalidArgumentException::class, 'does not exist');
$zipFile = new ZipFile();
$zipFile->addDir(uniqid('', true));
$zipFile->addDir(uniqid('', false));
}
/**
@@ -1471,7 +1484,7 @@ class ZipFileTest extends ZipTestCase
$this->setExpectedException(InvalidArgumentException::class, 'does not exist');
$zipFile = new ZipFile();
$zipFile->addDirRecursive(uniqid('', true));
$zipFile->addDirRecursive(uniqid('', false));
}
/**
@@ -1711,7 +1724,9 @@ class ZipFileTest extends ZipTestCase
*/
public function testSaveAsFileNotWritable()
{
$this->setExpectedException(InvalidArgumentException::class, 'can not open from write');
if (static::skipTestForWindows()) {
return;
}
if (static::skipTestForRootUser()) {
return;
@@ -1722,6 +1737,8 @@ class ZipFileTest extends ZipTestCase
$this->outputFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . basename($this->outputFilename);
$this->setExpectedExceptionRegExp(InvalidArgumentException::class, '~Cannot open ".*?" for writing.~');
$zipFile = new ZipFile();
$zipFile->saveAsFile($this->outputFilename);
}
@@ -1877,7 +1894,7 @@ class ZipFileTest extends ZipTestCase
*/
public function testAddEmptyDirNullName()
{
$this->setExpectedException(InvalidArgumentException::class, 'Dir name is null');
$this->setExpectedException(InvalidArgumentException::class, 'Entry name is null');
$zipFile = new ZipFile();
$zipFile->addEmptyDir(null);
@@ -1888,7 +1905,7 @@ class ZipFileTest extends ZipTestCase
*/
public function testAddEmptyDirEmptyName()
{
$this->setExpectedException(InvalidArgumentException::class, 'Empty dir name');
$this->setExpectedException(InvalidArgumentException::class, 'Empty entry name');
$zipFile = new ZipFile();
$zipFile->addEmptyDir('');
@@ -1943,6 +1960,8 @@ class ZipFileTest extends ZipTestCase
*/
public function testRewriteString()
{
$this->setExpectedException(ZipException::class, 'Overwrite is only supported for open local files');
$zipFile = new ZipFile();
$zipFile['file'] = 'content';
$zipFile['file2'] = 'content2';
@@ -1973,6 +1992,87 @@ class ZipFileTest extends ZipTestCase
$zipFile->rewrite();
}
/**
* Checks the ability to overwrite an open zip file with a relative path.
*
* @throws ZipException
*/
public function testRewriteRelativeFile()
{
$zipFile = new ZipFile();
$zipFile['entry.txt'] = 'test';
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
$outputDirname = \dirname($this->outputFilename);
static::assertTrue(chdir($outputDirname));
$relativeFilename = basename($this->outputFilename);
$zipFile->openFile($this->outputFilename);
$zipFile['entry2.txt'] = 'test';
$zipFile->saveAsFile($relativeFilename);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
}
/**
* Checks the ability to overwrite an open zip file with a relative path.
*
* @throws ZipException
*/
public function testRewriteDifferentWinDirectorySeparator()
{
if (\DIRECTORY_SEPARATOR !== '\\') {
static::markTestSkipped('Windows test only');
return;
}
$zipFile = new ZipFile();
$zipFile['entry.txt'] = 'test';
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
$alternativeOutputFilename = str_replace('\\', '/', $this->outputFilename);
self::assertCorrectZipArchive($alternativeOutputFilename);
$zipFile->openFile($this->outputFilename);
$zipFile['entry2.txt'] = 'test';
$zipFile->saveAsFile($alternativeOutputFilename);
$zipFile->close();
self::assertCorrectZipArchive($alternativeOutputFilename);
$zipFile->openFile($this->outputFilename);
static::assertCount(2, $zipFile);
$zipFile->close();
}
/**
* @throws ZipException
*/
public function testRewriteRelativeFile2()
{
$this->outputFilename = basename($this->outputFilename);
$zipFile = new ZipFile();
$zipFile['entry.txt'] = 'test';
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
$absoluteOutputFilename = getcwd() . \DIRECTORY_SEPARATOR . $this->outputFilename;
self::assertCorrectZipArchive($absoluteOutputFilename);
$zipFile->openFile($this->outputFilename);
$zipFile['entry2.txt'] = 'test';
$zipFile->saveAsFile($absoluteOutputFilename);
$zipFile->close();
self::assertCorrectZipArchive($absoluteOutputFilename);
}
/**
* @throws ZipException
*/

View File

@@ -77,7 +77,8 @@ class ZipPasswordTest extends ZipFileSetTestCase
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
static::assertCorrectZipArchive($this->outputFilename, $password);
/** @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/ WinZip 99-character limit */
static::assertCorrectZipArchive($this->outputFilename, substr($password, 0, 99));
// check from WinZip AES encryption
$zipFile->openFile($this->outputFilename);
@@ -137,7 +138,7 @@ class ZipPasswordTest extends ZipFileSetTestCase
);
}
$password = base64_encode(random_bytes(50));
$password = md5(random_bytes(50));
$zip = new ZipFile();
$zip->addDirRecursive($this->outputDirname);

View File

@@ -29,8 +29,8 @@ class ZipStreamOpenTest extends TestCase
*/
public function testOpenStream($resource, $exceptionClass = null, $exceptionMessage = null)
{
if ($resource === null) {
static::markTestSkipped('skip null resource');
if ($resource === null || $resource === false) {
static::markTestSkipped('skip resource');
return;
}

View File

@@ -24,14 +24,14 @@ abstract class ZipTestCase extends TestCase
*/
protected function setUp()
{
$id = uniqid('phpzip', true);
$tempDir = sys_get_temp_dir() . '/phpunit-phpzip';
$id = uniqid('phpzip', false);
$tempDir = sys_get_temp_dir() . \DIRECTORY_SEPARATOR . 'phpunit-phpzip';
if (!is_dir($tempDir) && !mkdir($tempDir, 0755, true) && !is_dir($tempDir)) {
throw new \RuntimeException('Dir ' . $tempDir . " can't created");
throw new \RuntimeException(sprintf('Directory "%s" was not created', $tempDir));
}
$this->outputFilename = $tempDir . '/' . $id . '.zip';
$this->outputDirname = $tempDir . '/' . $id;
$this->outputFilename = $tempDir . \DIRECTORY_SEPARATOR . $id . '.zip';
$this->outputDirname = $tempDir . \DIRECTORY_SEPARATOR . $id;
}
/**
@@ -58,64 +58,91 @@ abstract class ZipTestCase extends TestCase
*/
public static function assertCorrectZipArchive($filename, $password = null)
{
if (self::existsProgram('unzip')) {
$command = 'unzip';
if ($password !== null) {
$command .= ' -P ' . escapeshellarg($password);
}
$command .= ' -t ' . escapeshellarg($filename);
$command .= ' 2>&1';
exec($command, $output, $returnCode);
$output = implode(\PHP_EOL, $output);
if ($password !== null && $returnCode === 81) {
if (self::existsProgram('7z')) {
/**
* WinZip 99-character limit.
*
* @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
*/
$password = substr($password, 0, 99);
$command = '7z t -p' . escapeshellarg($password) . ' ' . escapeshellarg($filename);
exec($command, $output, $returnCode);
/**
* @var array $output
*/
$output = implode(\PHP_EOL, $output);
static::assertSame($returnCode, 0);
static::assertNotContains(' Errors', $output);
static::assertContains(' Ok', $output);
} else {
fwrite(\STDERR, 'Program unzip cannot support this function.' . \PHP_EOL);
fwrite(\STDERR, 'Please install 7z. For Ubuntu-like: sudo apt-get install p7zip-full' . \PHP_EOL);
}
} else {
static::assertSame($returnCode, 0, $output);
static::assertNotContains('incorrect password', $output);
static::assertContains(' OK', $output);
static::assertContains('No errors', $output);
}
if (self::existsProgram('7z')) {
self::assertCorrectZipArchiveFrom7z($filename, $password);
} elseif (self::existsProgram('unzip')) {
self::assertCorrectZipArchiveFromUnzip($filename, $password);
} else {
fwrite(\STDERR, 'Skipped testing the zip archive for errors using third-party utilities.' . \PHP_EOL);
fwrite(\STDERR, 'To fix this, install 7-zip or unzip.' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Ubuntu: sudo apt-get install p7zip-full unzip' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Windows:' . \PHP_EOL);
fwrite(\STDERR, ' * 7-zip - https://www.7-zip.org/download.html' . \PHP_EOL);
fwrite(\STDERR, ' * unzip - http://gnuwin32.sourceforge.net/packages/unzip.htm' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
}
}
/**
* @param string $filename
* @param string|null $password
*/
private static function assertCorrectZipArchiveFrom7z($filename, $password = null)
{
$command = '7z t';
if ($password !== null) {
$command .= ' -p' . escapeshellarg($password);
}
$command .= ' ' . escapeshellarg($filename) . ' 2>&1';
exec($command, $output, $returnCode);
$output = implode(\PHP_EOL, $output);
static::assertSame($returnCode, 0);
static::assertNotContains(' Errors', $output);
static::assertContains(' Ok', $output);
}
/**
* @param string $filename
* @param string|null $password
*/
private static function assertCorrectZipArchiveFromUnzip($filename, $password = null)
{
$command = 'unzip';
if ($password !== null) {
$command .= ' -P ' . escapeshellarg($password);
}
$command .= ' -t ' . escapeshellarg($filename) . ' 2>&1';
exec($command, $output, $returnCode);
$output = implode(\PHP_EOL, $output);
if ($password !== null && $returnCode === 81) {
fwrite(\STDERR, 'Program unzip cannot support this function.' . \PHP_EOL);
fwrite(\STDERR, 'You have to install 7-zip to complete this test.' . \PHP_EOL);
fwrite(\STDERR, 'Install 7-Zip on Ubuntu: sudo apt-get install p7zip-full' . \PHP_EOL);
fwrite(\STDERR, 'Install 7-Zip on Windows: https://www.7-zip.org/download.html' . \PHP_EOL);
return;
}
static::assertSame($returnCode, 0, $output);
static::assertNotContains('incorrect password', $output);
static::assertContains(' OK', $output);
static::assertContains('No errors', $output);
}
/**
* @param string $program
* @param array $successCodes
*
* @return bool
*/
protected static function existsProgram($program)
protected static function existsProgram($program, array $successCodes = [0])
{
if (\DIRECTORY_SEPARATOR !== '\\') {
exec('which ' . escapeshellarg($program), $output, $returnCode);
$command = \DIRECTORY_SEPARATOR === '\\' ?
escapeshellarg($program) :
'which ' . escapeshellarg($program);
$command .= ' 2>&1';
return $returnCode === 0;
}
// false for Windows
return false;
exec($command, $output, $returnCode);
return \in_array($returnCode, $successCodes, true);
}
/**
@@ -144,7 +171,7 @@ abstract class ZipTestCase extends TestCase
*/
public static function assertVerifyZipAlign($filename, $showErrors = false)
{
if (self::existsProgram('zipalign')) {
if (self::existsProgram('zipalign', [0, 2])) {
exec('zipalign -c -v 4 ' . escapeshellarg($filename), $output, $returnCode);
if ($showErrors && $returnCode !== 0) {
@@ -155,6 +182,14 @@ abstract class ZipTestCase extends TestCase
}
fwrite(\STDERR, "Cannot find the program 'zipalign' for the test" . \PHP_EOL);
fwrite(\STDERR, 'To fix this, install zipalign.' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Ubuntu: sudo apt-get install zipalign' . \PHP_EOL);
fwrite(\STDERR, \PHP_EOL);
fwrite(\STDERR, 'Install on Windows:' . \PHP_EOL);
fwrite(\STDERR, ' 1. Install Android Studio' . \PHP_EOL);
fwrite(\STDERR, ' 2. Install Android Sdk' . \PHP_EOL);
fwrite(\STDERR, ' 3. Add zipalign path to \$Path' . \PHP_EOL);
return null;
}
@@ -173,4 +208,18 @@ abstract class ZipTestCase extends TestCase
return false;
}
/**
* @return bool
*/
public static function skipTestForWindows()
{
if (\DIRECTORY_SEPARATOR === '\\') {
static::markTestSkipped('Skip on Windows');
return true;
}
return false;
}
}