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

22 Commits
3.2.0 ... 3.3.1

Author SHA1 Message Date
wapplay
52ce79d4d2 Merge branch 'release/3.3.1' 2020-04-01 16:33:45 +03:00
Ne-Lexa
decf4f5095 Merge pull request #47 from chx/chx-patch-1
Do not pin random_compat version
2020-04-01 16:29:06 +03:00
chx
70d8b8200b Do not pin random_compat version 2020-03-23 09:49:26 -07:00
Ne-Lexa
8fdc21eece Merge branch 'release/3.3.0' 2020-02-04 12:01:18 +03:00
Ne-Lexa
74c0a49057 Merge tag '3.3.0' into develop
Tagging version 3.3.0 3.3.0
2020-02-04 12:01:18 +03:00
Ne-Lexa
ae6d47f47a removed test ZipInfo::__toString() (depends on time zone) 2020-02-04 11:58:47 +03:00
Ne-Lexa
b3b676e3af Merge branch 'release/3.3.0' 2020-02-04 11:47:43 +03:00
Ne-Lexa
f3d769739b Merge tag '3.2.2' into develop
Tagging hotfix 3.2.2 3.2.2

# Conflicts:
#	.travis.yml
#	tests/ZipFileTest.php
2020-02-04 11:36:29 +03:00
Ne-Lexa
074443dbc4 Merge branch 'hotfix/3.2.2' 2020-02-04 11:29:58 +03:00
Ne-Lexa
cbb693213e fix replace contents by file 2020-02-04 11:18:32 +03:00
Ne-Lexa
820c63c30f ZipInfo tests 2020-01-31 13:42:19 +03:00
Ne-Lexa
2235de6b35 Merge branch 'feature/zipcontainer-override-support' into develop 2020-01-31 12:13:16 +03:00
Ne-Lexa
6e33ed08ef run php 7.4 in xenial 2020-01-22 16:26:24 +03:00
Ne-Lexa
79e77a8c88 fix close container in ZipFile::close() 2020-01-22 16:07:22 +03:00
Ne-Lexa
8dcde47072 Added a new option for extracting unix symlinks.
Added new parameter to get the list of extracted files.
2020-01-22 12:53:16 +03:00
Ne-Lexa
47161bdb02 zip extra fields tests 2020-01-22 12:48:47 +03:00
Ne-Lexa
943cf3e777 Fixed problem with cloning a zip container. 2020-01-22 12:48:15 +03:00
Ne-Lexa
5ec656fde4 Implemented the ability to override the instance of ZipContainer.
ZipContainer will be cloned before writing the zip file.
Tested custom ZipWriter, ZipReader, ZipContainer and ZipFile.
2020-01-21 14:13:47 +03:00
Ne-Lexa
d305ab68bc Merge branch 'hotfix/3.2.1' 2020-01-14 16:44:56 +03:00
Ne-Lexa
e0da8c94be Merge tag '3.2.1' into develop
Tagging hotfix 3.2.1 3.2.1
2020-01-14 16:44:56 +03:00
Ne-Lexa
8487dac9df zip extra tests, php 32-bit compat 2020-01-14 16:31:22 +03:00
Ne-Lexa
d21fdb35bb fix#13 fix#16 fix#27 fix#31 fix#41
Tagging version 3.2.0 3.2.0
- Fix memory leak problem
- Add new methods: `getEntry()`, `getEntries()`, `addSplFile()`, `addFromFinder()`
- Fix large zip files problem
2020-01-09 17:41:47 +03:00
55 changed files with 2934 additions and 245 deletions

2
.gitignore vendored
View File

@@ -2,4 +2,4 @@
*.iml
/.idea
/composer.lock
/.php_cs.cache
/*.cache

View File

@@ -107,4 +107,22 @@ namespace PHPSTORM_META {
\PhpZip\Constants\GeneralPurposeBitFlag::UTF8
);
expectedArguments(\PhpZip\Model\ZipEntry::setGeneralPurposeBitFlags(), 0, argumentsSet('zip_gpbf'));
registerArgumentsSet(
"winzip_aes_vendor_version",
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::VERSION_AE1,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::VERSION_AE2
);
registerArgumentsSet(
"winzip_aes_key_strength",
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_256BIT,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_128BIT,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_192BIT
);
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 0, argumentsSet('winzip_aes_vendor_version'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 1, argumentsSet('winzip_aes_key_strength'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 2, argumentsSet('compression_methods'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setVendorVersion(), 0, argumentsSet('winzip_aes_vendor_version'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setKeyStrength(), 0, argumentsSet('winzip_aes_key_strength'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setCompressionMethod(), 0, argumentsSet('compression_methods'));
}

View File

@@ -2,15 +2,15 @@ language: php
env:
global:
- ZIPALIGN_INSTALL=false
- COVERAGE=false
- PHPUNIT_FLAGS="-v -c phpunit.xml --testsuite only_fast_tests"
- PHPUNIT_FLAGS="-v -c phpunit.xml"
matrix:
include:
- php: 5.5
os: linux
dist: trusty
env: ZIPALIGN_INSTALL=false
- php: 5.6
os: linux
@@ -39,8 +39,8 @@ matrix:
- php: 7.4
os: linux
dist: bionic
env: COVERAGE=true ZIPALIGN_INSTALL=true PHPUNIT_FLAGS="-v -c phpunit.xml --testsuite only_fast_tests --coverage-clover=coverage.clover"
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

View File

@@ -3,6 +3,7 @@
`PhpZip` - php библиотека для продвинутой работы с ZIP-архивами.
[![Build Status](https://travis-ci.org/Ne-Lexa/php-zip.svg?branch=master)](https://travis-ci.org/Ne-Lexa/php-zip)
[![Code Coverage](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/nelexa/zip/v/stable)](https://packagist.org/packages/nelexa/zip)
[![Total Downloads](https://poser.pugx.org/nelexa/zip/downloads)](https://packagist.org/packages/nelexa/zip)
[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.5-8892BF.svg)](https://php.net/)

View File

@@ -3,6 +3,7 @@
`PhpZip` is a php-library for extended work with ZIP-archives.
[![Build Status](https://travis-ci.org/Ne-Lexa/php-zip.svg?branch=master)](https://travis-ci.org/Ne-Lexa/php-zip)
[![Code Coverage](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Ne-Lexa/php-zip/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/nelexa/zip/v/stable)](https://packagist.org/packages/nelexa/zip)
[![Total Downloads](https://poser.pugx.org/nelexa/zip/downloads)](https://packagist.org/packages/nelexa/zip)
[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.5-8892BF.svg)](https://php.net/)

View File

@@ -24,13 +24,14 @@
"php": "^5.5.9 || ^7.0",
"ext-zlib": "*",
"psr/http-message": "^1.0",
"paragonie/random_compat": ">=1 <9.99",
"paragonie/random_compat": "*",
"symfony/finder": "^3.0|^4.0|^5.0"
},
"require-dev": {
"ext-bz2": "*",
"ext-openssl": "*",
"ext-fileinfo": "*",
"ext-xml": "*",
"guzzlehttp/psr7": "^1.6",
"phpunit/phpunit": "^4.8|^5.7",
"symfony/var-dumper": "^3.0|^4.0|^5.0"

View File

@@ -22,6 +22,12 @@
</testsuite>
</testsuites>
<groups>
<exclude>
<group>large</group>
</exclude>
</groups>
<filter>
<whitelist>
<directory>src</directory>

View File

@@ -2,6 +2,9 @@
namespace PhpZip\Constants;
use PhpZip\IO\ZipReader;
use PhpZip\ZipFile;
/**
* Interface ZipOptions.
*/
@@ -10,20 +13,50 @@ interface ZipOptions
/**
* Boolean option for store just file names (skip directory names).
*
* @var string
* @see ZipFile::addFromFinder()
*/
const STORE_ONLY_FILES = 'only_files';
/** @var string */
/**
* Uses the specified compression method.
*
* @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile()
*/
const COMPRESSION_METHOD = 'compression_method';
/** @var string */
/**
* Set the specified record modification time.
* The value can be {@see \DateTimeInterface}, integer timestamp
* or a string of any format.
*
* @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile()
*/
const MODIFIED_TIME = 'mtime';
/**
* @var string
* Specifies the encoding of the record name for cases when the UTF-8
* usage flag is not set.
*
* The most commonly used encodings are compiled into the constants
* of the {@see DosCodePage} class.
*
* @see ZipFile::openFile()
* @see ZipFile::openFromString()
* @see ZipFile::openFromStream()
* @see ZipReader::getDefaultOptions()
* @see DosCodePage::getCodePages()
*/
const CHARSET = 'charset';
/**
* Allows ({@see true}) or denies ({@see false}) unpacking unix symlinks.
*
* This is a potentially dangerous operation for uncontrolled zip files.
* By default is ({@see false}).
*
* @see https://josipfranjkovic.blogspot.com/2014/12/reading-local-files-from-facebooks.html
*/
const EXTRACT_SYMLINKS = 'extract_symlinks';
}

View File

@@ -366,7 +366,7 @@ class ZipReader
/** @var UnicodePathExtraField|null $unicodePathExtraField */
$unicodePathExtraField = $zipEntry->getExtraField(UnicodePathExtraField::HEADER_ID);
if ($unicodePathExtraField !== null) {
if ($unicodePathExtraField !== null && $unicodePathExtraField->getCrc32() === crc32($entryName)) {
$unicodePath = $unicodePathExtraField->getUnicodeValue();
if ($unicodePath !== null) {
@@ -543,25 +543,23 @@ class ZipReader
/** @var string|ZipExtraField|null $className */
$className = ZipExtraDriver::getClassNameOrNull($headerId);
if ($className !== null) {
try {
$extraField = $local ?
\call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry) :
\call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
} catch (\Throwable $e) {
throw new \RuntimeException(
sprintf(
'Error parse %s extra field 0x%04X',
$local ? 'local' : 'central directory',
$headerId
)
);
try {
if ($className !== null) {
try {
$extraField = $local ?
\call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry) :
\call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
} catch (\Throwable $e) {
// skip errors while parsing invalid data
continue;
}
} else {
$extraField = new UnrecognizedExtraField($headerId, $bufferData);
}
} else {
$extraField = new UnrecognizedExtraField($headerId, $bufferData);
$collection->add($extraField);
} finally {
$pos += $data['dataSize'];
}
$collection->add($extraField);
$pos += $data['dataSize'];
}
}

View File

@@ -39,7 +39,9 @@ class ZipWriter
*/
public function __construct(ZipContainer $container)
{
$this->zipContainer = $container;
// we clone the container so that the changes made to
// it do not affect the data in the ZipFile class
$this->zipContainer = clone $container;
}
/**

View File

@@ -4,6 +4,7 @@ namespace PhpZip\Model\Data;
use PhpZip\Exception\ZipException;
use PhpZip\Model\ZipData;
use PhpZip\Model\ZipEntry;
/**
* Class ZipFileData.
@@ -16,11 +17,12 @@ class ZipFileData implements ZipData
/**
* ZipStringData constructor.
*
* @param ZipEntry $zipEntry
* @param \SplFileInfo $fileInfo
*
* @throws ZipException
*/
public function __construct(\SplFileInfo $fileInfo)
public function __construct(ZipEntry $zipEntry, \SplFileInfo $fileInfo)
{
if (!$fileInfo->isFile()) {
throw new ZipException('$fileInfo is not a file.');
@@ -31,6 +33,7 @@ class ZipFileData implements ZipData
}
$this->file = $fileInfo;
$zipEntry->setUncompressedSize($fileInfo->getSize());
}
/**

View File

@@ -4,18 +4,27 @@ namespace PhpZip\Model\Data;
use PhpZip\Model\ZipData;
use PhpZip\Model\ZipEntry;
use PhpZip\ZipFile;
/**
* Class ZipNewData.
* The class contains a streaming resource with new content added to the ZIP archive.
*/
class ZipNewData implements ZipData
{
/** @var resource */
private $stream;
/**
* A static variable allows closing the stream in the destructor
* only if it is its sole holder.
*
* @var array<int, int> array of resource ids and the number of class clones
*/
private static $guardClonedStream = [];
/** @var ZipEntry */
private $zipEntry;
/** @var resource */
private $stream;
/**
* ZipStringData constructor.
*
@@ -38,6 +47,12 @@ class ZipNewData implements ZipData
} elseif (\is_resource($data)) {
$this->stream = $data;
}
$resourceId = (int) $this->stream;
self::$guardClonedStream[$resourceId] =
isset(self::$guardClonedStream[$resourceId]) ?
self::$guardClonedStream[$resourceId] + 1 :
0;
}
/**
@@ -79,8 +94,35 @@ class ZipNewData implements ZipData
stream_copy_to_stream($stream, $outStream);
}
/**
* @see https://php.net/manual/en/language.oop5.cloning.php
*/
public function __clone()
{
$resourceId = (int) $this->stream;
self::$guardClonedStream[$resourceId] =
isset(self::$guardClonedStream[$resourceId]) ?
self::$guardClonedStream[$resourceId] + 1 :
1;
}
/**
* The stream will be closed when closing the zip archive.
*
* The method implements protection against closing the stream of the cloned object.
*
* @see ZipFile::close()
*/
public function __destruct()
{
$resourceId = (int) $this->stream;
if (isset(self::$guardClonedStream[$resourceId]) && self::$guardClonedStream[$resourceId] > 0) {
self::$guardClonedStream[$resourceId]--;
return;
}
if (\is_resource($this->stream)) {
fclose($this->stream);
}

View File

@@ -29,16 +29,6 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
$this->unicodeValue = (string) $unicodeValue;
}
/**
* @param string $unicodeValue
*
* @return static
*/
public static function create($unicodeValue)
{
return new static(crc32($unicodeValue), $unicodeValue);
}
/**
* @return int the CRC32 checksum of the filename or comment as
* encoded in the central directory of the zip file
@@ -48,6 +38,14 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
return $this->crc32;
}
/**
* @param int $crc32
*/
public function setCrc32($crc32)
{
$this->crc32 = (int) $crc32;
}
/**
* @return string
*/
@@ -62,7 +60,6 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
public function setUnicodeValue($unicodeValue)
{
$this->unicodeValue = $unicodeValue;
$this->crc32 = crc32($unicodeValue);
}
/**
@@ -78,19 +75,18 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
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.');
throw new ZipException('Unicode path extra data must have at least 5 bytes.');
}
$version = unpack('C', $buffer)[1];
$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']));
}
$crc32 = unpack('V', substr($buffer, 1))[1];
$unicodeValue = substr($buffer, 5);
return new static($crc32, $unicodeValue);
return new static($data['crc32'], $unicodeValue);
}
/**

View File

@@ -79,10 +79,10 @@ class AsiExtraField implements ZipExtraField
*/
public function __construct($mode, $uid = self::USER_GID_PID, $gid = self::USER_GID_PID, $link = '')
{
$this->mode = (int) $mode;
$this->uid = (int) $uid;
$this->gid = (int) $gid;
$this->link = (string) $link;
$this->mode = $mode;
$this->uid = $uid;
$this->gid = $gid;
$this->link = $link;
}
/**
@@ -121,7 +121,7 @@ class AsiExtraField implements ZipExtraField
$link = '';
if ($data['linkSize'] > 0) {
$link = substr($buffer, 8);
$link = substr($buffer, 10);
}
return new self($data['mode'], $data['uid'], $data['gid'], $link);
@@ -191,7 +191,7 @@ class AsiExtraField implements ZipExtraField
*/
public function setLink($link)
{
$this->link = $link;
$this->link = (string) $link;
$this->mode = $this->getPermissionsMode($this->mode);
}
@@ -214,11 +214,13 @@ class AsiExtraField implements ZipExtraField
*/
protected function getPermissionsMode($mode)
{
$type = UnixStat::UNX_IFMT;
$type = 0;
if ($this->isLink()) {
$type = UnixStat::UNX_IFLNK;
} elseif ($this->isDirectory()) {
} elseif (($mode & UnixStat::UNX_IFREG) !== 0) {
$type = UnixStat::UNX_IFREG;
} elseif (($mode & UnixStat::UNX_IFDIR) !== 0) {
$type = UnixStat::UNX_IFDIR;
}
@@ -264,7 +266,7 @@ class AsiExtraField implements ZipExtraField
*/
public function setUserId($uid)
{
$this->uid = $uid;
$this->uid = (int) $uid;
}
/**
@@ -280,7 +282,7 @@ class AsiExtraField implements ZipExtraField
*/
public function setGroupId($gid)
{
$this->gid = $gid;
$this->gid = (int) $gid;
}
/**

View File

@@ -359,6 +359,25 @@ class ExtendedTimestampExtraField implements ZipExtraField
public function setModifyTime($unixTime)
{
$this->modifyTime = $unixTime;
$this->updateFlags();
}
private function updateFlags()
{
$flags = 0;
if ($this->modifyTime !== null) {
$flags |= self::MODIFY_TIME_BIT;
}
if ($this->accessTime !== null) {
$flags |= self::ACCESS_TIME_BIT;
}
if ($this->createTime !== null) {
$flags |= self::CREATE_TIME_BIT;
}
$this->flags = $flags;
}
/**
@@ -370,6 +389,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
public function setAccessTime($unixTime)
{
$this->accessTime = $unixTime;
$this->updateFlags();
}
/**
@@ -381,6 +401,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
public function setCreateTime($unixTime)
{
$this->createTime = $unixTime;
$this->updateFlags();
}
/**

View File

@@ -115,25 +115,6 @@ class NewUnixExtraField implements ZipExtraField
return new self($data['version'], $gid, $uid);
}
/**
* Converts a signed byte into an unsigned integer representation
* (e.g., -1 becomes 255).
*
* @param int $b byte to convert to int
*
* @return int representation of the provided byte
*
* @since 1.5
*/
public static function signedByteToUnsignedInt($b)
{
if ($b >= 0) {
return $b;
}
return 256 + $b;
}
/**
* Populate data from this array as if it was in central directory data.
*
@@ -160,10 +141,10 @@ class NewUnixExtraField implements ZipExtraField
return pack(
'CCVCV',
$this->version,
4, // GIDSize
$this->gid,
4, // UIDSize
$this->uid
$this->uid,
4, // GIDSize
$this->gid
);
}
@@ -233,6 +214,14 @@ class NewUnixExtraField implements ZipExtraField
$this->gid = $gid & 0xffffffff;
}
/**
* @return int
*/
public function getVersion()
{
return $this->version;
}
/**
* @return string
*/

View File

@@ -3,6 +3,7 @@
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;
@@ -10,7 +11,7 @@ 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
*/
@@ -31,42 +32,45 @@ 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.
*/
const EPOCH_OFFSET = -11644473600;
const EPOCH_OFFSET = -116444736000000000;
/** @var int Modify ntfs time */
private $modifyTime;
private $modifyNtfsTime;
/** @var int Access ntfs time */
private $accessTime;
private $accessNtfsTime;
/** @var int Create ntfs time */
private $createTime;
private $createNtfsTime;
/**
* @param int $modifyTime
* @param int $accessTime
* @param int $createTime
* @param int $modifyNtfsTime
* @param int $accessNtfsTime
* @param int $createNtfsTime
*/
public function __construct($modifyTime, $accessTime, $createTime)
public function __construct($modifyNtfsTime, $accessNtfsTime, $createNtfsTime)
{
$this->modifyTime = (int) $modifyTime;
$this->accessTime = (int) $accessTime;
$this->createTime = (int) $createTime;
$this->modifyNtfsTime = (int) $modifyNtfsTime;
$this->accessNtfsTime = (int) $accessNtfsTime;
$this->createNtfsTime = (int) $createNtfsTime;
}
/**
* @param \DateTimeInterface $mtime
* @param \DateTimeInterface $atime
* @param \DateTimeInterface $ctime
* @param \DateTimeInterface $modifyDateTime
* @param \DateTimeInterface $accessDateTime
* @param \DateTimeInterface $createNtfsTime
*
* @return NtfsExtraField
*/
public static function create(\DateTimeInterface $mtime, \DateTimeInterface $atime, \DateTimeInterface $ctime)
{
public static function create(
\DateTimeInterface $modifyDateTime,
\DateTimeInterface $accessDateTime,
\DateTimeInterface $createNtfsTime
) {
return new self(
self::dateTimeToNtfsTime($mtime),
self::dateTimeToNtfsTime($atime),
self::dateTimeToNtfsTime($ctime)
self::dateTimeToNtfsTime($modifyDateTime),
self::dateTimeToNtfsTime($accessDateTime),
self::dateTimeToNtfsTime($createNtfsTime)
);
}
@@ -88,10 +92,16 @@ class NtfsExtraField implements ZipExtraField
* @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;
@@ -121,6 +131,8 @@ class NtfsExtraField implements ZipExtraField
* @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)
@@ -138,13 +150,61 @@ class NtfsExtraField implements ZipExtraField
{
$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->modifyTime);
$data .= PackUtil::packLongLE($this->accessTime);
$data .= PackUtil::packLongLE($this->createTime);
$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.
@@ -161,7 +221,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function getModifyDateTime()
{
return self::ntfsTimeToDateTime($this->modifyTime);
return self::ntfsTimeToDateTime($this->modifyNtfsTime);
}
/**
@@ -169,7 +229,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function setModifyDateTime(\DateTimeInterface $modifyTime)
{
$this->modifyTime = self::dateTimeToNtfsTime($modifyTime);
$this->modifyNtfsTime = self::dateTimeToNtfsTime($modifyTime);
}
/**
@@ -177,7 +237,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function getAccessDateTime()
{
return self::ntfsTimeToDateTime($this->accessTime);
return self::ntfsTimeToDateTime($this->accessNtfsTime);
}
/**
@@ -185,7 +245,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function setAccessDateTime(\DateTimeInterface $accessTime)
{
$this->accessTime = self::dateTimeToNtfsTime($accessTime);
$this->accessNtfsTime = self::dateTimeToNtfsTime($accessTime);
}
/**
@@ -193,7 +253,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function getCreateDateTime()
{
return self::ntfsTimeToDateTime($this->createTime);
return self::ntfsTimeToDateTime($this->createNtfsTime);
}
/**
@@ -201,7 +261,17 @@ class NtfsExtraField implements ZipExtraField
*/
public function setCreateDateTime(\DateTimeInterface $createTime)
{
$this->createTime = self::dateTimeToNtfsTime($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);
}
/**
@@ -211,23 +281,34 @@ class NtfsExtraField implements ZipExtraField
*/
public static function dateTimeToNtfsTime(\DateTimeInterface $dateTime)
{
return $dateTime->getTimestamp() * 10000000 + self::EPOCH_OFFSET;
return self::timestampToNtfsTime((float) $dateTime->format('U.u'));
}
/**
* @param int $time
* @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($time)
public static function ntfsTimeToDateTime($ntfsTime)
{
$timestamp = (int) ($time / 10000000 + self::EPOCH_OFFSET);
$timestamp = self::ntfsTimeToTimestamp($ntfsTime);
$dateTime = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6f', $timestamp));
try {
return new \DateTimeImmutable('@' . $timestamp);
} catch (\Exception $e) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp, 1, $e);
if ($dateTime === false) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp);
}
return $dateTime;
}
/**
@@ -238,17 +319,17 @@ class NtfsExtraField implements ZipExtraField
$args = [self::HEADER_ID];
$format = '0x%04x NtfsExtra:';
if ($this->modifyTime !== 0) {
if ($this->modifyNtfsTime !== 0) {
$format .= ' Modify:[%s]';
$args[] = $this->getModifyDateTime()->format(\DATE_ATOM);
}
if ($this->accessTime !== 0) {
if ($this->accessNtfsTime !== 0) {
$format .= ' Access:[%s]';
$args[] = $this->getAccessDateTime()->format(\DATE_ATOM);
}
if ($this->createTime !== 0) {
if ($this->createNtfsTime !== 0) {
$format .= ' Create:[%s]';
$args[] = $this->getCreateDateTime()->format(\DATE_ATOM);
}

View File

@@ -2,6 +2,7 @@
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\RuntimeException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
@@ -56,7 +57,7 @@ class UnrecognizedExtraField implements ZipExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
throw new \RuntimeException('Unsupport parse');
throw new RuntimeException('Unsupport parse');
}
/**
@@ -67,7 +68,7 @@ class UnrecognizedExtraField implements ZipExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
{
throw new \RuntimeException('Unsupport parse');
throw new RuntimeException('Unsupport parse');
}
/**

View File

@@ -256,7 +256,7 @@ class WinZipAesExtraField implements ZipExtraField
$vendorVersion = (int) $vendorVersion;
if (!\in_array($vendorVersion, self::$allowVendorVersions, true)) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf(
'Unsupport WinZip AES vendor version: %d',
$vendorVersion
@@ -294,7 +294,7 @@ class WinZipAesExtraField implements ZipExtraField
$keyStrength = (int) $keyStrength;
if (!isset(self::$encryptionStrengths[$keyStrength])) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf(
'Key strength %d not support value. Allow values: %s',
$keyStrength,

View File

@@ -53,4 +53,20 @@ class ImmutableZipContainer implements \Countable
{
return \count($this->entries);
}
/**
* When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties.
* Any properties that are references to other variables, will remain references.
* Once the cloning is complete, if a __clone() method is defined,
* then the newly created object's __clone() method will be called, to allow any necessary properties that need to
* be changed. NOT CALLABLE DIRECTLY.
*
* @see https://php.net/manual/en/language.oop5.cloning.php
*/
public function __clone()
{
foreach ($this->entries as $key => $value) {
$this->entries[$key] = clone $value;
}
}
}

View File

@@ -12,7 +12,12 @@ use PhpZip\Exception\ZipException;
*/
class ZipContainer extends ImmutableZipContainer
{
/** @var ImmutableZipContainer|null */
/**
* @var ImmutableZipContainer|null The source container contains zip entries from
* an open zip archive. The source container makes
* it possible to undo changes in the archive.
* When cloning, this container is not cloned.
*/
private $sourceContainer;
/**

View File

@@ -963,9 +963,12 @@ class ZipEntry
{
$dosTime = (int) $dosTime;
if ($dosTime < 0x00000000 || $dosTime > 0xffffffff) {
throw new InvalidArgumentException('DosTime out of range');
if (\PHP_INT_SIZE === 8) {
if ($dosTime < 0x00000000 || $dosTime > 0xffffffff) {
throw new InvalidArgumentException('DosTime out of range');
}
}
$this->dosTime = $dosTime;
return $this;
@@ -1010,9 +1013,12 @@ class ZipEntry
{
$this->externalAttributes = (int) $externalAttributes;
if ($externalAttributes < 0x00000000 || $externalAttributes > 0xffffffff) {
throw new InvalidArgumentException('external attributes out of range: ' . $externalAttributes);
if (\PHP_INT_SIZE === 8) {
if ($externalAttributes < 0x00000000 || $externalAttributes > 0xffffffff) {
throw new InvalidArgumentException('external attributes out of range: ' . $externalAttributes);
}
}
$this->externalAttributes = $externalAttributes;
return $this;

View File

@@ -89,7 +89,10 @@ class ZipEntryMatcher implements \Countable
*/
public function all()
{
$this->matches = array_keys($this->zipContainer->getEntries());
$this->matches = array_map(
'strval',
array_keys($this->zipContainer->getEntries())
);
return $this;
}
@@ -192,10 +195,7 @@ class ZipEntryMatcher implements \Countable
*
* @see http://php.net/manual/en/countable.count.php
*
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
* @return int the custom count as an integer
*
* @since 5.1.0
*/

View File

@@ -43,9 +43,10 @@ final class FilesUtil
\RecursiveIteratorIterator::CHILD_FIRST
);
/** @var \SplFileInfo $fileInfo */
foreach ($files as $fileInfo) {
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
$function($fileInfo->getRealPath());
$function($fileInfo->getPathname());
}
rmdir($dir);
}
@@ -303,36 +304,19 @@ final class FilesUtil
}
/**
* @param string $linkPath
* @param string $target
* @param string $path
* @param bool $allowSymlink
*
* @return bool
*/
public static function symlink($target, $linkPath)
public static function symlink($target, $path, $allowSymlink)
{
if (\DIRECTORY_SEPARATOR === '\\') {
$linkPath = str_replace('/', '\\', $linkPath);
$target = str_replace('/', '\\', $target);
$abs = null;
if (!self::isAbsolutePath($target)) {
$abs = realpath(\dirname($linkPath) . \DIRECTORY_SEPARATOR . $target);
if (\is_string($abs)) {
$target = $abs;
}
}
if (\DIRECTORY_SEPARATOR === '\\' || !$allowSymlink) {
return file_put_contents($path, $target) !== false;
}
if (!symlink($target, $linkPath)) {
if (\DIRECTORY_SEPARATOR === '\\' && is_file($target)) {
return copy($target, $linkPath);
}
return false;
}
return true;
return symlink($target, $path);
}
/**

View File

@@ -6,6 +6,7 @@
namespace PhpZip;
use PhpZip\Constants\UnixStat;
use PhpZip\Constants\ZipCompressionLevel;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipEncryptionMethod;
@@ -20,6 +21,7 @@ use PhpZip\IO\ZipReader;
use PhpZip\IO\ZipWriter;
use PhpZip\Model\Data\ZipFileData;
use PhpZip\Model\Data\ZipNewData;
use PhpZip\Model\ImmutableZipContainer;
use PhpZip\Model\ZipContainer;
use PhpZip\Model\ZipEntry;
use PhpZip\Model\ZipEntryMatcher;
@@ -68,7 +70,7 @@ class ZipFile implements ZipFileInterface
*/
public function __construct()
{
$this->zipContainer = new ZipContainer();
$this->zipContainer = $this->createZipContainer(null);
}
/**
@@ -90,6 +92,16 @@ class ZipFile implements ZipFileInterface
return new ZipWriter($this->zipContainer);
}
/**
* @param ImmutableZipContainer|null $sourceContainer
*
* @return ZipContainer
*/
protected function createZipContainer(ImmutableZipContainer $sourceContainer = null)
{
return new ZipContainer($sourceContainer);
}
/**
* Open zip archive from file.
*
@@ -130,7 +142,9 @@ class ZipFile implements ZipFileInterface
}
if (!($handle = fopen('php://temp', 'r+b'))) {
// @codeCoverageIgnoreStart
throw new ZipException("Can't open temp stream.");
// @codeCoverageIgnoreEnd
}
fwrite($handle, $data);
rewind($handle);
@@ -151,7 +165,7 @@ class ZipFile implements ZipFileInterface
public function openFromStream($handle, array $options = [])
{
$this->reader = $this->createZipReader($handle, $options);
$this->zipContainer = new ZipContainer($this->reader->read());
$this->zipContainer = $this->createZipContainer($this->reader->read());
return $this;
}
@@ -363,15 +377,20 @@ class ZipFile implements ZipFileInterface
*
* 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 The entries to extract. It accepts either
* a single entry name or an array of names.
* @param string $destDir location where to extract the files
* @param array|string|null $entries entries to extract
* @param array $options extract options
* @param array $extractedEntries if the extractedEntries argument
* 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)
public function extractTo($destDir, $entries = null, array $options = [], &$extractedEntries = [])
{
if (!file_exists($destDir)) {
throw new ZipException(sprintf('Destination %s not found', $destDir));
@@ -385,7 +404,14 @@ class ZipFile implements ZipFileInterface
throw new ZipException('Destination is not writable directory');
}
$extractedEntries = [];
if ($extractedEntries === null) {
$extractedEntries = [];
}
$defaultOptions = [
ZipOptions::EXTRACT_SYMLINKS => false,
];
$options += $defaultOptions;
$zipEntries = $this->zipContainer->getEntries();
@@ -435,7 +461,9 @@ class ZipFile implements ZipFileInterface
}
if (!mkdir($dir, $dirMode, true) && !is_dir($dir)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir));
// @codeCoverageIgnoreEnd
}
chmod($dir, $dirMode);
}
@@ -471,6 +499,7 @@ class ZipFile implements ZipFileInterface
/** @noinspection PhpUsageOfSilenceOperatorInspection */
if (!($handle = @fopen($file, 'w+b'))) {
// @codeCoverageIgnoreStart
throw new ZipException(
sprintf(
'Cannot extract zip entry %s. File %s cannot open for write.',
@@ -478,6 +507,7 @@ class ZipFile implements ZipFileInterface
$file
)
);
// @codeCoverageIgnoreEnd
}
try {
@@ -486,9 +516,8 @@ class ZipFile implements ZipFileInterface
unlink($file);
throw $e;
} finally {
fclose($handle);
}
fclose($handle);
if ($unixMode === 0) {
$unixMode = 0644;
@@ -503,8 +532,10 @@ class ZipFile implements ZipFileInterface
}
}
$allowSymlink = (bool) $options[ZipOptions::EXTRACT_SYMLINKS];
foreach ($symlinks as $linkPath => $target) {
if (!FilesUtil::symlink($target, $linkPath)) {
if (!FilesUtil::symlink($target, $linkPath, $allowSymlink)) {
unset($extractedEntries[$linkPath]);
}
}
@@ -515,7 +546,7 @@ class ZipFile implements ZipFileInterface
touch($dir, $lastMod);
}
// ksort($extractedEntries);
ksort($extractedEntries);
return $this;
}
@@ -652,9 +683,24 @@ class ZipFile implements ZipFileInterface
$entryName = $file->isDir() ? rtrim($entryName, '/\\') . '/' : $entryName;
$zipEntry = new ZipEntry($entryName);
$zipData = null;
$zipEntry->setCreatedOS(ZipPlatform::OS_UNIX);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
if ($file->isFile()) {
$zipData = null;
$filePerms = $file->getPerms();
if ($file->isLink()) {
$linkTarget = $file->getLinkTarget();
$lengthLinkTarget = \strlen($linkTarget);
$zipEntry->setCompressionMethod(ZipCompressionMethod::STORED);
$zipEntry->setUncompressedSize($lengthLinkTarget);
$zipEntry->setCompressedSize($lengthLinkTarget);
$zipEntry->setCrc(crc32($linkTarget));
$filePerms |= UnixStat::UNX_IFLNK;
$zipData = new ZipNewData($zipEntry, $linkTarget);
} elseif ($file->isFile()) {
if (isset($options[ZipOptions::COMPRESSION_METHOD])) {
$compressionMethod = $options[ZipOptions::COMPRESSION_METHOD];
} elseif ($file->getSize() < 512) {
@@ -665,30 +711,17 @@ class ZipFile implements ZipFileInterface
ZipCompressionMethod::DEFLATED;
}
$zipEntry->setUncompressedSize($file->getSize());
$zipEntry->setCompressionMethod($compressionMethod);
$zipData = new ZipFileData($file);
$zipData = new ZipFileData($zipEntry, $file);
} elseif ($file->isDir()) {
$zipEntry->setCompressionMethod(ZipCompressionMethod::STORED);
$zipEntry->setUncompressedSize(0);
$zipEntry->setCompressedSize(0);
$zipEntry->setCrc(0);
} elseif ($file->isLink()) {
$linkTarget = $file->getLinkTarget();
$lengthLinkTarget = \strlen($linkTarget);
$zipEntry->setCompressionMethod(ZipCompressionMethod::STORED);
$zipEntry->setUncompressedSize($lengthLinkTarget);
$zipEntry->setCompressedSize($lengthLinkTarget);
$zipEntry->setCrc(crc32($linkTarget));
$zipData = new ZipNewData($zipEntry, $linkTarget);
}
$zipEntry->setCreatedOS(ZipPlatform::OS_UNIX);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
$zipEntry->setUnixMode($file->getPerms());
$zipEntry->setUnixMode($filePerms);
$timestamp = null;
@@ -1769,8 +1802,9 @@ class ZipFile implements ZipFileInterface
if ($this->reader !== null) {
$this->reader->close();
$this->reader = null;
$this->zipContainer = new ZipContainer();
}
$this->zipContainer = $this->createZipContainer(null);
gc_collect_cycles();
}
/**

View File

@@ -292,15 +292,20 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
*
* 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 The entries to extract. It accepts either
* a single entry name or an array of names.
* @param string $destDir location where to extract the files
* @param array|string|null $entries entries to extract
* @param array $options extract options
* @param array $extractedEntries if the extractedEntries argument
* 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);
public function extractTo($destDir, $entries = null, array $options = [], &$extractedEntries = []);
/**
* Add entry from the string.

View File

@@ -0,0 +1,126 @@
<?php
namespace PhpZip\Tests;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\Tests\Internal\CustomZip\ZipFileCustomWriter;
use PhpZip\Tests\Internal\CustomZip\ZipFileWithBeforeSave;
use PhpZip\Tests\Internal\Epub\EpubFile;
use PhpZip\ZipFile;
/**
* Checks the ability to create own file-type class, reader, writer and container.
**.
*
* @internal
*
* @small
*/
final class CustomZipFormatTest extends ZipTestCase
{
/**
* @throws ZipException
*
* @see http://www.epubtest.org/test-books source epub files
*/
public function testEpub()
{
$epubFile = new EpubFile();
$epubFile->openFile(__DIR__ . '/resources/Advanced-v1.0.0.epub');
self::assertSame($epubFile->getRootFile(), 'EPUB/package.opf');
self::assertSame($epubFile->getMimeType(), 'application/epub+zip');
$epubInfo = $epubFile->getEpubInfo();
self::assertSame($epubInfo->toArray(), [
'title' => 'Advanced Accessibility Tests: Extended Descriptions',
'creator' => 'DAISY Consortium Transition to EPUB 3 and DIAGRAM Standards WG',
'language' => 'en-US',
'publisher' => 'DAISY Consortium and DIAGRAM Center',
'description' => 'Tests for accessible extended descriptions of images in EPUBs',
'rights' => 'This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike (CC BY-NC-SA) license.',
'date' => '2019-01-03',
'subject' => 'extended-descriptions',
]);
$epubFile->deleteFromName('mimetype');
self::assertFalse($epubFile->hasEntry('mimetype'));
try {
$epubFile->getMimeType();
self::fail('deleted mimetype');
} catch (ZipEntryNotFoundException $e) {
self::assertSame('Zip Entry "mimetype" was not found in the archive.', $e->getMessage());
}
$epubFile->saveAsFile($this->outputFilename);
self::assertFalse($epubFile->hasEntry('mimetype'));
$epubFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$epubFile->openFile($this->outputFilename);
// file appended in EpubWriter before write
self::assertTrue($epubFile->hasEntry('mimetype'));
$epubFile->close();
}
/**
* @throws \Exception
*/
public function testBeforeSaveInZipWriter()
{
$zipFile = new ZipFileCustomWriter();
for ($i = 0; $i < 10; $i++) {
$zipFile['file ' . $i] = 'contents file ' . $i;
}
$this->existsExtraFields($zipFile, false);
$zipFile->saveAsFile($this->outputFilename);
$this->existsExtraFields($zipFile, false);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
}
/**
* @throws \Exception
*/
public function testBeforeSaveInZipFile()
{
$zipFile = new ZipFileWithBeforeSave();
for ($i = 0; $i < 10; $i++) {
$zipFile['file ' . $i] = 'contents file ' . $i;
}
$this->existsExtraFields($zipFile, false);
$zipFile->saveAsFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
}
/**
* @param ZipFile $zipFile
* @param bool $exists
*/
private function existsExtraFields(ZipFile $zipFile, $exists)
{
foreach ($zipFile->getEntries() as $entry) {
$localExtras = $entry->getLocalExtraFields();
$cdExtras = $entry->getCdExtraFields();
self::assertSame(isset($localExtras[NtfsExtraField::HEADER_ID]), $exists);
self::assertSame(isset($cdExtras[NtfsExtraField::HEADER_ID]), $exists);
self::assertSame(isset($localExtras[NewUnixExtraField::HEADER_ID]), $exists);
self::assertSame(isset($cdExtras[NewUnixExtraField::HEADER_ID]), $exists);
}
}
}

View File

@@ -0,0 +1,101 @@
<?php
/** @noinspection PhpUndefinedMethodInspection */
namespace PhpZip\Tests\Extra\Fields;
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<\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($crc32, $unicodePath, $originalPath, $binaryData)
{
$crc32 = (int) $crc32; // for php 32-bit
$className = $this->getUnicodeExtraFieldClassName();
/** @var AbstractUnicodeExtraField $extraField */
$extraField = new $className($crc32, $unicodePath);
static::assertSame($extraField->getCrc32(), $crc32);
static::assertSame($extraField->getUnicodeValue(), $unicodePath);
static::assertSame(crc32($originalPath), $crc32);
static::assertSame($binaryData, $extraField->packLocalFileData());
static::assertSame($binaryData, $extraField->packCentralDirData());
static::assertEquals($className::unpackLocalFileData($binaryData), $extraField);
static::assertEquals($className::unpackCentralDirData($binaryData), $extraField);
}
/**
* @return array
*/
abstract public function provideExtraField();
public function testSetter()
{
$className = $this->getUnicodeExtraFieldClassName();
$entryName = '11111';
/** @var AbstractUnicodeExtraField $extraField */
$extraField = new $className(crc32($entryName), '22222');
static::assertSame($extraField->getHeaderId(), $className::HEADER_ID);
static::assertSame($extraField->getCrc32(), crc32($entryName));
static::assertSame($extraField->getUnicodeValue(), '22222');
$crc32 = 1234567;
$extraField->setCrc32($crc32);
static::assertSame($extraField->getCrc32(), $crc32);
$extraField->setUnicodeValue('44444');
static::assertSame($extraField->getUnicodeValue(), '44444');
}
/**
* @throws ZipException
*/
public function testUnicodeErrorParse()
{
$this->setExpectedException(
ZipException::class,
'Unicode path extra data must have at least 5 bytes.'
);
$className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData('');
}
/**
* @throws ZipException
*/
public function testUnknownVersionParse()
{
$this->setExpectedException(
ZipException::class,
'Unsupported version [2] for Unicode path extra data.'
);
$className = $this->getUnicodeExtraFieldClassName();
$className::unpackLocalFileData("\x02\x04a\xD28\xC3\xA4\\\xC3\xBC.txt");
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,101 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\Crc32Exception;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\AsiExtraField;
/**
* @internal
*
* @small
*/
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($mode, $uid, $gid, $link, $binaryData)
{
$asiExtraField = new AsiExtraField($mode, $uid, $gid, $link);
self::assertSame($asiExtraField->getHeaderId(), AsiExtraField::HEADER_ID);
self::assertSame($asiExtraField->getMode(), $mode);
self::assertSame($asiExtraField->getUserId(), $uid);
self::assertSame($asiExtraField->getGroupId(), $uid);
self::assertSame($asiExtraField->getLink(), $link);
self::assertSame($asiExtraField->packLocalFileData(), $binaryData);
self::assertSame($asiExtraField->packCentralDirData(), $binaryData);
self::assertEquals(AsiExtraField::unpackLocalFileData($binaryData), $asiExtraField);
self::assertEquals(AsiExtraField::unpackCentralDirData($binaryData), $asiExtraField);
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
040755,
AsiExtraField::USER_GID_PID,
AsiExtraField::USER_GID_PID,
'',
"#\x06\\\xF6\xEDA\x00\x00\x00\x00\xE8\x03\xE8\x03",
],
[
0100644,
0,
0,
'sites-enabled/example.conf',
"_\xB8\xC7b\xA4\x81\x1A\x00\x00\x00\x00\x00\x00\x00sites-enabled/example.conf",
],
];
}
public function testSetter()
{
$extraField = new AsiExtraField(0777);
$extraField->setMode(0100666);
self::assertSame(0100666, $extraField->getMode());
$extraField->setUserId(700);
self::assertSame(700, $extraField->getUserId());
$extraField->setGroupId(500);
self::assertSame(500, $extraField->getGroupId());
$extraField->setLink('link.txt');
self::assertSame($extraField->getLink(), 'link.txt');
self::assertSame(0120666, $extraField->getMode());
// dir mode
$extraField->setMode(0755);
self::assertSame(0120755, $extraField->getMode());
$extraField->setLink('');
self::assertSame($extraField->getLink(), '');
self::assertSame(0100755, $extraField->getMode());
}
/**
* @throws Crc32Exception
*/
public function testInvalidParse()
{
$this->setExpectedException(
Crc32Exception::class,
'Asi Unix Extra Filed Data (expected CRC32 value'
);
AsiExtraField::unpackLocalFileData("\x01\x06\\\xF6\xEDA\x00\x00\x00\x00\xE8\x03\xE8\x03");
}
}

View File

@@ -0,0 +1,158 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Model\Extra\Fields\ExtendedTimestampExtraField;
/**
* Class ExtendedTimestampExtraFieldTest.
*
* @internal
*
* @small
*/
final class ExtendedTimestampExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @param int $flags
* @param int|null $modifyTime
* @param int|null $accessTime
* @param int|null $createTime
* @param string $localData
* @param string $cdData
*
* @noinspection PhpTooManyParametersInspection
*/
public function testExtraField(
$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);
self::assertSame($localExtraField->getModifyTime(), $modifyTime);
self::assertSame($localExtraField->getAccessTime(), $accessTime);
self::assertSame($localExtraField->getCreateTime(), $createTime);
self::assertSame($localExtraField->packLocalFileData(), $localData);
self::assertEquals(ExtendedTimestampExtraField::unpackLocalFileData($localData), $localExtraField);
$extTimeField = ExtendedTimestampExtraField::create($modifyTime, $accessTime, $createTime);
self::assertEquals($extTimeField, $localExtraField);
$accessTime = null;
$createTime = null;
$cdExtraField = new ExtendedTimestampExtraField($flags, $modifyTime, $accessTime, $createTime);
self::assertSame($cdExtraField->getHeaderId(), ExtendedTimestampExtraField::HEADER_ID);
self::assertSame($cdExtraField->getFlags(), $flags);
self::assertSame($cdExtraField->getModifyTime(), $modifyTime);
self::assertSame($cdExtraField->getAccessTime(), $accessTime);
self::assertSame($cdExtraField->getCreateTime(), $createTime);
self::assertSame($cdExtraField->packCentralDirData(), $cdData);
self::assertEquals(ExtendedTimestampExtraField::unpackCentralDirData($cdData), $cdExtraField);
self::assertSame($localExtraField->packCentralDirData(), $cdData);
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT |
ExtendedTimestampExtraField::CREATE_TIME_BIT,
911512006,
911430000,
893709400,
"\x07\xC6\x91T6pQS6X\xECD5",
"\x07\xC6\x91T6",
],
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT,
1492955702,
1492955638,
null,
"\x036\xB2\xFCX\xF6\xB1\xFCX",
"\x036\xB2\xFCX",
],
[
ExtendedTimestampExtraField::MODIFY_TIME_BIT,
1470494391,
null,
null,
"\x01\xB7\xF6\xA5W",
"\x01\xB7\xF6\xA5W",
],
];
}
/**
* @throws \Exception
*/
public function testSetter()
{
$mtime = time();
$atime = null;
$ctime = null;
$field = ExtendedTimestampExtraField::create($mtime, $atime, $ctime);
self::assertSame($field->getFlags(), ExtendedTimestampExtraField::MODIFY_TIME_BIT);
self::assertSame($field->getModifyTime(), $mtime);
self::assertEquals($field->getModifyDateTime(), new \DateTimeImmutable('@' . $mtime));
self::assertSame($field->getAccessTime(), $atime);
self::assertSame($field->getCreateTime(), $ctime);
$atime = strtotime('-1 min');
$field->setAccessTime($atime);
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT
);
self::assertSame($field->getModifyTime(), $mtime);
self::assertSame($field->getAccessTime(), $atime);
self::assertEquals($field->getAccessDateTime(), new \DateTimeImmutable('@' . $atime));
self::assertSame($field->getCreateTime(), $ctime);
$ctime = strtotime('-1 hour');
$field->setCreateTime($ctime);
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT |
ExtendedTimestampExtraField::CREATE_TIME_BIT
);
self::assertSame($field->getModifyTime(), $mtime);
self::assertSame($field->getAccessTime(), $atime);
self::assertSame($field->getCreateTime(), $ctime);
self::assertEquals($field->getCreateDateTime(), new \DateTimeImmutable('@' . $ctime));
$field->setCreateTime(null);
self::assertNull($field->getCreateTime());
self::assertNull($field->getCreateDateTime());
self::assertSame(
$field->getFlags(),
ExtendedTimestampExtraField::MODIFY_TIME_BIT |
ExtendedTimestampExtraField::ACCESS_TIME_BIT
);
$field->setAccessTime(null);
self::assertNull($field->getAccessTime());
self::assertNull($field->getAccessDateTime());
self::assertSame($field->getFlags(), ExtendedTimestampExtraField::MODIFY_TIME_BIT);
$field->setModifyTime(null);
self::assertNull($field->getModifyTime());
self::assertNull($field->getModifyDateTime());
self::assertSame($field->getFlags(), 0);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\JarMarkerExtraField;
/**
* Class JarMarkerExtraFieldTest.
*
* @internal
*
* @small
*/
final class JarMarkerExtraFieldTest extends TestCase
{
/**
* @throws ZipException
*/
public function testExtraField()
{
$jarField = new JarMarkerExtraField();
self::assertSame('', $jarField->packLocalFileData());
self::assertSame('', $jarField->packCentralDirData());
self::assertEquals(JarMarkerExtraField::unpackLocalFileData(''), $jarField);
self::assertEquals(JarMarkerExtraField::unpackCentralDirData(''), $jarField);
}
/**
* @throws ZipException
*/
public function testInvalidUnpackLocalData()
{
$this->setExpectedException(
ZipException::class,
"JarMarker doesn't expect any data"
);
JarMarkerExtraField::unpackLocalFileData("\x02\x00\00");
}
/**
* @throws ZipException
*/
public function testInvalidUnpackCdData()
{
$this->setExpectedException(
ZipException::class,
"JarMarker doesn't expect any data"
);
JarMarkerExtraField::unpackCentralDirData("\x02\x00\00");
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
/**
* Class NewUnixExtraFieldTest.
*
* @internal
*
* @small
*/
final class NewUnixExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @param int $version
* @param int $uid
* @param int $gid
* @param string $binaryData
*
* @throws ZipException
*/
public function testExtraField($version, $uid, $gid, $binaryData)
{
$extraField = new NewUnixExtraField($version, $uid, $gid);
self::assertSame($extraField->getHeaderId(), NewUnixExtraField::HEADER_ID);
self::assertSame($extraField->getVersion(), $version);
self::assertSame($extraField->getGid(), $gid);
self::assertSame($extraField->getUid(), $uid);
self::assertEquals(NewUnixExtraField::unpackLocalFileData($binaryData), $extraField);
self::assertEquals(NewUnixExtraField::unpackCentralDirData($binaryData), $extraField);
self::assertSame($extraField->packLocalFileData(), $binaryData);
self::assertSame($extraField->packCentralDirData(), $binaryData);
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
1,
NewUnixExtraField::USER_GID_PID,
NewUnixExtraField::USER_GID_PID,
"\x01\x04\xE8\x03\x00\x00\x04\xE8\x03\x00\x00",
],
[
1,
501,
20,
"\x01\x04\xF5\x01\x00\x00\x04\x14\x00\x00\x00",
],
[
1,
500,
495,
"\x01\x04\xF4\x01\x00\x00\x04\xEF\x01\x00\x00",
],
[
1,
11252,
10545,
"\x01\x04\xF4+\x00\x00\x041)\x00\x00",
],
[
1,
1721,
1721,
"\x01\x04\xB9\x06\x00\x00\x04\xB9\x06\x00\x00",
],
];
}
public function testSetter()
{
$extraField = new NewUnixExtraField(1, 1000, 1000);
self::assertSame(1, $extraField->getVersion());
self::assertSame(1000, $extraField->getUid());
self::assertSame(1000, $extraField->getGid());
$extraField->setUid(0);
self::assertSame(0, $extraField->getUid());
self::assertSame(1000, $extraField->getGid());
$extraField->setGid(0);
self::assertSame(0, $extraField->getUid());
self::assertSame(0, $extraField->getGid());
}
}

View File

@@ -0,0 +1,245 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
/**
* Class NtfsExtraFieldTest.
*
* @internal
*
* @small
*/
final class NtfsExtraFieldTest extends TestCase
{
protected function setUp()
{
if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test');
}
}
/**
* @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(
$modifyNtfsTime,
$accessNtfsTime,
$createNtfsTime,
$modifyTimestamp,
$accessTimestamp,
$createTimestamp,
$binaryData
) {
$extraField = new NtfsExtraField($modifyNtfsTime, $accessNtfsTime, $createNtfsTime);
self::assertSame($extraField->getHeaderId(), NtfsExtraField::HEADER_ID);
self::assertEquals($extraField->getModifyDateTime()->getTimestamp(), (int) $modifyTimestamp);
self::assertEquals($extraField->getAccessDateTime()->getTimestamp(), (int) $accessTimestamp);
self::assertEquals($extraField->getCreateDateTime()->getTimestamp(), (int) $createTimestamp);
self::assertEquals(NtfsExtraField::unpackLocalFileData($binaryData), $extraField);
self::assertEquals(NtfsExtraField::unpackCentralDirData($binaryData), $extraField);
self::assertSame($extraField->packLocalFileData(), $binaryData);
self::assertSame($extraField->packCentralDirData(), $binaryData);
$extraFieldFromDateTime = NtfsExtraField::create(
$extraField->getModifyDateTime(),
$extraField->getAccessDateTime(),
$extraField->getCreateDateTime()
);
self::assertEqualsIntegerWithDelta($extraFieldFromDateTime->getModifyNtfsTime(), $extraField->getModifyNtfsTime(), 100);
self::assertEqualsIntegerWithDelta($extraFieldFromDateTime->getAccessNtfsTime(), $extraField->getAccessNtfsTime(), 100);
self::assertEqualsIntegerWithDelta($extraFieldFromDateTime->getCreateNtfsTime(), $extraField->getCreateNtfsTime(), 100);
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
129853553114795379,
129853553114795379,
129853552641022547,
1340881711.4795379,
1340881711.4795379,
1340881664.1022547,
"\x00\x00\x00\x00\x01\x00\x18\x00s\xCD:Z\x1EU\xCD\x01s\xCD:Z\x1EU\xCD\x01S\x9A\xFD=\x1EU\xCD\x01",
],
[
131301570250000000,
131865940850000000,
131840940680000000,
1485683425.000000,
1542120485.000000,
1539620468.000000,
"\x00\x00\x00\x00\x01\x00\x18\x00\x80\xC63\x1D\x15z\xD2\x01\x80@V\xE2_{\xD4\x01\x00\xB2\x15\x14\xA3d\xD4\x01",
],
[
132181086710000000,
132181086710000000,
132181086710000000,
1573635071.000000,
1573635071.000000,
1573635071.000000,
"\x00\x00\x00\x00\x01\x00\x18\x00\x80\xE9_\x7F\xFF\x99\xD5\x01\x80\xE9_\x7F\xFF\x99\xD5\x01\x80\xE9_\x7F\xFF\x99\xD5\x01",
],
];
}
/**
* @param int $expected
* @param int $actual
* @param int $delta
* @param string $message
*/
private static function assertEqualsIntegerWithDelta(
$expected,
$actual,
$delta,
$message = ''
) {
self::assertSame(
self::roundInt($expected, $delta),
self::roundInt($actual, $delta),
$message
);
}
/**
* @param int $number
* @param int $delta
*
* @return int
*/
private static function roundInt($number, $delta)
{
return (int) (floor($number / $delta) * $delta);
}
/**
* @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(
$mtimeNtfs,
$atimeNtfs,
$ctimeNtfs,
$mtimeTimestamp,
$atimeTimestamp,
$ctimeTimestamp
) {
self::assertEquals(NtfsExtraField::ntfsTimeToTimestamp($mtimeNtfs), $mtimeTimestamp, '', 0.00001);
self::assertEquals(NtfsExtraField::ntfsTimeToTimestamp($atimeNtfs), $atimeTimestamp, '', 0.00001);
self::assertEquals(NtfsExtraField::ntfsTimeToTimestamp($ctimeNtfs), $ctimeTimestamp, '', 0.00001);
self::assertEqualsIntegerWithDelta(NtfsExtraField::timestampToNtfsTime($mtimeTimestamp), $mtimeNtfs, 10);
self::assertEqualsIntegerWithDelta(NtfsExtraField::timestampToNtfsTime($atimeTimestamp), $atimeNtfs, 10);
self::assertEqualsIntegerWithDelta(NtfsExtraField::timestampToNtfsTime($ctimeTimestamp), $ctimeNtfs, 10);
}
/**
* @throws \Exception
*/
public function testSetter()
{
$timeZone = new \DateTimeZone('UTC');
$initDateTime = new \DateTimeImmutable('-1 min', $timeZone);
$mtimeDateTime = new \DateTimeImmutable('-1 hour', $timeZone);
$atimeDateTime = new \DateTimeImmutable('-1 day', $timeZone);
$ctimeDateTime = new \DateTimeImmutable('-1 year', $timeZone);
$extraField = NtfsExtraField::create($initDateTime, $initDateTime, $initDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
$extraField->setModifyDateTime($mtimeDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$mtimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
$extraField->setAccessDateTime($atimeDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$mtimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$atimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$initDateTime->getTimestamp()
);
$extraField->setCreateDateTime($ctimeDateTime);
self::assertEquals(
$extraField->getModifyDateTime()->getTimestamp(),
$mtimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getAccessDateTime()->getTimestamp(),
$atimeDateTime->getTimestamp()
);
self::assertEquals(
$extraField->getCreateDateTime()->getTimestamp(),
$ctimeDateTime->getTimestamp()
);
$newModifyNtfsTime = $extraField->getCreateNtfsTime();
$newAccessNtfsTime = $extraField->getModifyNtfsTime();
$newCreateNtfsTime = $extraField->getAccessNtfsTime();
$extraField->setModifyNtfsTime($newModifyNtfsTime);
$extraField->setAccessNtfsTime($newAccessNtfsTime);
$extraField->setCreateNtfsTime($newCreateNtfsTime);
self::assertSame($extraField->getModifyNtfsTime(), $newModifyNtfsTime);
self::assertSame($extraField->getAccessNtfsTime(), $newAccessNtfsTime);
self::assertSame($extraField->getCreateNtfsTime(), $newCreateNtfsTime);
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Model\Extra\Fields\OldUnixExtraField;
/**
* Class OldUnixExtraFieldTest.
*
* @internal
*
* @small
*/
final class OldUnixExtraFieldTest extends TestCase
{
/**
* @dataProvider provideExtraField
*
* @param int|null $accessTime
* @param int|null $modifyTime
* @param int|null $uid
* @param int|null $gid
* @param string $localBinaryData
* @param string $cdBinaryData
*
* @noinspection PhpTooManyParametersInspection
*
* @throws \Exception
*/
public function testExtraField(
$accessTime,
$modifyTime,
$uid,
$gid,
$localBinaryData,
$cdBinaryData
) {
$extraField = new OldUnixExtraField($accessTime, $modifyTime, $uid, $gid);
self::assertSame($extraField->getHeaderId(), OldUnixExtraField::HEADER_ID);
self::assertSame($extraField->getAccessTime(), $accessTime);
self::assertSame($extraField->getModifyTime(), $modifyTime);
self::assertSame($extraField->getUid(), $uid);
self::assertSame($extraField->getGid(), $gid);
if ($extraField->getModifyTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getModifyTime()),
$extraField->getModifyDateTime()
);
}
if ($extraField->getAccessTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getAccessTime()),
$extraField->getAccessDateTime()
);
}
self::assertEquals(OldUnixExtraField::unpackLocalFileData($localBinaryData), $extraField);
self::assertSame($extraField->packLocalFileData(), $localBinaryData);
$uid = null;
$gid = null;
$extraField = new OldUnixExtraField($accessTime, $modifyTime, $uid, $gid);
self::assertSame($extraField->getHeaderId(), OldUnixExtraField::HEADER_ID);
self::assertSame($extraField->getAccessTime(), $accessTime);
self::assertSame($extraField->getModifyTime(), $modifyTime);
self::assertNull($extraField->getUid());
self::assertNull($extraField->getGid());
if ($extraField->getModifyTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getModifyTime()),
$extraField->getModifyDateTime()
);
}
if ($extraField->getAccessTime() !== null) {
self::assertEquals(
new \DateTimeImmutable('@' . $extraField->getAccessTime()),
$extraField->getAccessDateTime()
);
}
self::assertEquals(OldUnixExtraField::unpackCentralDirData($cdBinaryData), $extraField);
self::assertSame($extraField->packCentralDirData(), $cdBinaryData);
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
1213373265,
1213365834,
502,
502,
"Q\x9BRHJ~RH\xF6\x01\xF6\x01",
"Q\x9BRHJ~RH",
],
[
935520420,
935520401,
501,
100,
"\xA4\xE8\xC27\x91\xE8\xC27\xF5\x01d\x00",
"\xA4\xE8\xC27\x91\xE8\xC27",
],
[
1402666135,
1402666135,
501,
20,
"\x97\xFC\x9AS\x97\xFC\x9AS\xF5\x01\x14\x00",
"\x97\xFC\x9AS\x97\xFC\x9AS",
],
[
null,
null,
null,
null,
'',
'',
],
];
}
public function testSetter()
{
$extraField = new OldUnixExtraField(null, null, null, null);
self::assertNull($extraField->getAccessTime());
self::assertNull($extraField->getAccessDateTime());
self::assertNull($extraField->getModifyTime());
self::assertNull($extraField->getModifyDateTime());
self::assertNull($extraField->getUid());
self::assertNull($extraField->getGid());
$extraField->setModifyTime(1402666135);
self::assertSame($extraField->getModifyTime(), 1402666135);
$extraField->setAccessTime(1213365834);
self::assertSame($extraField->getAccessTime(), 1213365834);
$extraField->setUid(500);
self::assertSame($extraField->getUid(), 500);
$extraField->setGid(100);
self::assertSame($extraField->getGid(), 100);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PhpZip\Model\Extra\Fields\UnicodeCommentExtraField;
/**
* @internal
*
* @small
*/
final class UnicodeCommentExtraFieldTest extends AbstractUnicodeExtraFieldTest
{
/**
* {@inheritdoc}
*/
protected function getUnicodeExtraFieldClassName()
{
return UnicodeCommentExtraField::class;
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
4293813303,
'комментарий',
"\xAA\xAE\xAC\xAC\xA5\xAD\xE2\xA0\xE0\xA8\xA9",
"\x017d\xEE\xFF\xD0\xBA\xD0\xBE\xD0\xBC\xD0\xBC\xD0\xB5\xD0\xBD\xD1\x82\xD0\xB0\xD1\x80\xD0\xB8\xD0\xB9",
],
[
897024324,
'תגובה',
"\x9A\x82\x85\x81\x84",
"\x01D\x81w5\xD7\xAA\xD7\x92\xD7\x95\xD7\x91\xD7\x94",
],
];
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PhpZip\Model\Extra\Fields\UnicodePathExtraField;
/**
* Class UnicodePathExtraFieldTest.
*
* @internal
*
* @small
*/
final class UnicodePathExtraFieldTest extends AbstractUnicodeExtraFieldTest
{
/**
* {@inheritdoc}
*/
protected function getUnicodeExtraFieldClassName()
{
return UnicodePathExtraField::class;
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
2728523760,
'txt\מבחן עברי.txt',
"txt/\x8E\x81\x87\x8F \x92\x81\x98\x89.txt",
"\x01\xF0\xF7\xA1\xA2txt\\\xD7\x9E\xD7\x91\xD7\x97\xD7\x9F \xD7\xA2\xD7\x91\xD7\xA8\xD7\x99.txt",
],
[
953311492,
'ä\ü.txt',
"\x84/\x81.txt",
"\x01\x04a\xD28\xC3\xA4\\\xC3\xBC.txt",
],
[
2965532848,
'Ölfässer.txt',
"\x99lf\x84sser.txt",
"\x01\xB0p\xC2\xB0\xC3\x96lf\xC3\xA4sser.txt",
],
[
3434671236,
'Как заработать в интернете.mp4',
"\x8A\xA0\xAA \xA7\xA0\xE0\xA0\xA1\xAE\xE2\xA0\xE2\xEC \xA2 \xA8\xAD\xE2\xA5\xE0\xAD\xA5\xE2\xA5.mp4",
"\x01\x84\xEC\xB8\xCC\xD0\x9A\xD0\xB0\xD0\xBA \xD0\xB7\xD0\xB0\xD1\x80\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x82\xD0\xB0\xD1\x82\xD1\x8C \xD0\xB2 \xD0\xB8\xD0\xBD\xD1\x82\xD0\xB5\xD1\x80\xD0\xBD\xD0\xB5\xD1\x82\xD0\xB5.mp4",
],
];
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Exception\RuntimeException;
use PhpZip\Model\Extra\Fields\UnrecognizedExtraField;
/**
* Class UnrecognizedExtraFieldTest.
*
* @internal
*
* @small
*/
final class UnrecognizedExtraFieldTest extends TestCase
{
public function testExtraField()
{
$headerId = 0xF00D;
$binaryData = "\x01\x02\x03\x04\x05";
$unrecognizedExtraField = new UnrecognizedExtraField($headerId, $binaryData);
self::assertSame($unrecognizedExtraField->getHeaderId(), $headerId);
self::assertSame($unrecognizedExtraField->getData(), $binaryData);
$newHeaderId = 0xDADA;
$newBinaryData = "\x05\x00";
$unrecognizedExtraField->setHeaderId($newHeaderId);
self::assertSame($unrecognizedExtraField->getHeaderId(), $newHeaderId);
$unrecognizedExtraField->setData($newBinaryData);
self::assertSame($unrecognizedExtraField->getData(), $newBinaryData);
self::assertSame($unrecognizedExtraField->packLocalFileData(), $newBinaryData);
self::assertSame($unrecognizedExtraField->packCentralDirData(), $newBinaryData);
}
public function testUnpackLocalData()
{
$this->setExpectedException(
RuntimeException::class,
'Unsupport parse'
);
UnrecognizedExtraField::unpackLocalFileData("\x01\x02");
}
public function testUnpackCentralDirData()
{
$this->setExpectedException(
RuntimeException::class,
'Unsupport parse'
);
UnrecognizedExtraField::unpackCentralDirData("\x01\x02");
}
}

View File

@@ -0,0 +1,240 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipEncryptionMethod;
use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\ZipException;
use PhpZip\Exception\ZipUnsupportMethodException;
use PhpZip\Model\Extra\Fields\WinZipAesExtraField;
/**
* @internal
*
* @small
*/
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(
$vendorVersion,
$keyStrength,
$compressionMethod,
$saltSize,
$binaryData
) {
$extraField = new WinZipAesExtraField($vendorVersion, $keyStrength, $compressionMethod);
self::assertSame($extraField->getHeaderId(), WinZipAesExtraField::HEADER_ID);
self::assertSame($extraField->getVendorVersion(), $vendorVersion);
self::assertSame($extraField->getKeyStrength(), $keyStrength);
self::assertSame($extraField->getCompressionMethod(), $compressionMethod);
self::assertSame($extraField->getVendorId(), WinZipAesExtraField::VENDOR_ID);
self::assertSame($extraField->getSaltSize(), $saltSize);
self::assertSame($binaryData, $extraField->packLocalFileData());
self::assertSame($binaryData, $extraField->packCentralDirData());
self::assertEquals(WinZipAesExtraField::unpackLocalFileData($binaryData), $extraField);
self::assertEquals(WinZipAesExtraField::unpackCentralDirData($binaryData), $extraField);
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_128BIT,
ZipCompressionMethod::STORED,
8,
"\x01\x00AE\x01\x00\x00",
],
[
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::DEFLATED,
12,
"\x01\x00AE\x02\x08\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_128BIT,
ZipCompressionMethod::DEFLATED,
8,
"\x02\x00AE\x01\x08\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_256BIT,
ZipCompressionMethod::STORED,
16,
"\x02\x00AE\x03\x00\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::DEFLATED,
12,
"\x02\x00AE\x02\x08\x00",
],
[
WinZipAesExtraField::VERSION_AE2,
WinZipAesExtraField::KEY_STRENGTH_256BIT,
ZipCompressionMethod::STORED,
16,
"\x02\x00AE\x03\x00\x00",
],
];
}
/**
* @throws ZipUnsupportMethodException
*/
public function testSetter()
{
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_256BIT,
ZipCompressionMethod::DEFLATED
);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE1);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_256BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 16);
self::assertSame($extraField->getEncryptionStrength(), 256);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_256);
$extraField->setVendorVersion(WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_256BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 16);
self::assertSame($extraField->getEncryptionStrength(), 256);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_256);
$extraField->setKeyStrength(WinZipAesExtraField::KEY_STRENGTH_128BIT);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_128BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 8);
self::assertSame($extraField->getEncryptionStrength(), 128);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_128);
$extraField->setKeyStrength(WinZipAesExtraField::KEY_STRENGTH_192BIT);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_192BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::DEFLATED);
self::assertSame($extraField->getSaltSize(), 12);
self::assertSame($extraField->getEncryptionStrength(), 192);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_192);
$extraField->setCompressionMethod(ZipCompressionMethod::STORED);
self::assertSame($extraField->getVendorVersion(), WinZipAesExtraField::VERSION_AE2);
self::assertSame($extraField->getKeyStrength(), WinZipAesExtraField::KEY_STRENGTH_192BIT);
self::assertSame($extraField->getCompressionMethod(), ZipCompressionMethod::STORED);
self::assertSame($extraField->getSaltSize(), 12);
self::assertSame($extraField->getEncryptionStrength(), 192);
self::assertSame($extraField->getEncryptionMethod(), ZipEncryptionMethod::WINZIP_AES_192);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testConstructUnsupportVendorVersion()
{
$this->setExpectedException(InvalidArgumentException::class, 'Unsupport WinZip AES vendor version: 3');
new WinZipAesExtraField(
3,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::STORED
);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testSetterUnsupportVendorVersion()
{
$this->setExpectedException(InvalidArgumentException::class, 'Unsupport WinZip AES vendor version: 3');
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::STORED
);
$extraField->setVendorVersion(3);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testConstructUnsupportCompressionMethod()
{
$this->setExpectedException(ZipUnsupportMethodException::class, 'Compression method 3 (Reduced compression factor 2) is not supported.');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
3
);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testSetterUnsupportCompressionMethod()
{
$this->setExpectedException(ZipUnsupportMethodException::class, 'Compression method 3 (Reduced compression factor 2) is not supported.');
$extraField = new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
WinZipAesExtraField::KEY_STRENGTH_192BIT,
ZipCompressionMethod::STORED
);
$extraField->setCompressionMethod(3);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testConstructUnsupportKeyStrength()
{
$this->setExpectedException(InvalidArgumentException::class, 'Key strength 16 not support value. Allow values: 1, 2, 3');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
0x10,
ZipCompressionMethod::STORED
);
}
/**
* @throws ZipUnsupportMethodException
*/
public function testSetterUnsupportKeyStrength()
{
$this->setExpectedException(InvalidArgumentException::class, 'Key strength 16 not support value. Allow values: 1, 2, 3');
new WinZipAesExtraField(
WinZipAesExtraField::VERSION_AE1,
0x10,
ZipCompressionMethod::STORED
);
}
}

View File

@@ -0,0 +1,132 @@
<?php
namespace PhpZip\Tests\Extra\Fields;
use PHPUnit\Framework\TestCase;
use PhpZip\Constants\ZipConstants;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\Zip64ExtraField;
use PhpZip\Model\ZipEntry;
/**
* @internal
*
* @small
*/
final class Zip64ExtraFieldTest extends TestCase
{
protected function setUp()
{
if (\PHP_INT_SIZE === 4) {
self::markTestSkipped('only 64 bit test');
}
}
/**
* @dataProvider provideExtraField
*
* @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(
$uncompressedSize,
$compressedSize,
$localHeaderOffset,
$diskStart,
$localBinaryData,
$cdBinaryData
) {
$extraField = new Zip64ExtraField(
$uncompressedSize,
$compressedSize,
$localHeaderOffset,
$diskStart
);
self::assertSame($extraField->getHeaderId(), Zip64ExtraField::HEADER_ID);
self::assertSame($extraField->getUncompressedSize(), $uncompressedSize);
self::assertSame($extraField->getCompressedSize(), $compressedSize);
self::assertSame($extraField->getLocalHeaderOffset(), $localHeaderOffset);
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);
if ($localBinaryData !== null) {
self::assertSame($localBinaryData, $extraField->packLocalFileData());
self::assertEquals(Zip64ExtraField::unpackLocalFileData($localBinaryData, $zipEntry), $extraField);
}
if ($cdBinaryData !== null) {
self::assertSame($cdBinaryData, $extraField->packCentralDirData());
self::assertEquals(Zip64ExtraField::unpackCentralDirData($cdBinaryData, $zipEntry), $extraField);
}
}
/**
* @return array
*/
public function provideExtraField()
{
return [
[
0,
2,
null,
null,
"\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00",
null,
],
[
5368709120,
5369580144,
null,
null,
null,
"\x00\x00\x00@\x01\x00\x00\x00pJ\x0D@\x01\x00\x00\x00",
],
[
null,
null,
4945378839,
null,
null,
"\x17~\xC4&\x01\x00\x00\x00",
],
];
}
public function testSetter()
{
$extraField = new Zip64ExtraField();
self::assertNull($extraField->getUncompressedSize());
self::assertNull($extraField->getCompressedSize());
self::assertNull($extraField->getLocalHeaderOffset());
self::assertNull($extraField->getDiskStart());
$uncompressedSize = 12222;
$extraField->setUncompressedSize($uncompressedSize);
self::assertSame($extraField->getUncompressedSize(), $uncompressedSize);
$compressedSize = 12222;
$extraField->setCompressedSize($uncompressedSize);
self::assertSame($extraField->getCompressedSize(), $compressedSize);
$localHeaderOffset = 12222;
$extraField->setLocalHeaderOffset($localHeaderOffset);
self::assertSame($extraField->getLocalHeaderOffset(), $localHeaderOffset);
$diskStart = 2;
$extraField->setDiskStart($diskStart);
self::assertSame($extraField->getDiskStart(), $diskStart);
}
}

View File

@@ -0,0 +1,39 @@
<?php
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
{
/**
* 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();
$ntfsTimeExtra = NtfsExtraField::create($now, $now->modify('-1 day'), $now->modify('-10 day'));
$unixExtra = new NewUnixExtraField();
foreach ($this->zipContainer->getEntries() as $entry) {
$entry->addExtraField($ntfsTimeExtra);
$entry->addExtraField($unixExtra);
}
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\IO\ZipWriter;
use PhpZip\ZipFile;
/**
* Class ZipFileCustomWriter.
*/
class ZipFileCustomWriter extends ZipFile
{
/**
* @return ZipWriter
*/
protected function createZipWriter()
{
return new CustomZipWriter($this->zipContainer);
}
}

View File

@@ -0,0 +1,28 @@
<?php
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()
{
$now = new \DateTimeImmutable();
$ntfsTimeExtra = NtfsExtraField::create($now, $now->modify('-1 day'), $now->modify('-10 day'));
$unixExtra = new NewUnixExtraField();
foreach ($this->zipContainer->getEntries() as $entry) {
$entry->addExtraField($ntfsTimeExtra);
$entry->addExtraField($unixExtra);
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
/** @noinspection PhpComposerExtensionStubsInspection */
namespace PhpZip\Tests\Internal\Epub;
use PhpZip\Constants\ZipPlatform;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\IO\ZipReader;
use PhpZip\IO\ZipWriter;
use PhpZip\Model\ImmutableZipContainer;
use PhpZip\Model\ZipContainer;
use PhpZip\Model\ZipEntry;
use PhpZip\ZipFile;
/**
* Class EpubFile.
*
* @property EpubZipContainer $zipContainer
*/
class EpubFile extends ZipFile
{
/**
* @return ZipWriter
*/
protected function createZipWriter()
{
return new EpubWriter($this->zipContainer);
}
/**
* @param resource $inputStream
* @param array $options
*
* @return ZipReader
*/
protected function createZipReader($inputStream, array $options = [])
{
return new EpubReader($inputStream, $options);
}
/**
* @param ImmutableZipContainer|null $sourceContainer
*
* @return ZipContainer
*/
protected function createZipContainer(ImmutableZipContainer $sourceContainer = null)
{
return new EpubZipContainer($sourceContainer);
}
/**
* @param ZipEntry $zipEntry
*/
protected function addZipEntry(ZipEntry $zipEntry)
{
$zipEntry->setCreatedOS(ZipPlatform::OS_DOS);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
parent::addZipEntry($zipEntry);
}
/**
* @throws ZipEntryNotFoundException
*
* @return string
*/
public function getMimeType()
{
return $this->zipContainer->getMimeType();
}
public function getEpubInfo()
{
return new EpubInfo($this->getEntryContents($this->getRootFile()));
}
/**
* @throws ZipException
*
* @return string
*/
public function getRootFile()
{
$entryName = 'META-INF/container.xml';
$contents = $this->getEntryContents($entryName);
$doc = new \DOMDocument();
$doc->loadXML($contents);
$xpath = new \DOMXPath($doc);
$rootFile = $xpath->evaluate('string(//@full-path)');
if ($rootFile === '') {
throw new ZipException('Incorrect ' . $entryName . ' file format');
}
return $rootFile;
}
}

View File

@@ -0,0 +1,159 @@
<?php
/** @noinspection PhpComposerExtensionStubsInspection */
namespace PhpZip\Tests\Internal\Epub;
use PhpZip\Exception\ZipException;
/**
* Class EpubInfo.
*
* @see http://idpf.org/epub/30/spec/epub30-publications.html
*/
class EpubInfo
{
/** @var string|null */
private $title;
/** @var string|null */
private $creator;
/** @var string|null */
private $language;
/** @var string|null */
private $publisher;
/** @var string|null */
private $description;
/** @var string|null */
private $rights;
/** @var string|null */
private $date;
/** @var string|null */
private $subject;
/**
* EpubInfo constructor.
*
* @param $xmlContents
*
* @throws ZipException
*/
public function __construct($xmlContents)
{
$doc = new \DOMDocument();
$doc->loadXML($xmlContents);
$xpath = new \DOMXpath($doc);
$xpath->registerNamespace('root', 'http://www.idpf.org/2007/opf');
$metaDataNodeList = $xpath->query('//root:metadata');
if (\count($metaDataNodeList) !== 1) {
throw new ZipException('Invalid .opf file format');
}
$metaDataNode = $metaDataNodeList->item(0);
$title = $xpath->evaluate('string(//dc:title)', $metaDataNode);
$creator = $xpath->evaluate('string(//dc:creator)', $metaDataNode);
$language = $xpath->evaluate('string(//dc:language)', $metaDataNode);
$publisher = $xpath->evaluate('string(//dc:publisher)', $metaDataNode);
$description = $xpath->evaluate('string(//dc:description)', $metaDataNode);
$rights = $xpath->evaluate('string(//dc:rights)', $metaDataNode);
$date = $xpath->evaluate('string(//dc:date)', $metaDataNode);
$subject = $xpath->evaluate('string(//dc:subject)', $metaDataNode);
$this->title = empty($title) ? null : $title;
$this->creator = empty($creator) ? null : $creator;
$this->language = empty($language) ? null : $language;
$this->publisher = empty($publisher) ? null : $publisher;
$this->description = empty($description) ? null : $description;
$this->rights = empty($rights) ? null : $rights;
$this->date = empty($date) ? null : $date;
$this->subject = empty($subject) ? null : $subject;
}
/**
* @return string|null
*/
public function getTitle()
{
return $this->title;
}
/**
* @return string|null
*/
public function getCreator()
{
return $this->creator;
}
/**
* @return string|null
*/
public function getLanguage()
{
return $this->language;
}
/**
* @return string|null
*/
public function getPublisher()
{
return $this->publisher;
}
/**
* @return string|null
*/
public function getDescription()
{
return $this->description;
}
/**
* @return string|null
*/
public function getRights()
{
return $this->rights;
}
/**
* @return string|null
*/
public function getDate()
{
return $this->date;
}
/**
* @return string|null
*/
public function getSubject()
{
return $this->subject;
}
/**
* @return array
*/
public function toArray()
{
return [
'title' => $this->title,
'creator' => $this->creator,
'language' => $this->language,
'publisher' => $this->publisher,
'description' => $this->description,
'rights' => $this->rights,
'date' => $this->date,
'subject' => $this->subject,
];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace PhpZip\Tests\Internal\Epub;
use PhpZip\IO\ZipReader;
/**
* Class EpubReader.
*/
class EpubReader extends ZipReader
{
/**
* @return bool
*
* @see https://github.com/w3c/epubcheck/issues/334
*/
protected function isZip64Support()
{
return false;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace PhpZip\Tests\Internal\Epub;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipPlatform;
use PhpZip\Exception\ZipUnsupportMethodException;
use PhpZip\IO\ZipWriter;
use PhpZip\Model\Data\ZipNewData;
use PhpZip\Model\ZipEntry;
/**
* Class EpubWriter.
*
* @property EpubZipContainer $zipContainer
*/
class EpubWriter extends ZipWriter
{
/**
* @throws ZipUnsupportMethodException
*/
protected function beforeWrite()
{
parent::beforeWrite();
if (!$this->zipContainer->hasEntry('mimetype')) {
$zipEntry = new ZipEntry('mimetype');
$zipEntry->setCreatedOS(ZipPlatform::OS_DOS);
$zipEntry->setExtractedOS(ZipPlatform::OS_DOS);
$zipEntry->setCompressionMethod(ZipCompressionMethod::STORED);
$zipEntry->setData(new ZipNewData($zipEntry, 'application/epub+zip'));
$this->zipContainer->addEntry($zipEntry);
}
$this->sortEntries();
}
private function sortEntries()
{
$this->zipContainer->sortByEntry(
static function (ZipEntry $a, ZipEntry $b) {
if (strcasecmp($a->getName(), 'mimetype') === 0) {
return -1;
}
if (strcasecmp($b->getName(), 'mimetype') === 0) {
return 1;
}
if ($a->isDirectory() && $b->isDirectory()) {
return strcmp($a->getName(), $b->getName());
}
if ($a->isDirectory()) {
return -1;
}
if ($b->isDirectory()) {
return 1;
}
return strcmp($a->getName(), $b->getName());
}
);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace PhpZip\Tests\Internal\Epub;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Model\ZipContainer;
/**
* Class EpubZipContainer.
*/
class EpubZipContainer extends ZipContainer
{
/**
* @throws ZipEntryNotFoundException
*
* @return string
*/
public function getMimeType()
{
return $this->getEntry('mimetype')->getData()->getDataAsString();
}
}

88
tests/SymlinkTest.php Normal file
View File

@@ -0,0 +1,88 @@
<?php
namespace PhpZip\Tests;
use PhpZip\Constants\ZipOptions;
use PhpZip\Util\FilesUtil;
use PhpZip\ZipFile;
use Symfony\Component\Finder\Finder;
/**
* @internal
*
* @small
*/
final class SymlinkTest extends ZipFileTest
{
/**
* 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
*
* @param bool $allowSymlink
*
* @throws \Exception
*/
public function testSymlink($allowSymlink)
{
if (!is_dir($this->outputDirname)) {
self::assertTrue(mkdir($this->outputDirname, 0755, true));
}
$contentsFile = random_bytes(100);
$filePath = $this->outputDirname . '/file.bin';
$symlinkPath = $this->outputDirname . '/symlink.bin';
$symlinkTarget = basename($filePath);
self::assertNotFalse(file_put_contents($filePath, $contentsFile));
self::assertTrue(symlink($symlinkTarget, $symlinkPath));
$finder = (new Finder())->in($this->outputDirname);
$zipFile = new ZipFile();
$zipFile->addFromFinder($finder);
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
FilesUtil::removeDir($this->outputDirname);
self::assertFalse(is_dir($this->outputDirname));
self::assertTrue(mkdir($this->outputDirname, 0755, true));
$zipFile->openFile($this->outputFilename);
$zipFile->extractTo($this->outputDirname, null, [
ZipOptions::EXTRACT_SYMLINKS => $allowSymlink,
]);
$zipFile->close();
$splFileInfo = new \SplFileInfo($symlinkPath);
if ($allowSymlink) {
self::assertTrue($splFileInfo->isLink());
self::assertSame($splFileInfo->getLinkTarget(), $symlinkTarget);
} else {
self::assertFalse($splFileInfo->isLink());
self::assertStringEqualsFile($symlinkPath, $symlinkTarget);
}
}
/**
* @return \Generator
*/
public function provideAllowSymlink()
{
yield 'allow' => [true];
yield 'deny' => [false];
}
}

View File

@@ -2,6 +2,7 @@
namespace PhpZip\Tests;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Exception\ZipException;
use PhpZip\ZipFile;
@@ -32,7 +33,7 @@ class Zip64Test extends ZipTestCase
$zipFile = new ZipFile();
for ($i = 0; $i < $countFiles; $i++) {
$zipFile[$i . '.txt'] = (string) $i;
$zipFile->addFromString($i . '.txt', (string) $i, ZipCompressionMethod::STORED);
}
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();

View File

@@ -266,7 +266,7 @@ class ZipEntryTest extends TestCase
$dosEntryName = DosCodePage::fromUTF8($entryName, $charset);
static::assertSame(DosCodePage::toUTF8($dosEntryName, $charset), $entryName);
$unicodePathExtraField = UnicodePathExtraField::create($entryName);
$unicodePathExtraField = new UnicodePathExtraField(crc32($dosEntryName), $entryName);
$zipEntry = new ZipEntry($dosEntryName, $charset);
static::assertSame($zipEntry->getName(), $dosEntryName);
@@ -304,6 +304,55 @@ class ZipEntryTest extends TestCase
static::assertNull($zipEntry->getData());
}
/**
* @throws \Exception
*/
public function testZipNewDataGuardClone()
{
$resource = fopen('php://temp', 'r+b');
static::assertNotFalse($resource);
fwrite($resource, random_bytes(1024));
rewind($resource);
$zipEntry = new ZipEntry('entry');
$zipEntry2 = new ZipEntry('entry2');
$zipData = new ZipNewData($zipEntry, $resource);
$zipData2 = new ZipNewData($zipEntry2, $resource);
$cloneData = clone $zipData;
$cloneData2 = clone $cloneData;
static::assertSame($zipData->getDataAsStream(), $resource);
static::assertSame($zipData2->getDataAsStream(), $resource);
static::assertSame($cloneData->getDataAsStream(), $resource);
static::assertSame($cloneData2->getDataAsStream(), $resource);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
unset($cloneData);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
unset($zipData);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
unset($zipData2);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
$reflectionClass = new \ReflectionClass($cloneData2);
static::assertSame(
$reflectionClass->getStaticProperties()['guardClonedStream'][(int) $resource],
0
);
unset($cloneData2);
$validResource = \is_resource($resource);
static::assertFalse($validResource);
}
/**
* @dataProvider providePlatform
*
@@ -800,6 +849,12 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidDosTime($dosTime)
{
if (\PHP_INT_SIZE === 4) {
static::markTestSkipped('only 64 bit test');
return;
}
$this->setExpectedException(InvalidArgumentException::class, 'DosTime out of range');
$zipEntry = new ZipEntry('entry');
@@ -951,6 +1006,12 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidExternalAttributes($externalAttributes)
{
if (\PHP_INT_SIZE === 4) {
static::markTestSkipped('only 64 bit test');
return;
}
$this->setExpectedException(InvalidArgumentException::class, 'external attributes out of range');
$zipEntry = new ZipEntry('entry');
@@ -1510,9 +1571,10 @@ class ZipEntryTest extends TestCase
public function testClone()
{
$newUnixExtra = new NewUnixExtraField();
$zipData = new ZipFileData(new \SplFileInfo(__FILE__));
$zipEntry = new ZipEntry('entry');
$zipData = new ZipFileData($zipEntry, new \SplFileInfo(__FILE__));
$zipEntry->addExtraField($newUnixExtra);
$zipEntry->setData($zipData);

View File

@@ -1,41 +0,0 @@
<?php
namespace PhpZip\Tests;
use PhpZip\Exception\ZipException;
use PhpZip\Tests\Internal\ZipFileExtended;
/**
* @internal
*
* @small
*/
class ZipEventTest extends ZipTestCase
{
/**
* @throws ZipException
*/
public function testBeforeSave()
{
$zipFile = new ZipFileExtended();
$zipFile->openFile(__DIR__ . '/resources/apk.zip');
static::assertTrue(isset($zipFile['META-INF/MANIFEST.MF']));
static::assertTrue(isset($zipFile['META-INF/CERT.SF']));
static::assertTrue(isset($zipFile['META-INF/CERT.RSA']));
// the "META-INF/" folder will be deleted when saved
// in the ZipFileExtended::onBeforeSave() method
$zipFile->saveAsFile($this->outputFilename);
static::assertFalse(isset($zipFile['META-INF/MANIFEST.MF']));
static::assertFalse(isset($zipFile['META-INF/CERT.SF']));
static::assertFalse(isset($zipFile['META-INF/CERT.RSA']));
$zipFile->close();
static::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
static::assertFalse(isset($zipFile['META-INF/MANIFEST.MF']));
static::assertFalse(isset($zipFile['META-INF/CERT.SF']));
static::assertFalse(isset($zipFile['META-INF/CERT.RSA']));
$zipFile->close();
}
}

View File

@@ -10,6 +10,7 @@ use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\Exception\ZipUnsupportMethodException;
use PhpZip\Model\Data\ZipFileData;
use PhpZip\Model\ZipEntry;
use PhpZip\Model\ZipInfo;
use PhpZip\Util\FilesUtil;
@@ -1009,16 +1010,16 @@ class ZipFileTest extends ZipTestCase
'test1.txt' => random_bytes(255),
'test2.txt' => random_bytes(255),
'test/test 2/test3.txt' => random_bytes(255),
'test empty/dir' => null,
'test empty/dir/' => null,
];
$zipFile = new ZipFile();
foreach ($entries as $entryName => $value) {
if ($value === null) {
foreach ($entries as $entryName => $contents) {
if ($contents === null) {
$zipFile->addEmptyDir($entryName);
} else {
$zipFile->addFromString($entryName, $value);
$zipFile->addFromString($entryName, $contents);
}
}
$zipFile->saveAsFile($this->outputFilename);
@@ -1027,19 +1028,28 @@ class ZipFileTest extends ZipTestCase
static::assertTrue(mkdir($this->outputDirname, 0755, true));
$zipFile->openFile($this->outputFilename);
$zipFile->extractTo($this->outputDirname);
$zipFile->extractTo($this->outputDirname, null, [], $extractedEntries);
foreach ($entries as $entryName => $value) {
foreach ($entries as $entryName => $contents) {
$fullExtractedFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . $entryName;
if ($value === null) {
static::assertTrue(
isset($extractedEntries[$fullExtractedFilename]),
'No extract info for ' . $fullExtractedFilename
);
if ($contents === null) {
static::assertTrue(is_dir($fullExtractedFilename));
static::assertTrue(FilesUtil::isEmptyDir($fullExtractedFilename));
} else {
static::assertTrue(is_file($fullExtractedFilename));
$contents = file_get_contents($fullExtractedFilename);
static::assertSame($contents, $value);
static::assertSame($contents, $contents);
}
/** @var ZipEntry $entry */
$entry = $extractedEntries[$fullExtractedFilename];
static::assertSame($entry->getName(), $entryName);
}
$zipFile->close();
}
@@ -2418,4 +2428,87 @@ class ZipFileTest extends ZipTestCase
static::assertSame($zipFile->getEntry($newEntryName)->getCompressionMethod(), ZipCompressionMethod::STORED);
$zipFile->close();
}
/**
* @throws ZipEntryNotFoundException
* @throws ZipException
*/
public function testCloneZipContainerInZipWriter()
{
$zipFile = new ZipFile();
$zipFile['file 1'] = 'contents';
$zipEntryBeforeWrite = $zipFile->getEntry('file 1');
$zipFile->saveAsFile($this->outputFilename);
$zipAfterBeforeWrite = $zipFile->getEntry('file 1');
static::assertSame($zipAfterBeforeWrite, $zipEntryBeforeWrite);
$zipFile->close();
}
/**
* @throws ZipException
*/
public function testMultiSave()
{
$zipFile = new ZipFile();
$zipFile['file 1'] = 'contents';
for ($i = 0; $i < 10; $i++) {
$zipFile->saveAsFile($this->outputFilename);
self::assertCorrectZipArchive($this->outputFilename);
}
$zipFile->close();
}
/**
* @throws ZipEntryNotFoundException
* @throws ZipException
*/
public function testNoData()
{
$this->setExpectedException(ZipException::class, 'No data for zip entry file');
$entryName = 'file';
$zipFile = new ZipFile();
try {
$zipFile[$entryName] = '';
$zipEntry = $zipFile->getEntry($entryName);
$zipEntry->setData(null);
$zipFile->getEntryContents($entryName);
} finally {
$zipFile->close();
}
}
/**
* @throws ZipEntryNotFoundException
* @throws ZipException
*/
public function testReplaceEntryContentsByFile()
{
$entryName = basename(__FILE__);
$zipFile = new ZipFile();
$zipFile[$entryName] = 'contents';
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
$zipFile->openFile($this->outputFilename);
$entry = $zipFile->getEntry($entryName);
$data = new ZipFileData($entry, new \SplFileInfo(__FILE__));
$entry->setData($data);
$zipFile->saveAsFile($this->outputFilename);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
static::assertSame(
$zipFile->getEntryContents($entryName),
file_get_contents(__FILE__)
);
$zipFile->close();
}
}

116
tests/ZipInfoTest.php Normal file
View File

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

Binary file not shown.