mirror of
https://github.com/Ne-Lexa/php-zip.git
synced 2025-07-30 20:20:11 +02:00
php cs fix
This commit is contained in:
@@ -6,6 +6,7 @@ php:
|
||||
- '7.1'
|
||||
- '7.2'
|
||||
- '7.3'
|
||||
- '7.4'
|
||||
|
||||
# cache vendor dirs
|
||||
cache:
|
||||
|
@@ -21,8 +21,8 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"ext-zlib": "*",
|
||||
"php": "^5.5 || ^7.0",
|
||||
"ext-zlib": "*",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -45,5 +45,15 @@
|
||||
"ext-bz2": "Needed to support BZIP2 compression",
|
||||
"ext-fileinfo": "Needed to get mime-type file"
|
||||
},
|
||||
"minimum-stability": "stable"
|
||||
"minimum-stability": "stable",
|
||||
"scripts": {
|
||||
"php:fix": "php .php_cs --force",
|
||||
"php:fix:debug": "php .php_cs"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1.x-dev",
|
||||
"dev-develop": "3.2.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,65 +12,287 @@ use PhpZip\Util\PackUtil;
|
||||
* Traditional PKWARE Encryption Engine.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
{
|
||||
/**
|
||||
* Encryption header size
|
||||
*/
|
||||
/** Encryption header size */
|
||||
const STD_DEC_HDR_SIZE = 12;
|
||||
|
||||
/**
|
||||
* Crc table
|
||||
* Crc table.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $CRC_TABLE = [
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
0x00000000,
|
||||
0x77073096,
|
||||
0xee0e612c,
|
||||
0x990951ba,
|
||||
0x076dc419,
|
||||
0x706af48f,
|
||||
0xe963a535,
|
||||
0x9e6495a3,
|
||||
0x0edb8832,
|
||||
0x79dcb8a4,
|
||||
0xe0d5e91e,
|
||||
0x97d2d988,
|
||||
0x09b64c2b,
|
||||
0x7eb17cbd,
|
||||
0xe7b82d07,
|
||||
0x90bf1d91,
|
||||
0x1db71064,
|
||||
0x6ab020f2,
|
||||
0xf3b97148,
|
||||
0x84be41de,
|
||||
0x1adad47d,
|
||||
0x6ddde4eb,
|
||||
0xf4d4b551,
|
||||
0x83d385c7,
|
||||
0x136c9856,
|
||||
0x646ba8c0,
|
||||
0xfd62f97a,
|
||||
0x8a65c9ec,
|
||||
0x14015c4f,
|
||||
0x63066cd9,
|
||||
0xfa0f3d63,
|
||||
0x8d080df5,
|
||||
0x3b6e20c8,
|
||||
0x4c69105e,
|
||||
0xd56041e4,
|
||||
0xa2677172,
|
||||
0x3c03e4d1,
|
||||
0x4b04d447,
|
||||
0xd20d85fd,
|
||||
0xa50ab56b,
|
||||
0x35b5a8fa,
|
||||
0x42b2986c,
|
||||
0xdbbbc9d6,
|
||||
0xacbcf940,
|
||||
0x32d86ce3,
|
||||
0x45df5c75,
|
||||
0xdcd60dcf,
|
||||
0xabd13d59,
|
||||
0x26d930ac,
|
||||
0x51de003a,
|
||||
0xc8d75180,
|
||||
0xbfd06116,
|
||||
0x21b4f4b5,
|
||||
0x56b3c423,
|
||||
0xcfba9599,
|
||||
0xb8bda50f,
|
||||
0x2802b89e,
|
||||
0x5f058808,
|
||||
0xc60cd9b2,
|
||||
0xb10be924,
|
||||
0x2f6f7c87,
|
||||
0x58684c11,
|
||||
0xc1611dab,
|
||||
0xb6662d3d,
|
||||
0x76dc4190,
|
||||
0x01db7106,
|
||||
0x98d220bc,
|
||||
0xefd5102a,
|
||||
0x71b18589,
|
||||
0x06b6b51f,
|
||||
0x9fbfe4a5,
|
||||
0xe8b8d433,
|
||||
0x7807c9a2,
|
||||
0x0f00f934,
|
||||
0x9609a88e,
|
||||
0xe10e9818,
|
||||
0x7f6a0dbb,
|
||||
0x086d3d2d,
|
||||
0x91646c97,
|
||||
0xe6635c01,
|
||||
0x6b6b51f4,
|
||||
0x1c6c6162,
|
||||
0x856530d8,
|
||||
0xf262004e,
|
||||
0x6c0695ed,
|
||||
0x1b01a57b,
|
||||
0x8208f4c1,
|
||||
0xf50fc457,
|
||||
0x65b0d9c6,
|
||||
0x12b7e950,
|
||||
0x8bbeb8ea,
|
||||
0xfcb9887c,
|
||||
0x62dd1ddf,
|
||||
0x15da2d49,
|
||||
0x8cd37cf3,
|
||||
0xfbd44c65,
|
||||
0x4db26158,
|
||||
0x3ab551ce,
|
||||
0xa3bc0074,
|
||||
0xd4bb30e2,
|
||||
0x4adfa541,
|
||||
0x3dd895d7,
|
||||
0xa4d1c46d,
|
||||
0xd3d6f4fb,
|
||||
0x4369e96a,
|
||||
0x346ed9fc,
|
||||
0xad678846,
|
||||
0xda60b8d0,
|
||||
0x44042d73,
|
||||
0x33031de5,
|
||||
0xaa0a4c5f,
|
||||
0xdd0d7cc9,
|
||||
0x5005713c,
|
||||
0x270241aa,
|
||||
0xbe0b1010,
|
||||
0xc90c2086,
|
||||
0x5768b525,
|
||||
0x206f85b3,
|
||||
0xb966d409,
|
||||
0xce61e49f,
|
||||
0x5edef90e,
|
||||
0x29d9c998,
|
||||
0xb0d09822,
|
||||
0xc7d7a8b4,
|
||||
0x59b33d17,
|
||||
0x2eb40d81,
|
||||
0xb7bd5c3b,
|
||||
0xc0ba6cad,
|
||||
0xedb88320,
|
||||
0x9abfb3b6,
|
||||
0x03b6e20c,
|
||||
0x74b1d29a,
|
||||
0xead54739,
|
||||
0x9dd277af,
|
||||
0x04db2615,
|
||||
0x73dc1683,
|
||||
0xe3630b12,
|
||||
0x94643b84,
|
||||
0x0d6d6a3e,
|
||||
0x7a6a5aa8,
|
||||
0xe40ecf0b,
|
||||
0x9309ff9d,
|
||||
0x0a00ae27,
|
||||
0x7d079eb1,
|
||||
0xf00f9344,
|
||||
0x8708a3d2,
|
||||
0x1e01f268,
|
||||
0x6906c2fe,
|
||||
0xf762575d,
|
||||
0x806567cb,
|
||||
0x196c3671,
|
||||
0x6e6b06e7,
|
||||
0xfed41b76,
|
||||
0x89d32be0,
|
||||
0x10da7a5a,
|
||||
0x67dd4acc,
|
||||
0xf9b9df6f,
|
||||
0x8ebeeff9,
|
||||
0x17b7be43,
|
||||
0x60b08ed5,
|
||||
0xd6d6a3e8,
|
||||
0xa1d1937e,
|
||||
0x38d8c2c4,
|
||||
0x4fdff252,
|
||||
0xd1bb67f1,
|
||||
0xa6bc5767,
|
||||
0x3fb506dd,
|
||||
0x48b2364b,
|
||||
0xd80d2bda,
|
||||
0xaf0a1b4c,
|
||||
0x36034af6,
|
||||
0x41047a60,
|
||||
0xdf60efc3,
|
||||
0xa867df55,
|
||||
0x316e8eef,
|
||||
0x4669be79,
|
||||
0xcb61b38c,
|
||||
0xbc66831a,
|
||||
0x256fd2a0,
|
||||
0x5268e236,
|
||||
0xcc0c7795,
|
||||
0xbb0b4703,
|
||||
0x220216b9,
|
||||
0x5505262f,
|
||||
0xc5ba3bbe,
|
||||
0xb2bd0b28,
|
||||
0x2bb45a92,
|
||||
0x5cb36a04,
|
||||
0xc2d7ffa7,
|
||||
0xb5d0cf31,
|
||||
0x2cd99e8b,
|
||||
0x5bdeae1d,
|
||||
0x9b64c2b0,
|
||||
0xec63f226,
|
||||
0x756aa39c,
|
||||
0x026d930a,
|
||||
0x9c0906a9,
|
||||
0xeb0e363f,
|
||||
0x72076785,
|
||||
0x05005713,
|
||||
0x95bf4a82,
|
||||
0xe2b87a14,
|
||||
0x7bb12bae,
|
||||
0x0cb61b38,
|
||||
0x92d28e9b,
|
||||
0xe5d5be0d,
|
||||
0x7cdcefb7,
|
||||
0x0bdbdf21,
|
||||
0x86d3d2d4,
|
||||
0xf1d4e242,
|
||||
0x68ddb3f8,
|
||||
0x1fda836e,
|
||||
0x81be16cd,
|
||||
0xf6b9265b,
|
||||
0x6fb077e1,
|
||||
0x18b74777,
|
||||
0x88085ae6,
|
||||
0xff0f6a70,
|
||||
0x66063bca,
|
||||
0x11010b5c,
|
||||
0x8f659eff,
|
||||
0xf862ae69,
|
||||
0x616bffd3,
|
||||
0x166ccf45,
|
||||
0xa00ae278,
|
||||
0xd70dd2ee,
|
||||
0x4e048354,
|
||||
0x3903b3c2,
|
||||
0xa7672661,
|
||||
0xd06016f7,
|
||||
0x4969474d,
|
||||
0x3e6e77db,
|
||||
0xaed16a4a,
|
||||
0xd9d65adc,
|
||||
0x40df0b66,
|
||||
0x37d83bf0,
|
||||
0xa9bcae53,
|
||||
0xdebb9ec5,
|
||||
0x47b2cf7f,
|
||||
0x30b5ffe9,
|
||||
0xbdbdf21c,
|
||||
0xcabac28a,
|
||||
0x53b39330,
|
||||
0x24b4a3a6,
|
||||
0xbad03605,
|
||||
0xcdd70693,
|
||||
0x54de5729,
|
||||
0x23d967bf,
|
||||
0xb3667a2e,
|
||||
0xc4614ab8,
|
||||
0x5d681b02,
|
||||
0x2a6f2b94,
|
||||
0xb40bbe37,
|
||||
0xc30c8ea1,
|
||||
0x5a05df1b,
|
||||
0x2d02ef8d,
|
||||
];
|
||||
|
||||
/**
|
||||
* Encryption keys
|
||||
* Encryption keys.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $keys = [];
|
||||
/**
|
||||
* @var ZipEntry
|
||||
*/
|
||||
|
||||
/** @var ZipEntry */
|
||||
private $entry;
|
||||
|
||||
/**
|
||||
@@ -84,7 +306,7 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial keys
|
||||
* Initial keys.
|
||||
*
|
||||
* @param string $password
|
||||
*/
|
||||
@@ -93,6 +315,7 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
$this->keys[0] = 305419896;
|
||||
$this->keys[1] = 591751049;
|
||||
$this->keys[2] = 878082192;
|
||||
|
||||
foreach (unpack('C*', $password) as $b) {
|
||||
$this->updateKeys($b);
|
||||
}
|
||||
@@ -105,17 +328,18 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
*/
|
||||
private function updateKeys($charAt)
|
||||
{
|
||||
$this->keys[0] = self::crc32($this->keys[0], $charAt);
|
||||
$this->keys[1] = $this->keys[1] + ($this->keys[0] & 0xff);
|
||||
$this->keys[0] = $this->crc32($this->keys[0], $charAt);
|
||||
$this->keys[1] += ($this->keys[0] & 0xff);
|
||||
$this->keys[1] = PackUtil::toSignedInt32($this->keys[1] * 134775813 + 1);
|
||||
$this->keys[2] = PackUtil::toSignedInt32(self::crc32($this->keys[2], ($this->keys[1] >> 24) & 0xff));
|
||||
$this->keys[2] = PackUtil::toSignedInt32($this->crc32($this->keys[2], ($this->keys[1] >> 24) & 0xff));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update crc.
|
||||
*
|
||||
* @param int $oldCrc
|
||||
* @param string $charAt
|
||||
* @param int $charAt
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function crc32($oldCrc, $charAt)
|
||||
@@ -125,8 +349,10 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipAuthenticationException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decrypt($content)
|
||||
{
|
||||
@@ -135,6 +361,7 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
|
||||
$headerBytes = array_values(unpack('C*', substr($content, 0, self::STD_DEC_HDR_SIZE)));
|
||||
$byte = 0;
|
||||
|
||||
foreach ($headerBytes as &$byte) {
|
||||
$byte = ($byte ^ $this->decryptByte()) & 0xff;
|
||||
$this->updateKeys($byte);
|
||||
@@ -147,19 +374,24 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
// compare against the CRC otherwise
|
||||
$checkByte = ($this->entry->getCrc() >> 24) & 0xff;
|
||||
}
|
||||
|
||||
if ($byte !== $checkByte) {
|
||||
throw new ZipAuthenticationException(sprintf(
|
||||
'Invalid password for zip entry "%s"',
|
||||
$this->entry->getName()
|
||||
));
|
||||
throw new ZipAuthenticationException(
|
||||
sprintf(
|
||||
'Invalid password for zip entry "%s"',
|
||||
$this->entry->getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$outputContent = "";
|
||||
$outputContent = '';
|
||||
|
||||
foreach (unpack('C*', substr($content, self::STD_DEC_HDR_SIZE)) as $val) {
|
||||
$val = ($val ^ $this->decryptByte()) & 0xff;
|
||||
$this->updateKeys($val);
|
||||
$outputContent .= pack('c', $val);
|
||||
}
|
||||
|
||||
return $outputContent;
|
||||
}
|
||||
|
||||
@@ -171,15 +403,18 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
private function decryptByte()
|
||||
{
|
||||
$temp = $this->keys[2] | 2;
|
||||
|
||||
return (($temp * ($temp ^ 1)) >> 8) & 0xffffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encryption data
|
||||
* Encryption data.
|
||||
*
|
||||
* @param string $data
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipCryptoException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encrypt($data)
|
||||
{
|
||||
@@ -196,13 +431,16 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
$headerBytes[self::STD_DEC_HDR_SIZE - 2] = pack('c', ($crc >> 16) & 0xff);
|
||||
|
||||
$headerBytes = $this->encryptData($headerBytes);
|
||||
|
||||
return $headerBytes . $this->encryptData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipCryptoException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function encryptData($content)
|
||||
{
|
||||
@@ -210,20 +448,24 @@ class TraditionalPkwareEncryptionEngine implements ZipEncryptionEngine
|
||||
throw new ZipCryptoException('content is null');
|
||||
}
|
||||
$buff = '';
|
||||
|
||||
foreach (unpack('C*', $content) as $val) {
|
||||
$buff .= pack('c', $this->encryptByte($val));
|
||||
}
|
||||
|
||||
return $buff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $byte
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function encryptByte($byte)
|
||||
{
|
||||
$tempVal = $byte ^ $this->decryptByte() & 0xff;
|
||||
$this->updateKeys($byte);
|
||||
|
||||
return $tempVal;
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ use PhpZip\Util\CryptoUtil;
|
||||
* WinZip Aes Encryption Engine.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
@@ -24,18 +25,18 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
* in bits (AES_BLOCK_SIZE_BITS).
|
||||
*/
|
||||
const AES_BLOCK_SIZE_BITS = 128;
|
||||
|
||||
const PWD_VERIFIER_BITS = 16;
|
||||
/**
|
||||
* The iteration count for the derived keys of the cipher, KLAC and MAC.
|
||||
*/
|
||||
|
||||
/** The iteration count for the derived keys of the cipher, KLAC and MAC. */
|
||||
const ITERATION_COUNT = 1000;
|
||||
/**
|
||||
* @var ZipEntry
|
||||
*/
|
||||
|
||||
/** @var ZipEntry */
|
||||
private $entry;
|
||||
|
||||
/**
|
||||
* WinZipAesEngine constructor.
|
||||
*
|
||||
* @param ZipEntry $entry
|
||||
*/
|
||||
public function __construct(ZipEntry $entry)
|
||||
@@ -47,17 +48,19 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
* Decrypt from stream resource.
|
||||
*
|
||||
* @param string $content Input stream buffer
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipAuthenticationException
|
||||
* @throws ZipCryptoException
|
||||
* @throws \PhpZip\Exception\ZipException
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decrypt($content)
|
||||
{
|
||||
$extraFieldsCollection = $this->entry->getExtraFieldsCollection();
|
||||
|
||||
if (!isset($extraFieldsCollection[WinZipAesEntryExtraField::getHeaderId()])) {
|
||||
throw new ZipCryptoException($this->entry->getName() . " (missing extra field for WinZip AES entry)");
|
||||
throw new ZipCryptoException($this->entry->getName() . ' (missing extra field for WinZip AES entry)');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,32 +81,37 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
|
||||
// Init start, end and size of encrypted data.
|
||||
$start = $pos;
|
||||
$endPos = strlen($content);
|
||||
$endPos = \strlen($content);
|
||||
$footerSize = $sha1Size / 2;
|
||||
$end = $endPos - $footerSize;
|
||||
$size = $end - $start;
|
||||
|
||||
if (0 > $size) {
|
||||
throw new ZipCryptoException($this->entry->getName() . " (false positive WinZip AES entry is too short)");
|
||||
if ($size < 0) {
|
||||
throw new ZipCryptoException($this->entry->getName() . ' (false positive WinZip AES entry is too short)');
|
||||
}
|
||||
|
||||
// Load authentication code.
|
||||
$authenticationCode = substr($content, $end, $footerSize);
|
||||
|
||||
if ($end + $footerSize !== $endPos) {
|
||||
// This should never happen unless someone is writing to the
|
||||
// end of the file concurrently!
|
||||
throw new ZipCryptoException("Expected end of file after WinZip AES authentication code!");
|
||||
throw new ZipCryptoException('Expected end of file after WinZip AES authentication code!');
|
||||
}
|
||||
|
||||
$password = $this->entry->getPassword();
|
||||
assert($password !== null);
|
||||
assert(self::AES_BLOCK_SIZE_BITS <= $keyStrengthBits);
|
||||
\assert($password !== null);
|
||||
\assert($keyStrengthBits >= self::AES_BLOCK_SIZE_BITS);
|
||||
|
||||
// WinZip 99-character limit
|
||||
// @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
|
||||
/**
|
||||
* WinZip 99-character limit.
|
||||
*
|
||||
* @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
|
||||
*/
|
||||
$password = substr($password, 0, 99);
|
||||
$ctrIvSize = self::AES_BLOCK_SIZE_BITS / 8;
|
||||
$iv = str_repeat(chr(0), $ctrIvSize);
|
||||
$iv = str_repeat(\chr(0), $ctrIvSize);
|
||||
|
||||
do {
|
||||
// Here comes the strange part about WinZip AES encryption:
|
||||
// Its unorthodox use of the Password-Based Key Derivation
|
||||
@@ -111,7 +119,7 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
// Yes, the password verifier is only a 16 bit value.
|
||||
// So we must use the MAC for password verification, too.
|
||||
$keyParam = hash_pbkdf2(
|
||||
"sha1",
|
||||
'sha1',
|
||||
$password,
|
||||
$salt,
|
||||
self::ITERATION_COUNT,
|
||||
@@ -126,9 +134,11 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
$content = substr($content, $start, $size);
|
||||
$mac = hash_hmac('sha1', $content, $sha1MacParam, true);
|
||||
|
||||
if (substr($mac, 0, 10) !== $authenticationCode) {
|
||||
throw new ZipAuthenticationException($this->entry->getName() .
|
||||
" (authenticated WinZip AES entry content has been tampered with)");
|
||||
if (strpos($mac, $authenticationCode) !== 0) {
|
||||
throw new ZipAuthenticationException(
|
||||
$this->entry->getName() .
|
||||
' (authenticated WinZip AES entry content has been tampered with)'
|
||||
);
|
||||
}
|
||||
|
||||
return self::aesCtrSegmentIntegerCounter($content, $key, $iv, false);
|
||||
@@ -137,25 +147,27 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
/**
|
||||
* Decryption or encryption AES-CTR with Segment Integer Count (SIC).
|
||||
*
|
||||
* @param string $str Data
|
||||
* @param string $key Key
|
||||
* @param string $iv IV
|
||||
* @param bool $encrypted If true encryption else decryption
|
||||
* @param string $str Data
|
||||
* @param string $key Key
|
||||
* @param string $iv IV
|
||||
* @param bool $encrypted If true encryption else decryption
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function aesCtrSegmentIntegerCounter($str, $key, $iv, $encrypted = true)
|
||||
{
|
||||
$numOfBlocks = ceil(strlen($str) / 16);
|
||||
$numOfBlocks = ceil(\strlen($str) / 16);
|
||||
$ctrStr = '';
|
||||
for ($i = 0; $i < $numOfBlocks; ++$i) {
|
||||
for ($j = 0; $j < 16; ++$j) {
|
||||
$n = ord($iv[$j]);
|
||||
$n = \ord($iv[$j]);
|
||||
|
||||
if (++$n === 0x100) {
|
||||
// overflow, set this one to 0, increment next
|
||||
$iv[$j] = chr(0);
|
||||
$iv[$j] = \chr(0);
|
||||
} else {
|
||||
// no overflow, just write incremented number back and abort
|
||||
$iv[$j] = chr($n);
|
||||
$iv[$j] = \chr($n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -164,6 +176,7 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
self::encryptCtr($data, $key, $iv) :
|
||||
self::decryptCtr($data, $key, $iv);
|
||||
}
|
||||
|
||||
return $ctrStr;
|
||||
}
|
||||
|
||||
@@ -171,78 +184,97 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
* Encrypt AES-CTR.
|
||||
*
|
||||
* @param string $data Raw data
|
||||
* @param string $key Aes key
|
||||
* @param string $iv Aes IV
|
||||
* @param string $key Aes key
|
||||
* @param string $iv Aes IV
|
||||
*
|
||||
* @return string Encrypted data
|
||||
*/
|
||||
private static function encryptCtr($data, $key, $iv)
|
||||
{
|
||||
if (extension_loaded("openssl")) {
|
||||
$numBits = strlen($key) * 8;
|
||||
if (\extension_loaded('openssl')) {
|
||||
$numBits = \strlen($key) * 8;
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return openssl_encrypt($data, 'AES-' . $numBits . '-CTR', $key, OPENSSL_RAW_DATA, $iv);
|
||||
} elseif (extension_loaded("mcrypt")) {
|
||||
/** @noinspection PhpDeprecationInspection */
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, "ctr", $iv);
|
||||
} else {
|
||||
throw new RuntimeException('Extension openssl or mcrypt not loaded');
|
||||
return openssl_encrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
|
||||
}
|
||||
|
||||
if (\extension_loaded('mcrypt')) {
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv);
|
||||
}
|
||||
|
||||
throw new RuntimeException('Extension openssl or mcrypt not loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt AES-CTR.
|
||||
*
|
||||
* @param string $data Encrypted data
|
||||
* @param string $key Aes key
|
||||
* @param string $iv Aes IV
|
||||
* @param string $key Aes key
|
||||
* @param string $iv Aes IV
|
||||
*
|
||||
* @return string Raw data
|
||||
*/
|
||||
private static function decryptCtr($data, $key, $iv)
|
||||
{
|
||||
if (extension_loaded("openssl")) {
|
||||
$numBits = strlen($key) * 8;
|
||||
if (\extension_loaded('openssl')) {
|
||||
$numBits = \strlen($key) * 8;
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return openssl_decrypt($data, 'AES-' . $numBits . '-CTR', $key, OPENSSL_RAW_DATA, $iv);
|
||||
} elseif (extension_loaded("mcrypt")) {
|
||||
/** @noinspection PhpDeprecationInspection */
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, "ctr", $iv);
|
||||
} else {
|
||||
throw new RuntimeException('Extension openssl or mcrypt not loaded');
|
||||
return openssl_decrypt($data, 'AES-' . $numBits . '-CTR', $key, \OPENSSL_RAW_DATA, $iv);
|
||||
}
|
||||
|
||||
if (\extension_loaded('mcrypt')) {
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv);
|
||||
}
|
||||
|
||||
throw new RuntimeException('Extension openssl or mcrypt not loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Encryption string.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
* @throws \PhpZip\Exception\ZipException
|
||||
*/
|
||||
public function encrypt($content)
|
||||
{
|
||||
// Init key strength.
|
||||
$password = $this->entry->getPassword();
|
||||
|
||||
if ($password === null) {
|
||||
throw new ZipException('No password was set for the entry "'.$this->entry->getName().'"');
|
||||
throw new ZipException('No password was set for the entry "' . $this->entry->getName() . '"');
|
||||
}
|
||||
|
||||
// WinZip 99-character limit
|
||||
// @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
|
||||
/**
|
||||
* WinZip 99-character limit.
|
||||
*
|
||||
* @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
|
||||
*/
|
||||
$password = substr($password, 0, 99);
|
||||
|
||||
$keyStrengthBits = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod($this->entry->getEncryptionMethod());
|
||||
$keyStrengthBits = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod(
|
||||
$this->entry->getEncryptionMethod()
|
||||
);
|
||||
$keyStrengthBytes = $keyStrengthBits / 8;
|
||||
|
||||
$salt = CryptoUtil::randomBytes($keyStrengthBytes / 2);
|
||||
|
||||
$keyParam = hash_pbkdf2("sha1", $password, $salt, self::ITERATION_COUNT, (2 * $keyStrengthBits + self::PWD_VERIFIER_BITS) / 8, true);
|
||||
$keyParam = hash_pbkdf2(
|
||||
'sha1',
|
||||
$password,
|
||||
$salt,
|
||||
self::ITERATION_COUNT,
|
||||
(2 * $keyStrengthBits + self::PWD_VERIFIER_BITS) / 8,
|
||||
true
|
||||
);
|
||||
$sha1HMacParam = substr($keyParam, $keyStrengthBytes, $keyStrengthBytes);
|
||||
|
||||
// Can you believe they "forgot" the nonce in the CTR mode IV?! :-(
|
||||
$ctrIvSize = self::AES_BLOCK_SIZE_BITS / 8;
|
||||
$iv = str_repeat(chr(0), $ctrIvSize);
|
||||
$iv = str_repeat(\chr(0), $ctrIvSize);
|
||||
|
||||
$key = substr($keyParam, 0, $keyStrengthBytes);
|
||||
|
||||
@@ -250,10 +282,9 @@ class WinZipAesEngine implements ZipEncryptionEngine
|
||||
|
||||
$mac = hash_hmac('sha1', $content, $sha1HMacParam, true);
|
||||
|
||||
return ($salt .
|
||||
return $salt .
|
||||
substr($keyParam, 2 * $keyStrengthBytes, self::PWD_VERIFIER_BITS / 8) .
|
||||
$content .
|
||||
substr($mac, 0, 10)
|
||||
);
|
||||
substr($mac, 0, 10);
|
||||
}
|
||||
}
|
||||
|
@@ -5,9 +5,10 @@ namespace PhpZip\Crypto;
|
||||
use PhpZip\Exception\ZipAuthenticationException;
|
||||
|
||||
/**
|
||||
* Encryption Engine
|
||||
* Encryption Engine.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
@@ -17,8 +18,10 @@ interface ZipEncryptionEngine
|
||||
* Decryption string.
|
||||
*
|
||||
* @param string $encryptionContent
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipAuthenticationException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decrypt($encryptionContent);
|
||||
|
||||
@@ -26,6 +29,7 @@ interface ZipEncryptionEngine
|
||||
* Encryption string.
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function encrypt($content);
|
||||
|
@@ -32,20 +32,19 @@ class Crc32Exception extends ZipException
|
||||
* Crc32Exception constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $expected
|
||||
* @param int $actual
|
||||
* @param int $expected
|
||||
* @param int $actual
|
||||
*/
|
||||
public function __construct($name, $expected, $actual)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
"%s (expected CRC32 value 0x%x, but is actually 0x%x)",
|
||||
'%s (expected CRC32 value 0x%x, but is actually 0x%x)',
|
||||
$name,
|
||||
$expected,
|
||||
$actual
|
||||
)
|
||||
);
|
||||
assert($expected != $actual);
|
||||
$this->expectedCrc = $expected;
|
||||
$this->actualCrc = $actual;
|
||||
}
|
||||
|
@@ -10,21 +10,22 @@ namespace PhpZip\Exception;
|
||||
*/
|
||||
class ZipEntryNotFoundException extends ZipException
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $entryName;
|
||||
|
||||
/**
|
||||
* ZipEntryNotFoundException constructor.
|
||||
*
|
||||
* @param string $entryName
|
||||
*/
|
||||
public function __construct($entryName)
|
||||
{
|
||||
parent::__construct(sprintf(
|
||||
"Zip Entry \"%s\" was not found in the archive.",
|
||||
$entryName
|
||||
));
|
||||
parent::__construct(
|
||||
sprintf(
|
||||
'Zip Entry "%s" was not found in the archive.',
|
||||
$entryName
|
||||
)
|
||||
);
|
||||
$this->entryName = $entryName;
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@ namespace PhpZip\Exception;
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*
|
||||
* @see \Exception
|
||||
*/
|
||||
class ZipException extends \Exception
|
||||
|
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Exception;
|
||||
|
||||
/**
|
||||
* Thrown if entry not found.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
* @deprecated Rename class exception, using ZipEntryNotFoundException
|
||||
*/
|
||||
class ZipNotFoundEntry extends ZipEntryNotFoundException
|
||||
{
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Exception;
|
||||
|
||||
/**
|
||||
* Thrown if entry unsupport compression method.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
* @deprecated Rename exception class, using ZipUnsupportMethodException
|
||||
*/
|
||||
class ZipUnsupportMethod extends ZipUnsupportMethodException
|
||||
{
|
||||
}
|
@@ -23,12 +23,14 @@ interface ExtraField
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize();
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
*
|
||||
* @param string $data
|
||||
*/
|
||||
public function deserialize($data);
|
||||
|
@@ -31,51 +31,60 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return sizeof($this->collection);
|
||||
return \count($this->collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Extra Field with the given Header ID or null
|
||||
* if no such Extra Field exists.
|
||||
*
|
||||
* @param int $headerId The requested Header ID.
|
||||
* @return ExtraField The Extra Field with the given Header ID or
|
||||
* if no such Extra Field exists.
|
||||
* @throws ZipException If headerId is out of range.
|
||||
* @param int $headerId the requested Header ID
|
||||
*
|
||||
* @throws ZipException if headerId is out of range
|
||||
*
|
||||
* @return ExtraField|null the Extra Field with the given Header ID or
|
||||
* if no such Extra Field exists
|
||||
*/
|
||||
public function get($headerId)
|
||||
{
|
||||
if (0x0000 > $headerId || $headerId > 0xffff) {
|
||||
if ($headerId < 0x0000 || $headerId > 0xffff) {
|
||||
throw new ZipException('headerId out of range');
|
||||
}
|
||||
|
||||
if (isset($this->collection[$headerId])) {
|
||||
return $this->collection[$headerId];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given Extra Field in this collection.
|
||||
*
|
||||
* @param ExtraField $extraField The Extra Field to store in this collection.
|
||||
* @return ExtraField The Extra Field previously associated with the Header ID of
|
||||
* of the given Extra Field or null if no such Extra Field existed.
|
||||
* @throws ZipException If headerId is out of range.
|
||||
* @param ExtraField $extraField the Extra Field to store in this collection
|
||||
*
|
||||
* @throws ZipException if headerId is out of range
|
||||
*
|
||||
* @return ExtraField the Extra Field previously associated with the Header ID of
|
||||
* of the given Extra Field or null if no such Extra Field existed
|
||||
*/
|
||||
public function add(ExtraField $extraField)
|
||||
{
|
||||
$headerId = $extraField::getHeaderId();
|
||||
if (0x0000 > $headerId || $headerId > 0xffff) {
|
||||
|
||||
if ($headerId < 0x0000 || $headerId > 0xffff) {
|
||||
throw new ZipException('headerId out of range');
|
||||
}
|
||||
$this->collection[$headerId] = $extraField;
|
||||
|
||||
return $extraField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Extra Field exists
|
||||
* Returns Extra Field exists.
|
||||
*
|
||||
* @param int $headerId the requested Header ID
|
||||
*
|
||||
* @param int $headerId The requested Header ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function has($headerId)
|
||||
@@ -86,34 +95,43 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Removes the Extra Field with the given Header ID.
|
||||
*
|
||||
* @param int $headerId The requested Header ID.
|
||||
* @return ExtraField The Extra Field with the given Header ID or null
|
||||
* if no such Extra Field exists.
|
||||
* @throws ZipException If headerId is out of range or extra field not found.
|
||||
* @param int $headerId the requested Header ID
|
||||
*
|
||||
* @throws ZipException if headerId is out of range or extra field not found
|
||||
*
|
||||
* @return ExtraField the Extra Field with the given Header ID or null
|
||||
* if no such Extra Field exists
|
||||
*/
|
||||
public function remove($headerId)
|
||||
{
|
||||
if (0x0000 > $headerId || $headerId > 0xffff) {
|
||||
if ($headerId < 0x0000 || $headerId > 0xffff) {
|
||||
throw new ZipException('headerId out of range');
|
||||
}
|
||||
|
||||
if (isset($this->collection[$headerId])) {
|
||||
$ef = $this->collection[$headerId];
|
||||
unset($this->collection[$headerId]);
|
||||
|
||||
return $ef;
|
||||
}
|
||||
|
||||
throw new ZipException('ExtraField not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||
* Whether a offset exists.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* An offset to check for.
|
||||
* </p>
|
||||
* @return boolean true on success or false on failure.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value will be casted to boolean if non-boolean was returned.
|
||||
* An offset to check for.
|
||||
* </p>
|
||||
*
|
||||
* @return bool true on success or false on failure.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value will be casted to boolean if non-boolean was returned.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
@@ -122,14 +140,19 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetget.php
|
||||
* Offset to retrieve.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetget.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* The offset to retrieve.
|
||||
* </p>
|
||||
* @return mixed Can return all value types.
|
||||
* @since 5.0.0
|
||||
* The offset to retrieve.
|
||||
* </p>
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return mixed can return all value types
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
@@ -137,23 +160,26 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetset.php
|
||||
* Offset to set.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetset.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* The offset to assign the value to.
|
||||
* </p>
|
||||
* @param mixed $value <p>
|
||||
* The value to set.
|
||||
* </p>
|
||||
* @return void
|
||||
* The offset to assign the value to.
|
||||
* </p>
|
||||
* @param mixed $value <p>
|
||||
* The value to set.
|
||||
* </p>
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if ($value instanceof ExtraField) {
|
||||
if ($offset !== $value::getHeaderId()) {
|
||||
throw new InvalidArgumentException("Value header id !== array access key");
|
||||
throw new InvalidArgumentException('Value header id !== array access key');
|
||||
}
|
||||
$this->add($value);
|
||||
} else {
|
||||
@@ -162,13 +188,16 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||
* Offset to unset.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* The offset to unset.
|
||||
* </p>
|
||||
* @return void
|
||||
* The offset to unset.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
@@ -177,9 +206,12 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current element
|
||||
* @link http://php.net/manual/en/iterator.current.php
|
||||
* @return mixed Can return any type.
|
||||
* Return the current element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.current.php
|
||||
*
|
||||
* @return mixed can return any type
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function current()
|
||||
@@ -188,9 +220,9 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward to next element
|
||||
* @link http://php.net/manual/en/iterator.next.php
|
||||
* @return void Any returned value is ignored.
|
||||
* Move forward to next element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.next.php
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function next()
|
||||
@@ -199,9 +231,12 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key of the current element
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
* Return the key of the current element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.key.php
|
||||
*
|
||||
* @return mixed scalar on success, or null on failure
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function key()
|
||||
@@ -210,10 +245,13 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid
|
||||
* @link http://php.net/manual/en/iterator.valid.php
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
* Checks if current position is valid.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.valid.php
|
||||
*
|
||||
* @return bool The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function valid()
|
||||
@@ -222,9 +260,9 @@ class ExtraFieldsCollection implements \Countable, \ArrayAccess, \Iterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
* @return void Any returned value is ignored.
|
||||
* Rewind the Iterator to the first element.
|
||||
*
|
||||
* @see http://php.net/manual/en/iterator.rewind.php
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function rewind()
|
||||
|
@@ -13,16 +13,14 @@ use PhpZip\Model\ZipEntry;
|
||||
use PhpZip\Util\StringUtil;
|
||||
|
||||
/**
|
||||
* Extra Fields Factory
|
||||
* Extra Fields Factory.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class ExtraFieldsFactory
|
||||
{
|
||||
/**
|
||||
* @var array|null
|
||||
*/
|
||||
/** @var array|null */
|
||||
protected static $registry;
|
||||
|
||||
private function __construct()
|
||||
@@ -30,18 +28,22 @@ class ExtraFieldsFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extra
|
||||
* @param string $extra
|
||||
* @param ZipEntry|null $entry
|
||||
* @return ExtraFieldsCollection
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ExtraFieldsCollection
|
||||
*/
|
||||
public static function createExtraFieldCollections($extra, ZipEntry $entry = null)
|
||||
{
|
||||
$extraFieldsCollection = new ExtraFieldsCollection();
|
||||
|
||||
if ($extra !== null) {
|
||||
$extraLength = strlen($extra);
|
||||
$extraLength = \strlen($extra);
|
||||
|
||||
if ($extraLength > 0xffff) {
|
||||
throw new ZipException("Extra Fields too large: " . $extraLength);
|
||||
throw new ZipException('Extra Fields too large: ' . $extraLength);
|
||||
}
|
||||
$pos = 0;
|
||||
$endPos = $extraLength;
|
||||
@@ -49,38 +51,53 @@ class ExtraFieldsFactory
|
||||
while ($endPos - $pos >= 4) {
|
||||
$unpack = unpack('vheaderId/vdataSize', substr($extra, $pos, 4));
|
||||
$pos += 4;
|
||||
$headerId = (int)$unpack['headerId'];
|
||||
$dataSize = (int)$unpack['dataSize'];
|
||||
$extraField = ExtraFieldsFactory::create($headerId);
|
||||
$headerId = (int) $unpack['headerId'];
|
||||
$dataSize = (int) $unpack['dataSize'];
|
||||
$extraField = self::create($headerId);
|
||||
|
||||
if ($extraField instanceof Zip64ExtraField && $entry !== null) {
|
||||
$extraField->setEntry($entry);
|
||||
}
|
||||
$extraField->deserialize(substr($extra, $pos, $dataSize));
|
||||
|
||||
if ($dataSize > 0) {
|
||||
$content = substr($extra, $pos, $dataSize);
|
||||
|
||||
if ($content !== false) {
|
||||
$extraField->deserialize($content);
|
||||
$extraFieldsCollection[$headerId] = $extraField;
|
||||
}
|
||||
}
|
||||
|
||||
$pos += $dataSize;
|
||||
$extraFieldsCollection[$headerId] = $extraField;
|
||||
}
|
||||
}
|
||||
|
||||
return $extraFieldsCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ExtraFieldsCollection $extraFieldsCollection
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function createSerializedData(ExtraFieldsCollection $extraFieldsCollection)
|
||||
{
|
||||
$extraData = '';
|
||||
|
||||
foreach ($extraFieldsCollection as $extraField) {
|
||||
$data = $extraField->serialize();
|
||||
$extraData .= pack('vv', $extraField::getHeaderId(), strlen($data));
|
||||
$extraData .= pack('vv', $extraField::getHeaderId(), \strlen($data));
|
||||
$extraData .= $data;
|
||||
}
|
||||
|
||||
$size = strlen($extraData);
|
||||
if (0x0000 > $size || $size > 0xffff) {
|
||||
$size = \strlen($extraData);
|
||||
|
||||
if ($size < 0x0000 || $size > 0xffff) {
|
||||
throw new ZipException('Size extra out of range: ' . $size . '. Extra data: ' . $extraData);
|
||||
}
|
||||
|
||||
return $extraData;
|
||||
}
|
||||
|
||||
@@ -90,29 +107,31 @@ class ExtraFieldsFactory
|
||||
* The returned Extra Field still requires proper initialization, for
|
||||
* example by calling ExtraField::readFrom.
|
||||
*
|
||||
* @param int $headerId An unsigned short integer (two bytes) which indicates
|
||||
* the type of the returned Extra Field.
|
||||
* @return ExtraField A new Extra Field or null if not support header id.
|
||||
* @throws ZipException If headerId is out of range.
|
||||
* @param int $headerId an unsigned short integer (two bytes) which indicates
|
||||
* the type of the returned Extra Field
|
||||
*
|
||||
* @throws ZipException if headerId is out of range
|
||||
*
|
||||
* @return ExtraField a new Extra Field or null if not support header id
|
||||
*/
|
||||
public static function create($headerId)
|
||||
{
|
||||
if (0x0000 > $headerId || $headerId > 0xffff) {
|
||||
if ($headerId < 0x0000 || $headerId > 0xffff) {
|
||||
throw new ZipException('headerId out of range');
|
||||
}
|
||||
|
||||
/**
|
||||
* @var ExtraField $extraField
|
||||
*/
|
||||
if (isset(self::getRegistry()[$headerId])) {
|
||||
$extraClassName = self::getRegistry()[$headerId];
|
||||
$extraField = new $extraClassName;
|
||||
/** @var ExtraField $extraField */
|
||||
$extraField = new $extraClassName();
|
||||
|
||||
if ($headerId !== $extraField::getHeaderId()) {
|
||||
throw new ZipException('Runtime error support headerId ' . $headerId);
|
||||
}
|
||||
} else {
|
||||
$extraField = new DefaultExtraField($headerId);
|
||||
}
|
||||
|
||||
return $extraField;
|
||||
}
|
||||
|
||||
@@ -130,6 +149,7 @@ class ExtraFieldsFactory
|
||||
self::$registry[ApkAlignmentExtraField::getHeaderId()] = ApkAlignmentExtraField::class;
|
||||
self::$registry[JarMarkerExtraField::getHeaderId()] = JarMarkerExtraField::class;
|
||||
}
|
||||
|
||||
return self::$registry;
|
||||
}
|
||||
|
||||
@@ -151,6 +171,7 @@ class ExtraFieldsFactory
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
*
|
||||
* @return Zip64ExtraField
|
||||
*/
|
||||
public static function createZip64Extra(ZipEntry $entry)
|
||||
@@ -160,19 +181,22 @@ class ExtraFieldsFactory
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @param int $padding
|
||||
* @param int $padding
|
||||
*
|
||||
* @return ApkAlignmentExtraField
|
||||
*/
|
||||
public static function createApkAlignExtra(ZipEntry $entry, $padding)
|
||||
{
|
||||
$padding = (int)$padding;
|
||||
$padding = (int) $padding;
|
||||
$multiple = 4;
|
||||
|
||||
if (StringUtil::endsWith($entry->getName(), '.so')) {
|
||||
$multiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
|
||||
}
|
||||
$extraField = new ApkAlignmentExtraField();
|
||||
$extraField->setMultiple($multiple);
|
||||
$extraField->setPadding($padding);
|
||||
|
||||
return $extraField;
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ use PhpZip\Exception\InvalidArgumentException;
|
||||
use PhpZip\Extra\ExtraField;
|
||||
|
||||
/**
|
||||
* Apk Alignment Extra Field
|
||||
* Apk Alignment Extra Field.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
@@ -21,13 +21,10 @@ class ApkAlignmentExtraField implements ExtraField
|
||||
|
||||
const ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $multiple;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $padding;
|
||||
|
||||
/**
|
||||
@@ -44,6 +41,7 @@ class ApkAlignmentExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
@@ -53,25 +51,31 @@ class ApkAlignmentExtraField implements ExtraField
|
||||
['vc*', $this->multiple],
|
||||
array_fill(2, $this->padding, 0)
|
||||
);
|
||||
return call_user_func_array('pack', $args);
|
||||
|
||||
return \call_user_func_array('pack', $args);
|
||||
}
|
||||
|
||||
return pack('v', $this->multiple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
*
|
||||
* @param string $data
|
||||
*/
|
||||
public function deserialize($data)
|
||||
{
|
||||
$length = strlen($data);
|
||||
$length = \strlen($data);
|
||||
|
||||
if ($length < 2) {
|
||||
// This is APK alignment field.
|
||||
// FORMAT:
|
||||
// * uint16 alignment multiple (in bytes)
|
||||
// * remaining bytes -- padding to achieve alignment of data which starts after
|
||||
// the extra field
|
||||
throw new InvalidArgumentException("Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.");
|
||||
throw new InvalidArgumentException(
|
||||
'Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.'
|
||||
);
|
||||
}
|
||||
$this->multiple = unpack('v', $data)[1];
|
||||
$this->padding = $length - 2;
|
||||
|
@@ -14,26 +14,23 @@ use PhpZip\Extra\ExtraField;
|
||||
*/
|
||||
class DefaultExtraField implements ExtraField
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private static $headerId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructs a new Extra Field.
|
||||
*
|
||||
* @param int $headerId an unsigned short integer (two bytes) indicating the
|
||||
* type of the Extra Field.
|
||||
* type of the Extra Field
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function __construct($headerId)
|
||||
{
|
||||
if (0x0000 > $headerId || $headerId > 0xffff) {
|
||||
if ($headerId < 0x0000 || $headerId > 0xffff) {
|
||||
throw new ZipException('headerId out of range');
|
||||
}
|
||||
self::$headerId = $headerId;
|
||||
@@ -53,6 +50,7 @@ class DefaultExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
@@ -62,6 +60,7 @@ class DefaultExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
*
|
||||
* @param string $data
|
||||
*/
|
||||
public function deserialize($data)
|
||||
|
@@ -30,6 +30,7 @@ class JarMarkerExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
@@ -39,12 +40,14 @@ class JarMarkerExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function deserialize($data)
|
||||
{
|
||||
if (strlen($data) !== 0) {
|
||||
if ($data !== '') {
|
||||
throw new ZipException("JarMarker doesn't expect any data");
|
||||
}
|
||||
}
|
||||
|
@@ -6,31 +6,31 @@ use PhpZip\Extra\ExtraField;
|
||||
use PhpZip\Util\PackUtil;
|
||||
|
||||
/**
|
||||
* NTFS Extra Field
|
||||
* NTFS Extra Field.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class NtfsExtraField implements ExtraField
|
||||
{
|
||||
|
||||
/**
|
||||
* Modify time
|
||||
* Modify time.
|
||||
*
|
||||
* @var int Unix Timestamp
|
||||
*/
|
||||
private $mtime;
|
||||
|
||||
/**
|
||||
* Access Time
|
||||
* Access Time.
|
||||
*
|
||||
* @var int Unix Timestamp
|
||||
*/
|
||||
private $atime;
|
||||
|
||||
/**
|
||||
* Create Time
|
||||
* Create Time.
|
||||
*
|
||||
* @var int Unix Time
|
||||
*/
|
||||
@@ -50,11 +50,13 @@ class NtfsExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
*
|
||||
* @param string $data
|
||||
*/
|
||||
public function deserialize($data)
|
||||
{
|
||||
$unpack = unpack('vtag/vsizeAttr', substr($data, 0, 4));
|
||||
|
||||
if ($unpack['sizeAttr'] === 24) {
|
||||
$tagData = substr($data, 4, $unpack['sizeAttr']);
|
||||
$this->mtime = PackUtil::unpackLongLE(substr($tagData, 0, 8)) / 10000000 - 11644473600;
|
||||
@@ -65,11 +67,13 @@ class NtfsExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
$serialize = '';
|
||||
|
||||
if ($this->mtime !== null && $this->atime !== null && $this->ctime !== null) {
|
||||
$mtimeLong = ($this->mtime + 11644473600) * 10000000;
|
||||
$atimeLong = ($this->atime + 11644473600) * 10000000;
|
||||
@@ -80,6 +84,7 @@ class NtfsExtraField implements ExtraField
|
||||
. PackUtil::packLongLE($atimeLong)
|
||||
. PackUtil::packLongLE($ctimeLong);
|
||||
}
|
||||
|
||||
return $serialize;
|
||||
}
|
||||
|
||||
@@ -96,7 +101,7 @@ class NtfsExtraField implements ExtraField
|
||||
*/
|
||||
public function setMtime($mtime)
|
||||
{
|
||||
$this->mtime = (int)$mtime;
|
||||
$this->mtime = (int) $mtime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +117,7 @@ class NtfsExtraField implements ExtraField
|
||||
*/
|
||||
public function setAtime($atime)
|
||||
{
|
||||
$this->atime = (int)$atime;
|
||||
$this->atime = (int) $atime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,6 +133,6 @@ class NtfsExtraField implements ExtraField
|
||||
*/
|
||||
public function setCtime($ctime)
|
||||
{
|
||||
$this->ctime = (int)$ctime;
|
||||
$this->ctime = (int) $ctime;
|
||||
}
|
||||
}
|
||||
|
@@ -9,42 +9,49 @@ use PhpZip\ZipFileInterface;
|
||||
/**
|
||||
* WinZip AES Extra Field.
|
||||
*
|
||||
* @see http://www.winzip.com/win/en/aes_info.htm AES Encryption Information: Encryption Specification AE-1 and AE-2 (WinZip Computing, S.L.)
|
||||
* @see http://www.winzip.com/win/en/aes_info.htm AES Encryption Information: Encryption Specification AE-1 and AE-2
|
||||
* (WinZip Computing, S.L.)
|
||||
* @see http://www.winzip.com/win/en/aes_tips.htm AES Coding Tips for Developers (WinZip Computing, S.L.)
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class WinZipAesEntryExtraField implements ExtraField
|
||||
{
|
||||
const DATA_SIZE = 7;
|
||||
|
||||
const VENDOR_ID = 17729; // 'A' | ('E' << 8);
|
||||
|
||||
/**
|
||||
* Entries of this type <em>do</em> include the standard ZIP CRC-32 value.
|
||||
* For use with @see WinZipAesEntryExtraField::setVendorVersion()}/@see WinZipAesEntryExtraField::getVendorVersion().
|
||||
* For use with @see WinZipAesEntryExtraField::setVendorVersion()}/@see
|
||||
* WinZipAesEntryExtraField::getVendorVersion().
|
||||
*/
|
||||
const VV_AE_1 = 1;
|
||||
|
||||
/**
|
||||
* Entries of this type do <em>not</em> include the standard ZIP CRC-32 value.
|
||||
* For use with @see WinZipAesEntryExtraField::setVendorVersion()}/@see WinZipAesEntryExtraField::getVendorVersion().
|
||||
* For use with @see WinZipAesEntryExtraField::setVendorVersion()}/@see
|
||||
* WinZipAesEntryExtraField::getVendorVersion().
|
||||
*/
|
||||
const VV_AE_2 = 2;
|
||||
|
||||
const KEY_STRENGTH_128BIT = 128;
|
||||
|
||||
const KEY_STRENGTH_192BIT = 192;
|
||||
|
||||
const KEY_STRENGTH_256BIT = 256;
|
||||
|
||||
protected static $keyStrengths = [
|
||||
self::KEY_STRENGTH_128BIT => 0x01,
|
||||
self::KEY_STRENGTH_192BIT => 0x02,
|
||||
self::KEY_STRENGTH_256BIT => 0x03
|
||||
self::KEY_STRENGTH_256BIT => 0x03,
|
||||
];
|
||||
|
||||
protected static $encryptionMethods = [
|
||||
self::KEY_STRENGTH_128BIT => ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128,
|
||||
self::KEY_STRENGTH_192BIT => ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192,
|
||||
self::KEY_STRENGTH_256BIT => ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
self::KEY_STRENGTH_256BIT => ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -94,14 +101,16 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
/**
|
||||
* Sets the vendor version.
|
||||
*
|
||||
* @param int $vendorVersion the vendor version
|
||||
*
|
||||
* @throws ZipException unsupport vendor version
|
||||
*
|
||||
* @see WinZipAesEntryExtraField::VV_AE_1
|
||||
* @see WinZipAesEntryExtraField::VV_AE_2
|
||||
* @param int $vendorVersion the vendor version.
|
||||
* @throws ZipException Unsupport vendor version.
|
||||
*/
|
||||
public function setVendorVersion($vendorVersion)
|
||||
{
|
||||
if ($vendorVersion < self::VV_AE_1 || self::VV_AE_2 < $vendorVersion) {
|
||||
if ($vendorVersion < self::VV_AE_1 || $vendorVersion > self::VV_AE_2) {
|
||||
throw new ZipException($vendorVersion);
|
||||
}
|
||||
$this->vendorVersion = $vendorVersion;
|
||||
@@ -118,8 +127,9 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|int
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
public function getKeyStrength()
|
||||
{
|
||||
@@ -127,16 +137,20 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $encryptionStrength Encryption strength as bits.
|
||||
* @param int $encryptionStrength encryption strength as bits
|
||||
*
|
||||
* @throws ZipException if unsupport encryption strength
|
||||
*
|
||||
* @return int
|
||||
* @throws ZipException If unsupport encryption strength.
|
||||
*/
|
||||
public static function keyStrength($encryptionStrength)
|
||||
{
|
||||
$flipKeyStrength = array_flip(self::$keyStrengths);
|
||||
|
||||
if (!isset($flipKeyStrength[$encryptionStrength])) {
|
||||
throw new ZipException("Unsupport encryption strength " . $encryptionStrength);
|
||||
throw new ZipException('Unsupport encryption strength ' . $encryptionStrength);
|
||||
}
|
||||
|
||||
return $flipKeyStrength[$encryptionStrength];
|
||||
}
|
||||
|
||||
@@ -153,8 +167,9 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
/**
|
||||
* Internal encryption method.
|
||||
*
|
||||
* @return int
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getEncryptionMethod()
|
||||
{
|
||||
@@ -165,15 +180,19 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* @param int $encryptionMethod
|
||||
* @return int
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getKeyStrangeFromEncryptionMethod($encryptionMethod)
|
||||
{
|
||||
$flipKey = array_flip(self::$encryptionMethods);
|
||||
|
||||
if (!isset($flipKey[$encryptionMethod])) {
|
||||
throw new ZipException("Unsupport encryption method " . $encryptionMethod);
|
||||
throw new ZipException('Unsupport encryption method ' . $encryptionMethod);
|
||||
}
|
||||
|
||||
return $flipKey[$encryptionMethod];
|
||||
}
|
||||
|
||||
@@ -181,11 +200,12 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
* Sets compression method.
|
||||
*
|
||||
* @param int $compressionMethod Compression method
|
||||
* @throws ZipException Compression method out of range.
|
||||
*
|
||||
* @throws ZipException compression method out of range
|
||||
*/
|
||||
public function setMethod($compressionMethod)
|
||||
{
|
||||
if (0x0000 > $compressionMethod || $compressionMethod > 0xffff) {
|
||||
if ($compressionMethod < 0x0000 || $compressionMethod > 0xffff) {
|
||||
throw new ZipException('Compression method out of range');
|
||||
}
|
||||
$this->method = $compressionMethod;
|
||||
@@ -204,7 +224,8 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
/**
|
||||
* Returns encryption strength.
|
||||
*
|
||||
* @param int $keyStrength Key strength in bits.
|
||||
* @param int $keyStrength key strength in bits
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function encryptionStrength($keyStrength)
|
||||
@@ -216,6 +237,7 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
@@ -231,13 +253,16 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function deserialize($data)
|
||||
{
|
||||
$size = strlen($data);
|
||||
if (self::DATA_SIZE !== $size) {
|
||||
$size = \strlen($data);
|
||||
|
||||
if ($size !== self::DATA_SIZE) {
|
||||
throw new ZipException('WinZip AES Extra data invalid size: ' . $size . '. Must be ' . self::DATA_SIZE);
|
||||
}
|
||||
|
||||
@@ -249,7 +274,8 @@ class WinZipAesEntryExtraField implements ExtraField
|
||||
*/
|
||||
$unpack = unpack('vvendorVersion/vvendorId/ckeyStrength/vmethod', $data);
|
||||
$this->setVendorVersion($unpack['vendorVersion']);
|
||||
if (self::VENDOR_ID !== $unpack['vendorId']) {
|
||||
|
||||
if ($unpack['vendorId'] !== self::VENDOR_ID) {
|
||||
throw new ZipException('Vendor id invalid: ' . $unpack['vendorId'] . '. Must be ' . self::VENDOR_ID);
|
||||
}
|
||||
$this->setKeyStrength(self::keyStrength($unpack['keyStrength'])); // checked
|
||||
|
@@ -3,14 +3,16 @@
|
||||
namespace PhpZip\Extra\Fields;
|
||||
|
||||
use PhpZip\Exception\RuntimeException;
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Extra\ExtraField;
|
||||
use PhpZip\Model\ZipEntry;
|
||||
use PhpZip\Util\PackUtil;
|
||||
|
||||
/**
|
||||
* ZIP64 Extra Field
|
||||
* ZIP64 Extra Field.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
@@ -18,13 +20,13 @@ class Zip64ExtraField implements ExtraField
|
||||
{
|
||||
/** The Header ID for a ZIP64 Extended Information Extra Field. */
|
||||
const ZIP64_HEADER_ID = 0x0001;
|
||||
/**
|
||||
* @var ZipEntry
|
||||
*/
|
||||
|
||||
/** @var ZipEntry */
|
||||
protected $entry;
|
||||
|
||||
/**
|
||||
* Zip64ExtraField constructor.
|
||||
*
|
||||
* @param ZipEntry $entry
|
||||
*/
|
||||
public function __construct(ZipEntry $entry = null)
|
||||
@@ -56,61 +58,71 @@ class Zip64ExtraField implements ExtraField
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
if ($this->entry === null) {
|
||||
throw new RuntimeException("entry is null");
|
||||
throw new RuntimeException('entry is null');
|
||||
}
|
||||
$data = '';
|
||||
// Write out Uncompressed Size.
|
||||
$size = $this->entry->getSize();
|
||||
if (0xffffffff <= $size) {
|
||||
|
||||
if ($size >= 0xffffffff) {
|
||||
$data .= PackUtil::packLongLE($size);
|
||||
}
|
||||
// Write out Compressed Size.
|
||||
$compressedSize = $this->entry->getCompressedSize();
|
||||
if (0xffffffff <= $compressedSize) {
|
||||
|
||||
if ($compressedSize >= 0xffffffff) {
|
||||
$data .= PackUtil::packLongLE($compressedSize);
|
||||
}
|
||||
// Write out Relative Header Offset.
|
||||
$offset = $this->entry->getOffset();
|
||||
if (0xffffffff <= $offset) {
|
||||
|
||||
if ($offset >= 0xffffffff) {
|
||||
$data .= PackUtil::packLongLE($offset);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
*
|
||||
* @param string $data
|
||||
* @throws \PhpZip\Exception\ZipException
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function deserialize($data)
|
||||
{
|
||||
if ($this->entry === null) {
|
||||
throw new RuntimeException("entry is null");
|
||||
throw new RuntimeException('entry is null');
|
||||
}
|
||||
$off = 0;
|
||||
// Read in Uncompressed Size.
|
||||
$size = $this->entry->getSize();
|
||||
if (0xffffffff <= $size) {
|
||||
assert(0xffffffff === $size);
|
||||
|
||||
if ($size >= 0xffffffff) {
|
||||
\assert($size === 0xffffffff);
|
||||
$this->entry->setSize(PackUtil::unpackLongLE(substr($data, $off, 8)));
|
||||
$off += 8;
|
||||
}
|
||||
// Read in Compressed Size.
|
||||
$compressedSize = $this->entry->getCompressedSize();
|
||||
if (0xffffffff <= $compressedSize) {
|
||||
assert(0xffffffff === $compressedSize);
|
||||
|
||||
if ($compressedSize >= 0xffffffff) {
|
||||
\assert($compressedSize === 0xffffffff);
|
||||
$this->entry->setCompressedSize(PackUtil::unpackLongLE(substr($data, $off, 8)));
|
||||
$off += 8;
|
||||
}
|
||||
// Read in Relative Header Offset.
|
||||
$offset = $this->entry->getOffset();
|
||||
if (0xffffffff <= $offset) {
|
||||
assert(0xffffffff, $offset);
|
||||
|
||||
if ($offset >= 0xffffffff) {
|
||||
\assert(0xffffffff, $offset);
|
||||
$this->entry->setOffset(PackUtil::unpackLongLE(substr($data, $off, 8)));
|
||||
}
|
||||
}
|
||||
|
@@ -10,9 +10,7 @@ namespace PhpZip\Mapper;
|
||||
*/
|
||||
class OffsetPositionMapper extends PositionMapper
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $offset;
|
||||
|
||||
/**
|
||||
@@ -20,11 +18,12 @@ class OffsetPositionMapper extends PositionMapper
|
||||
*/
|
||||
public function __construct($offset)
|
||||
{
|
||||
$this->offset = (int)$offset;
|
||||
$this->offset = (int) $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function map($position)
|
||||
@@ -34,6 +33,7 @@ class OffsetPositionMapper extends PositionMapper
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function unmap($position)
|
||||
|
@@ -12,6 +12,7 @@ class PositionMapper
|
||||
{
|
||||
/**
|
||||
* @param int $position
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function map($position)
|
||||
@@ -21,6 +22,7 @@ class PositionMapper
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function unmap($position)
|
||||
|
@@ -3,7 +3,7 @@
|
||||
namespace PhpZip\Model;
|
||||
|
||||
/**
|
||||
* Read End of Central Directory
|
||||
* Read End of Central Directory.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
@@ -12,10 +12,13 @@ class EndOfCentralDirectory
|
||||
{
|
||||
/** Zip64 End Of Central Directory Record. */
|
||||
const ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG = 0x06064B50;
|
||||
|
||||
/** Zip64 End Of Central Directory Locator. */
|
||||
const ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG = 0x07064B50;
|
||||
|
||||
/** End Of Central Directory Record signature. */
|
||||
const END_OF_CENTRAL_DIRECTORY_RECORD_SIG = 0x06054B50;
|
||||
|
||||
/**
|
||||
* The minimum length of the End Of Central Directory Record.
|
||||
*
|
||||
@@ -34,6 +37,7 @@ class EndOfCentralDirectory
|
||||
* zipfile comment length 2
|
||||
*/
|
||||
const END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN = 22;
|
||||
|
||||
/**
|
||||
* The length of the Zip64 End Of Central Directory Locator.
|
||||
* zip64 end of central dir locator
|
||||
@@ -43,9 +47,10 @@ class EndOfCentralDirectory
|
||||
* central directory 4
|
||||
* relative offset of the zip64
|
||||
* end of central directory record 8
|
||||
* total number of disks 4
|
||||
* total number of disks 4.
|
||||
*/
|
||||
const ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_LEN = 20;
|
||||
|
||||
/**
|
||||
* The minimum length of the Zip64 End Of Central Directory Record.
|
||||
*
|
||||
@@ -68,24 +73,22 @@ class EndOfCentralDirectory
|
||||
* the starting disk number 8
|
||||
*/
|
||||
const ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN = 56;
|
||||
/**
|
||||
* @var string|null The archive comment.
|
||||
*/
|
||||
|
||||
/** @var string|null the archive comment */
|
||||
private $comment;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $entryCount;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $zip64 = false;
|
||||
|
||||
/**
|
||||
* EndOfCentralDirectory constructor.
|
||||
* @param int $entryCount
|
||||
* @param null|string $comment
|
||||
* @param bool $zip64
|
||||
*
|
||||
* @param int $entryCount
|
||||
* @param string|null $comment
|
||||
* @param bool $zip64
|
||||
*/
|
||||
public function __construct($entryCount, $comment, $zip64 = false)
|
||||
{
|
||||
@@ -95,7 +98,7 @@ class EndOfCentralDirectory
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
|
@@ -12,17 +12,14 @@ use PhpZip\Model\ZipEntry;
|
||||
*/
|
||||
class OutputOffsetEntry
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $offset;
|
||||
/**
|
||||
* @var ZipEntry
|
||||
*/
|
||||
|
||||
/** @var ZipEntry */
|
||||
private $entry;
|
||||
|
||||
/**
|
||||
* @param int $pos
|
||||
* @param int $pos
|
||||
* @param ZipEntry $entry
|
||||
*/
|
||||
public function __construct($pos, ZipEntry $entry)
|
||||
|
@@ -16,59 +16,48 @@ use PhpZip\ZipFileInterface;
|
||||
* Abstract ZIP entry.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
abstract class ZipAbstractEntry implements ZipEntry
|
||||
{
|
||||
/**
|
||||
* @var int Bit flags for init state.
|
||||
*/
|
||||
/** @var int bit flags for init state */
|
||||
private $init;
|
||||
/**
|
||||
* @var string Entry name (filename in archive)
|
||||
*/
|
||||
|
||||
/** @var string Entry name (filename in archive) */
|
||||
private $name;
|
||||
/**
|
||||
* @var int Made by platform
|
||||
*/
|
||||
|
||||
/** @var int Made by platform */
|
||||
private $platform;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $versionNeededToExtract = 20;
|
||||
/**
|
||||
* @var int Compression method
|
||||
*/
|
||||
|
||||
/** @var int Compression method */
|
||||
private $method;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $general;
|
||||
/**
|
||||
* @var int Dos time
|
||||
*/
|
||||
|
||||
/** @var int Dos time */
|
||||
private $dosTime;
|
||||
/**
|
||||
* @var int Crc32
|
||||
*/
|
||||
|
||||
/** @var int Crc32 */
|
||||
private $crc;
|
||||
/**
|
||||
* @var int Compressed size
|
||||
*/
|
||||
|
||||
/** @var int Compressed size */
|
||||
private $compressedSize = self::UNKNOWN;
|
||||
/**
|
||||
* @var int Uncompressed size
|
||||
*/
|
||||
|
||||
/** @var int Uncompressed size */
|
||||
private $size = self::UNKNOWN;
|
||||
/**
|
||||
* @var int External attributes
|
||||
*/
|
||||
|
||||
/** @var int External attributes */
|
||||
private $externalAttributes;
|
||||
/**
|
||||
* @var int Relative Offset Of Local File Header.
|
||||
*/
|
||||
|
||||
/** @var int relative Offset Of Local File Header */
|
||||
private $offset = self::UNKNOWN;
|
||||
|
||||
/**
|
||||
* Collections of Extra Fields.
|
||||
* Keys from Header ID [int] and value Extra Field [ExtraField].
|
||||
@@ -77,26 +66,26 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* @var ExtraFieldsCollection
|
||||
*/
|
||||
private $extraFieldsCollection;
|
||||
/**
|
||||
* @var string Comment field.
|
||||
*/
|
||||
|
||||
/** @var string comment field */
|
||||
private $comment;
|
||||
/**
|
||||
* @var string Entry password for read or write encryption data.
|
||||
*/
|
||||
|
||||
/** @var string entry password for read or write encryption data */
|
||||
private $password;
|
||||
|
||||
/**
|
||||
* Encryption method.
|
||||
*
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $encryptionMethod = ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $compressionLevel = ZipFileInterface::LEVEL_DEFAULT_COMPRESSION;
|
||||
|
||||
/**
|
||||
@@ -109,6 +98,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function setEntry(ZipEntry $entry)
|
||||
@@ -146,25 +136,30 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Set entry name.
|
||||
*
|
||||
* @param string $name New entry name
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$length = strlen($name);
|
||||
if (0x0000 > $length || $length > 0xffff) {
|
||||
$length = \strlen($name);
|
||||
|
||||
if ($length < 0x0000 || $length > 0xffff) {
|
||||
throw new ZipException('Illegal zip entry name parameter');
|
||||
}
|
||||
$this->setGeneralPurposeBitFlag(self::GPBF_UTF8, true);
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indexed General Purpose Bit Flag.
|
||||
*
|
||||
* @param int $mask
|
||||
* @param int $mask
|
||||
* @param bool $bit
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setGeneralPurposeBitFlag($mask, $bit)
|
||||
@@ -174,6 +169,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
} else {
|
||||
$this->general &= ~$mask;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -186,29 +182,34 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
}
|
||||
|
||||
/**
|
||||
* Set platform
|
||||
* Set platform.
|
||||
*
|
||||
* @param int $platform
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setPlatform($platform)
|
||||
{
|
||||
$known = self::UNKNOWN !== $platform;
|
||||
$known = $platform !== self::UNKNOWN;
|
||||
|
||||
if ($known) {
|
||||
if (0x00 > $platform || $platform > 0xff) {
|
||||
throw new ZipException("Platform out of range");
|
||||
if ($platform < 0x00 || $platform > 0xff) {
|
||||
throw new ZipException('Platform out of range');
|
||||
}
|
||||
$this->platform = $platform;
|
||||
} else {
|
||||
$this->platform = 0;
|
||||
}
|
||||
$this->setInit(self::BIT_PLATFORM, $known);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $mask
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isInit($mask)
|
||||
@@ -217,7 +218,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $mask
|
||||
* @param int $mask
|
||||
* @param bool $init
|
||||
*/
|
||||
protected function setInit($mask, $init)
|
||||
@@ -243,11 +244,13 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Set version needed to extract.
|
||||
*
|
||||
* @param int $version
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setVersionNeededToExtract($version)
|
||||
{
|
||||
$this->versionNeededToExtract = $version;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -256,8 +259,8 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
*/
|
||||
public function isZip64ExtensionsRequired()
|
||||
{
|
||||
return 0xffffffff <= $this->getCompressedSize()
|
||||
|| 0xffffffff <= $this->getSize();
|
||||
return $this->getCompressedSize() >= 0xffffffff
|
||||
|| $this->getSize() >= 0xffffffff;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,12 +276,14 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
/**
|
||||
* Sets the compressed size of this entry.
|
||||
*
|
||||
* @param int $compressedSize The Compressed Size.
|
||||
* @param int $compressedSize the Compressed Size
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setCompressedSize($compressedSize)
|
||||
{
|
||||
$this->compressedSize = $compressedSize;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -295,12 +300,14 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
/**
|
||||
* Sets the uncompressed size of this entry.
|
||||
*
|
||||
* @param int $size The (Uncompressed) Size.
|
||||
* @param int $size the (Uncompressed) Size
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setSize($size)
|
||||
{
|
||||
$this->size = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -316,16 +323,19 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setOffset($offset)
|
||||
{
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the General Purpose Bit Flags.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getGeneralPurposeBitFlags()
|
||||
@@ -336,19 +346,25 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
/**
|
||||
* Sets the General Purpose Bit Flags.
|
||||
*
|
||||
* @var int general
|
||||
* @return ZipEntry
|
||||
* @param mixed $general
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @var int general
|
||||
*/
|
||||
public function setGeneralPurposeBitFlags($general)
|
||||
{
|
||||
if (0x0000 > $general || $general > 0xffff) {
|
||||
if ($general < 0x0000 || $general > 0xffff) {
|
||||
throw new ZipException('general out of range');
|
||||
}
|
||||
$this->general = $general;
|
||||
|
||||
if ($this->method === ZipFileInterface::METHOD_DEFLATED) {
|
||||
$bit1 = $this->getGeneralPurposeBitFlag(self::GPBF_COMPRESSION_FLAG1);
|
||||
$bit2 = $this->getGeneralPurposeBitFlag(self::GPBF_COMPRESSION_FLAG2);
|
||||
|
||||
if ($bit1 && !$bit2) {
|
||||
$this->compressionLevel = ZipFileInterface::LEVEL_BEST_COMPRESSION;
|
||||
} elseif (!$bit1 && $bit2) {
|
||||
@@ -359,6 +375,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
$this->compressionLevel = ZipFileInterface::LEVEL_DEFAULT_COMPRESSION;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -376,6 +393,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Returns the indexed General Purpose Bit Flag.
|
||||
*
|
||||
* @param int $mask
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getGeneralPurposeBitFlag($mask)
|
||||
@@ -387,24 +405,26 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Sets the encryption property to false and removes any other
|
||||
* encryption artifacts.
|
||||
*
|
||||
* @return ZipEntry
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function disableEncryption()
|
||||
{
|
||||
$this->setEncrypted(false);
|
||||
$headerId = WinZipAesEntryExtraField::getHeaderId();
|
||||
|
||||
if (isset($this->extraFieldsCollection[$headerId])) {
|
||||
/**
|
||||
* @var WinZipAesEntryExtraField $field
|
||||
*/
|
||||
/** @var WinZipAesEntryExtraField $field */
|
||||
$field = $this->extraFieldsCollection[$headerId];
|
||||
if (self::METHOD_WINZIP_AES === $this->getMethod()) {
|
||||
|
||||
if ($this->getMethod() === self::METHOD_WINZIP_AES) {
|
||||
$this->setMethod($field === null ? self::UNKNOWN : $field->getMethod());
|
||||
}
|
||||
unset($this->extraFieldsCollection[$headerId]);
|
||||
}
|
||||
$this->password = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -412,12 +432,14 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Sets the encryption flag for this ZIP entry.
|
||||
*
|
||||
* @param bool $encrypted
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setEncrypted($encrypted)
|
||||
{
|
||||
$encrypted = (bool)$encrypted;
|
||||
$encrypted = (bool) $encrypted;
|
||||
$this->setGeneralPurposeBitFlag(self::GPBF_ENCRYPTED, $encrypted);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -429,6 +451,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
public function getMethod()
|
||||
{
|
||||
$isInit = $this->isInit(self::BIT_METHOD);
|
||||
|
||||
return $isInit ?
|
||||
$this->method & 0xffff :
|
||||
self::UNKNOWN;
|
||||
@@ -438,17 +461,21 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Sets the compression method for this entry.
|
||||
*
|
||||
* @param int $method
|
||||
*
|
||||
* @throws ZipException if method is not STORED, DEFLATED, BZIP2 or UNKNOWN
|
||||
*
|
||||
* @return ZipEntry
|
||||
* @throws ZipException If method is not STORED, DEFLATED, BZIP2 or UNKNOWN.
|
||||
*/
|
||||
public function setMethod($method)
|
||||
{
|
||||
if ($method === self::UNKNOWN) {
|
||||
$this->method = $method;
|
||||
$this->setInit(self::BIT_METHOD, false);
|
||||
|
||||
return $this;
|
||||
}
|
||||
if (0x0000 > $method || $method > 0xffff) {
|
||||
|
||||
if ($method < 0x0000 || $method > 0xffff) {
|
||||
throw new ZipException('method out of range: ' . $method);
|
||||
}
|
||||
switch ($method) {
|
||||
@@ -461,13 +488,14 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ZipException($this->name . " (unsupported compression method $method)");
|
||||
throw new ZipException($this->name . " (unsupported compression method {$method})");
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Unix Timestamp
|
||||
* Get Unix Timestamp.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -476,11 +504,12 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
if (!$this->isInit(self::BIT_DATE_TIME)) {
|
||||
return self::UNKNOWN;
|
||||
}
|
||||
|
||||
return DateTimeConverter::toUnixTimestamp($this->getDosTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Dos Time
|
||||
* Get Dos Time.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -490,14 +519,17 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Dos Time
|
||||
* Set Dos Time.
|
||||
*
|
||||
* @param int $dosTime
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function setDosTime($dosTime)
|
||||
{
|
||||
$dosTime = sprintf('%u', $dosTime);
|
||||
if (0x00000000 > $dosTime || $dosTime > 0xffffffff) {
|
||||
$dosTime = (int) $dosTime;
|
||||
|
||||
if ($dosTime < 0x00000000 || $dosTime > 0xffffffff) {
|
||||
throw new ZipException('DosTime out of range');
|
||||
}
|
||||
$this->dosTime = $dosTime;
|
||||
@@ -508,49 +540,57 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Set time from unix timestamp.
|
||||
*
|
||||
* @param int $unixTimestamp
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setTime($unixTimestamp)
|
||||
{
|
||||
$known = self::UNKNOWN != $unixTimestamp;
|
||||
$known = $unixTimestamp !== self::UNKNOWN;
|
||||
|
||||
if ($known) {
|
||||
$this->dosTime = DateTimeConverter::toDosTime($unixTimestamp);
|
||||
} else {
|
||||
$this->dosTime = 0;
|
||||
}
|
||||
$this->setInit(self::BIT_DATE_TIME, $known);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the external file attributes.
|
||||
*
|
||||
* @return int The external file attributes.
|
||||
* @return int the external file attributes
|
||||
*/
|
||||
public function getExternalAttributes()
|
||||
{
|
||||
if (!$this->isInit(self::BIT_EXTERNAL_ATTR)) {
|
||||
return $this->isDirectory() ? 0x10 : 0;
|
||||
}
|
||||
|
||||
return $this->externalAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the external file attributes.
|
||||
*
|
||||
* @param int $externalAttributes the external file attributes.
|
||||
* @param int $externalAttributes the external file attributes
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setExternalAttributes($externalAttributes)
|
||||
{
|
||||
$known = self::UNKNOWN != $externalAttributes;
|
||||
$known = $externalAttributes !== self::UNKNOWN;
|
||||
|
||||
if ($known) {
|
||||
$this->externalAttributes = $externalAttributes;
|
||||
} else {
|
||||
$this->externalAttributes = 0;
|
||||
}
|
||||
$this->setInit(self::BIT_EXTERNAL_ATTR, $known);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -575,8 +615,10 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
|
||||
/**
|
||||
* Returns a protective copy of the serialized Extra Fields.
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtra()
|
||||
{
|
||||
@@ -591,7 +633,8 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* (application) data.
|
||||
* Consider storing such data in a separate entry instead.
|
||||
*
|
||||
* @param string $data The byte array holding the serialized Extra Fields.
|
||||
* @param string $data the byte array holding the serialized Extra Fields
|
||||
*
|
||||
* @throws ZipException if the serialized Extra Fields exceed 64 KB
|
||||
*/
|
||||
public function setExtra($data)
|
||||
@@ -600,32 +643,36 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns comment entry
|
||||
* Returns comment entry.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
return null !== $this->comment ? $this->comment : "";
|
||||
return $this->comment !== null ? $this->comment : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set entry comment.
|
||||
*
|
||||
* @param $comment
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setComment($comment)
|
||||
{
|
||||
if ($comment !== null) {
|
||||
$commentLength = strlen($comment);
|
||||
if (0x0000 > $commentLength || $commentLength > 0xffff) {
|
||||
throw new ZipException("Comment too long");
|
||||
$commentLength = \strlen($comment);
|
||||
|
||||
if ($commentLength < 0x0000 || $commentLength > 0xffff) {
|
||||
throw new ZipException('Comment too long');
|
||||
}
|
||||
}
|
||||
$this->setGeneralPurposeBitFlag(self::GPBF_UTF8, true);
|
||||
$this->comment = $comment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -634,11 +681,11 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
*/
|
||||
public function isDataDescriptorRequired()
|
||||
{
|
||||
return ($this->getCrc() | $this->getCompressedSize() | $this->getSize()) == self::UNKNOWN;
|
||||
return ($this->getCrc() | $this->getCompressedSize() | $this->getSize()) === self::UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return crc32 content or 0 for WinZip AES v2
|
||||
* Return crc32 content or 0 for WinZip AES v2.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -651,12 +698,14 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
* Set crc32 content.
|
||||
*
|
||||
* @param int $crc
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setCrc($crc)
|
||||
{
|
||||
$this->crc = $crc;
|
||||
$this->setInit(self::BIT_CRC, true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -669,24 +718,29 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password and encryption method from entry
|
||||
* Set password and encryption method from entry.
|
||||
*
|
||||
* @param string $password
|
||||
* @param int|null $encryptionMethod
|
||||
*
|
||||
* @param string $password
|
||||
* @param null|int $encryptionMethod
|
||||
* @return ZipEntry
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setPassword($password, $encryptionMethod = null)
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
if ($encryptionMethod !== null) {
|
||||
$this->setEncryptionMethod($encryptionMethod);
|
||||
}
|
||||
|
||||
if (!empty($this->password)) {
|
||||
$this->setEncrypted(true);
|
||||
} else {
|
||||
$this->disableEncryption();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -699,20 +753,22 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
}
|
||||
|
||||
/**
|
||||
* Set encryption method
|
||||
* Set encryption method.
|
||||
*
|
||||
* @param int $encryptionMethod
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
*
|
||||
* @param int $encryptionMethod
|
||||
* @return ZipEntry
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function setEncryptionMethod($encryptionMethod)
|
||||
{
|
||||
if (null !== $encryptionMethod) {
|
||||
if ($encryptionMethod !== null) {
|
||||
if (
|
||||
$encryptionMethod !== ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL
|
||||
&& $encryptionMethod !== ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128
|
||||
@@ -723,6 +779,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
}
|
||||
$this->encryptionMethod = $encryptionMethod;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -736,6 +793,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
|
||||
/**
|
||||
* @param int $compressionLevel
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setCompressionLevel($compressionLevel = ZipFileInterface::LEVEL_DEFAULT_COMPRESSION)
|
||||
@@ -743,15 +801,18 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
if ($compressionLevel < ZipFileInterface::LEVEL_DEFAULT_COMPRESSION ||
|
||||
$compressionLevel > ZipFileInterface::LEVEL_BEST_COMPRESSION
|
||||
) {
|
||||
throw new InvalidArgumentException('Invalid compression level. Minimum level ' .
|
||||
ZipFileInterface::LEVEL_DEFAULT_COMPRESSION . '. Maximum level ' . ZipFileInterface::LEVEL_BEST_COMPRESSION);
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid compression level. Minimum level ' .
|
||||
ZipFileInterface::LEVEL_DEFAULT_COMPRESSION . '. Maximum level ' . ZipFileInterface::LEVEL_BEST_COMPRESSION
|
||||
);
|
||||
}
|
||||
$this->compressionLevel = $compressionLevel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone extra fields
|
||||
* Clone extra fields.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
|
@@ -2,26 +2,27 @@
|
||||
|
||||
namespace PhpZip\Model\Entry;
|
||||
|
||||
use PhpZip\Exception\InvalidArgumentException;
|
||||
use PhpZip\Exception\ZipException;
|
||||
|
||||
/**
|
||||
* Source Entry Changes
|
||||
* Source Entry Changes.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class ZipChangesEntry extends ZipAbstractEntry
|
||||
{
|
||||
/**
|
||||
* @var ZipSourceEntry
|
||||
*/
|
||||
/** @var ZipSourceEntry */
|
||||
protected $entry;
|
||||
|
||||
/**
|
||||
* ZipChangesEntry constructor.
|
||||
*
|
||||
* @param ZipSourceEntry $entry
|
||||
*
|
||||
* @throws ZipException
|
||||
* @throws \PhpZip\Exception\InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(ZipSourceEntry $entry)
|
||||
{
|
||||
@@ -47,8 +48,9 @@ class ZipChangesEntry extends ZipAbstractEntry
|
||||
/**
|
||||
* Returns an string content of the given entry.
|
||||
*
|
||||
* @return null|string
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEntryContent()
|
||||
{
|
||||
|
@@ -11,23 +11,22 @@ use PhpZip\ZipFileInterface;
|
||||
*/
|
||||
class ZipNewEntry extends ZipAbstractEntry
|
||||
{
|
||||
/**
|
||||
* @var resource|string|null
|
||||
*/
|
||||
/** @var resource|string|null */
|
||||
protected $content;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $clone = false;
|
||||
|
||||
/**
|
||||
* ZipNewEntry constructor.
|
||||
*
|
||||
* @param string|resource|null $content
|
||||
*/
|
||||
public function __construct($content = null)
|
||||
{
|
||||
parent::__construct();
|
||||
if ($content !== null && !is_string($content) && !is_resource($content)) {
|
||||
|
||||
if ($content !== null && !\is_string($content) && !\is_resource($content)) {
|
||||
throw new InvalidArgumentException('invalid content');
|
||||
}
|
||||
$this->content = $content;
|
||||
@@ -36,16 +35,18 @@ class ZipNewEntry extends ZipAbstractEntry
|
||||
/**
|
||||
* Returns an string content of the given entry.
|
||||
*
|
||||
* @return null|string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEntryContent()
|
||||
{
|
||||
if (is_resource($this->content)) {
|
||||
if (\is_resource($this->content)) {
|
||||
if (stream_get_meta_data($this->content)['seekable']) {
|
||||
rewind($this->content);
|
||||
}
|
||||
|
||||
return stream_get_contents($this->content);
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
@@ -57,18 +58,19 @@ class ZipNewEntry extends ZipAbstractEntry
|
||||
public function getVersionNeededToExtract()
|
||||
{
|
||||
$method = $this->getMethod();
|
||||
return self::METHOD_WINZIP_AES === $method ? 51 :
|
||||
|
||||
return $method === self::METHOD_WINZIP_AES ? 51 :
|
||||
(
|
||||
ZipFileInterface::METHOD_BZIP2 === $method ? 46 :
|
||||
$method === ZipFileInterface::METHOD_BZIP2 ? 46 :
|
||||
(
|
||||
$this->isZip64ExtensionsRequired() ? 45 :
|
||||
(ZipFileInterface::METHOD_DEFLATED === $method || $this->isDirectory() ? 20 : 10)
|
||||
$this->isZip64ExtensionsRequired() ? 45 :
|
||||
($method === ZipFileInterface::METHOD_DEFLATED || $this->isDirectory() ? 20 : 10)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone extra fields
|
||||
* Clone extra fields.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
@@ -78,7 +80,7 @@ class ZipNewEntry extends ZipAbstractEntry
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (!$this->clone && $this->content !== null && is_resource($this->content)) {
|
||||
if (!$this->clone && $this->content !== null && \is_resource($this->content)) {
|
||||
fclose($this->content);
|
||||
$this->content = null;
|
||||
}
|
||||
|
@@ -12,28 +12,31 @@ use PhpZip\Exception\ZipException;
|
||||
*/
|
||||
class ZipNewFileEntry extends ZipAbstractEntry
|
||||
{
|
||||
/**
|
||||
* @var string Filename
|
||||
*/
|
||||
/** @var string Filename */
|
||||
protected $file;
|
||||
|
||||
/**
|
||||
* ZipNewEntry constructor.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function __construct($file)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if ($file === null) {
|
||||
throw new InvalidArgumentException("file is null");
|
||||
throw new InvalidArgumentException('file is null');
|
||||
}
|
||||
$file = (string)$file;
|
||||
$file = (string) $file;
|
||||
|
||||
if (!is_file($file)) {
|
||||
throw new ZipException("File $file does not exist.");
|
||||
throw new ZipException("File {$file} does not exist.");
|
||||
}
|
||||
|
||||
if (!is_readable($file)) {
|
||||
throw new ZipException("The '$file' file could not be read. Check permissions.");
|
||||
throw new ZipException("The '{$file}' file could not be read. Check permissions.");
|
||||
}
|
||||
$this->file = $file;
|
||||
}
|
||||
@@ -41,13 +44,14 @@ class ZipNewFileEntry extends ZipAbstractEntry
|
||||
/**
|
||||
* Returns an string content of the given entry.
|
||||
*
|
||||
* @return null|string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEntryContent()
|
||||
{
|
||||
if (!is_file($this->file)) {
|
||||
throw new RuntimeException("File {$this->file} does not exist.");
|
||||
}
|
||||
|
||||
return file_get_contents($this->file);
|
||||
}
|
||||
}
|
||||
|
@@ -9,34 +9,27 @@ use PhpZip\Stream\ZipInputStreamInterface;
|
||||
* This class is used to represent a ZIP file entry.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class ZipSourceEntry extends ZipAbstractEntry
|
||||
{
|
||||
/**
|
||||
* Max size cached content in memory.
|
||||
*/
|
||||
/** Max size cached content in memory. */
|
||||
const MAX_SIZE_CACHED_CONTENT_IN_MEMORY = 524288; // 512 kb
|
||||
/**
|
||||
* @var ZipInputStreamInterface
|
||||
*/
|
||||
|
||||
/** @var ZipInputStreamInterface */
|
||||
protected $inputStream;
|
||||
/**
|
||||
* @var string|resource Cached entry content.
|
||||
*/
|
||||
|
||||
/** @var string|resource cached entry content */
|
||||
protected $entryContent;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $readPassword;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $clone = false;
|
||||
|
||||
/**
|
||||
* ZipSourceEntry constructor.
|
||||
*
|
||||
* @param ZipInputStreamInterface $inputStream
|
||||
*/
|
||||
public function __construct(ZipInputStreamInterface $inputStream)
|
||||
@@ -56,6 +49,8 @@ class ZipSourceEntry extends ZipAbstractEntry
|
||||
/**
|
||||
* Returns an string content of the given entry.
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEntryContent()
|
||||
@@ -63,23 +58,28 @@ class ZipSourceEntry extends ZipAbstractEntry
|
||||
if ($this->entryContent === null) {
|
||||
// In order not to unpack again, we cache the content in memory or on disk
|
||||
$content = $this->inputStream->readEntryContent($this);
|
||||
|
||||
if ($this->getSize() < self::MAX_SIZE_CACHED_CONTENT_IN_MEMORY) {
|
||||
$this->entryContent = $content;
|
||||
} else {
|
||||
$this->entryContent = fopen('php://temp', 'r+b');
|
||||
fwrite($this->entryContent, $content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
if (is_resource($this->entryContent)) {
|
||||
|
||||
if (\is_resource($this->entryContent)) {
|
||||
rewind($this->entryContent);
|
||||
|
||||
return stream_get_contents($this->entryContent);
|
||||
}
|
||||
|
||||
return $this->entryContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone extra fields
|
||||
* Clone extra fields.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
@@ -89,7 +89,7 @@ class ZipSourceEntry extends ZipAbstractEntry
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (!$this->clone && $this->entryContent !== null && is_resource($this->entryContent)) {
|
||||
if (!$this->clone && $this->entryContent !== null && \is_resource($this->entryContent)) {
|
||||
fclose($this->entryContent);
|
||||
}
|
||||
}
|
||||
|
@@ -10,26 +10,32 @@ use PhpZip\ZipFileInterface;
|
||||
* ZIP file entry.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
interface ZipEntry
|
||||
{
|
||||
// Bit masks for initialized fields.
|
||||
const BIT_PLATFORM = 1,
|
||||
BIT_METHOD = 2 /* 1 << 1 */,
|
||||
BIT_CRC = 4 /* 1 << 2 */,
|
||||
BIT_DATE_TIME = 64 /* 1 << 6 */,
|
||||
BIT_EXTERNAL_ATTR = 128 /* 1 << 7*/
|
||||
;
|
||||
const BIT_PLATFORM = 1;
|
||||
|
||||
const BIT_METHOD = 2;
|
||||
|
||||
const BIT_CRC = 4;
|
||||
|
||||
const BIT_DATE_TIME = 64;
|
||||
|
||||
const BIT_EXTERNAL_ATTR = 128;
|
||||
|
||||
/** The unknown value for numeric properties. */
|
||||
const UNKNOWN = -1;
|
||||
|
||||
/** Windows platform. */
|
||||
const PLATFORM_FAT = 0;
|
||||
|
||||
/** Unix platform. */
|
||||
const PLATFORM_UNIX = 3;
|
||||
|
||||
/** MacOS platform */
|
||||
const PLATFORM_OS_X = 19;
|
||||
|
||||
@@ -40,7 +46,9 @@ interface ZipEntry
|
||||
const METHOD_WINZIP_AES = 99;
|
||||
|
||||
/** General Purpose Bit Flag mask for encrypted data. */
|
||||
const GPBF_ENCRYPTED = 1; // 1 << 0
|
||||
const GPBF_ENCRYPTED = 1;
|
||||
|
||||
// 1 << 0
|
||||
// (For Methods 8 and 9 - Deflating)
|
||||
// Bit 2 Bit 1
|
||||
// 0 0 Normal compression
|
||||
@@ -48,18 +56,24 @@ interface ZipEntry
|
||||
// 1 0 Fast compression
|
||||
// 1 1 Super Fast compression
|
||||
const GPBF_COMPRESSION_FLAG1 = 2; // 1 << 1
|
||||
|
||||
const GPBF_COMPRESSION_FLAG2 = 4; // 1 << 2
|
||||
|
||||
/** General Purpose Bit Flag mask for data descriptor. */
|
||||
const GPBF_DATA_DESCRIPTOR = 8; // 1 << 3
|
||||
|
||||
/** General Purpose Bit Flag mask for strong encryption. */
|
||||
const GPBF_STRONG_ENCRYPTION = 64; // 1 << 6
|
||||
|
||||
/** General Purpose Bit Flag mask for UTF-8. */
|
||||
const GPBF_UTF8 = 2048; // 1 << 11
|
||||
|
||||
/** Local File Header signature. */
|
||||
const LOCAL_FILE_HEADER_SIG = 0x04034B50;
|
||||
|
||||
/** Data Descriptor signature. */
|
||||
const DATA_DESCRIPTOR_SIG = 0x08074B50;
|
||||
|
||||
/**
|
||||
* The minimum length of the Local File Header record.
|
||||
*
|
||||
@@ -76,6 +90,7 @@ interface ZipEntry
|
||||
* extra field length 2
|
||||
*/
|
||||
const LOCAL_FILE_HEADER_MIN_LEN = 30;
|
||||
|
||||
/**
|
||||
* Local File Header signature 4
|
||||
* Version Needed To Extract 2
|
||||
@@ -85,12 +100,11 @@ interface ZipEntry
|
||||
* Last Mod File Date 2
|
||||
* CRC-32 4
|
||||
* Compressed Size 4
|
||||
* Uncompressed Size 4
|
||||
* Uncompressed Size 4.
|
||||
*/
|
||||
const LOCAL_FILE_HEADER_FILE_NAME_LENGTH_POS = 26;
|
||||
/**
|
||||
* Default compression level for bzip2
|
||||
*/
|
||||
|
||||
/** Default compression level for bzip2 */
|
||||
const LEVEL_DEFAULT_BZIP2_COMPRESSION = 4;
|
||||
|
||||
/**
|
||||
@@ -104,8 +118,10 @@ interface ZipEntry
|
||||
* Set entry name.
|
||||
*
|
||||
* @param string $name New entry name
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setName($name);
|
||||
|
||||
@@ -115,11 +131,13 @@ interface ZipEntry
|
||||
public function getPlatform();
|
||||
|
||||
/**
|
||||
* Set platform
|
||||
* Set platform.
|
||||
*
|
||||
* @param int $platform
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setPlatform($platform);
|
||||
|
||||
@@ -134,6 +152,7 @@ interface ZipEntry
|
||||
* Set version needed to extract.
|
||||
*
|
||||
* @param int $version
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setVersionNeededToExtract($version);
|
||||
@@ -153,9 +172,11 @@ interface ZipEntry
|
||||
/**
|
||||
* Sets the compressed size of this entry.
|
||||
*
|
||||
* @param int $compressedSize The Compressed Size.
|
||||
* @return ZipEntry
|
||||
* @param int $compressedSize the Compressed Size
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setCompressedSize($compressedSize);
|
||||
|
||||
@@ -169,9 +190,11 @@ interface ZipEntry
|
||||
/**
|
||||
* Sets the uncompressed size of this entry.
|
||||
*
|
||||
* @param int $size The (Uncompressed) Size.
|
||||
* @return ZipEntry
|
||||
* @param int $size the (Uncompressed) Size
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setSize($size);
|
||||
|
||||
@@ -184,8 +207,10 @@ interface ZipEntry
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setOffset($offset);
|
||||
|
||||
@@ -207,9 +232,13 @@ interface ZipEntry
|
||||
/**
|
||||
* Sets the General Purpose Bit Flags.
|
||||
*
|
||||
* @var int general
|
||||
* @return ZipEntry
|
||||
* @param mixed $general
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @var int general
|
||||
*/
|
||||
public function setGeneralPurposeBitFlags($general);
|
||||
|
||||
@@ -217,6 +246,7 @@ interface ZipEntry
|
||||
* Returns the indexed General Purpose Bit Flag.
|
||||
*
|
||||
* @param int $mask
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getGeneralPurposeBitFlag($mask);
|
||||
@@ -224,8 +254,9 @@ interface ZipEntry
|
||||
/**
|
||||
* Sets the indexed General Purpose Bit Flag.
|
||||
*
|
||||
* @param int $mask
|
||||
* @param int $mask
|
||||
* @param bool $bit
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setGeneralPurposeBitFlag($mask, $bit);
|
||||
@@ -241,6 +272,7 @@ interface ZipEntry
|
||||
* Sets the encryption flag for this ZIP entry.
|
||||
*
|
||||
* @param bool $encrypted
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setEncrypted($encrypted);
|
||||
@@ -264,13 +296,15 @@ interface ZipEntry
|
||||
* Sets the compression method for this entry.
|
||||
*
|
||||
* @param int $method
|
||||
*
|
||||
* @throws ZipException if method is not STORED, DEFLATED, BZIP2 or UNKNOWN
|
||||
*
|
||||
* @return ZipEntry
|
||||
* @throws ZipException If method is not STORED, DEFLATED, BZIP2 or UNKNOWN.
|
||||
*/
|
||||
public function setMethod($method);
|
||||
|
||||
/**
|
||||
* Get Unix Timestamp
|
||||
* Get Unix Timestamp.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -280,20 +314,23 @@ interface ZipEntry
|
||||
* Set time from unix timestamp.
|
||||
*
|
||||
* @param int $unixTimestamp
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setTime($unixTimestamp);
|
||||
|
||||
/**
|
||||
* Get Dos Time
|
||||
* Get Dos Time.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDosTime();
|
||||
|
||||
/**
|
||||
* Set Dos Time
|
||||
* Set Dos Time.
|
||||
*
|
||||
* @param int $dosTime
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function setDosTime($dosTime);
|
||||
@@ -301,16 +338,18 @@ interface ZipEntry
|
||||
/**
|
||||
* Returns the external file attributes.
|
||||
*
|
||||
* @return int The external file attributes.
|
||||
* @return int the external file attributes
|
||||
*/
|
||||
public function getExternalAttributes();
|
||||
|
||||
/**
|
||||
* Sets the external file attributes.
|
||||
*
|
||||
* @param int $externalAttributes the external file attributes.
|
||||
* @return ZipEntry
|
||||
* @param int $externalAttributes the external file attributes
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setExternalAttributes($externalAttributes);
|
||||
|
||||
@@ -335,13 +374,14 @@ interface ZipEntry
|
||||
* (application) data.
|
||||
* Consider storing such data in a separate entry instead.
|
||||
*
|
||||
* @param string $data The byte array holding the serialized Extra Fields.
|
||||
* @param string $data the byte array holding the serialized Extra Fields
|
||||
*
|
||||
* @throws ZipException if the serialized Extra Fields exceed 64 KB
|
||||
*/
|
||||
public function setExtra($data);
|
||||
|
||||
/**
|
||||
* Returns comment entry
|
||||
* Returns comment entry.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -351,6 +391,7 @@ interface ZipEntry
|
||||
* Set entry comment.
|
||||
*
|
||||
* @param $comment
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setComment($comment);
|
||||
@@ -361,7 +402,7 @@ interface ZipEntry
|
||||
public function isDataDescriptorRequired();
|
||||
|
||||
/**
|
||||
* Return crc32 content or 0 for WinZip AES v2
|
||||
* Return crc32 content or 0 for WinZip AES v2.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -371,8 +412,10 @@ interface ZipEntry
|
||||
* Set crc32 content.
|
||||
*
|
||||
* @param int $crc
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setCrc($crc);
|
||||
|
||||
@@ -382,10 +425,11 @@ interface ZipEntry
|
||||
public function getPassword();
|
||||
|
||||
/**
|
||||
* Set password and encryption method from entry
|
||||
* Set password and encryption method from entry.
|
||||
*
|
||||
* @param string $password
|
||||
* @param int|null $encryptionMethod
|
||||
*
|
||||
* @param string $password
|
||||
* @param null|int $encryptionMethod
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setPassword($password, $encryptionMethod = null);
|
||||
@@ -396,29 +440,33 @@ interface ZipEntry
|
||||
public function getEncryptionMethod();
|
||||
|
||||
/**
|
||||
* Set encryption method
|
||||
* Set encryption method.
|
||||
*
|
||||
* @param int $encryptionMethod
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192
|
||||
* @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
*
|
||||
* @param int $encryptionMethod
|
||||
* @return ZipEntry
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function setEncryptionMethod($encryptionMethod);
|
||||
|
||||
/**
|
||||
* Returns an string content of the given entry.
|
||||
*
|
||||
* @return null|string
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEntryContent();
|
||||
|
||||
/**
|
||||
* @param int $compressionLevel
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function setCompressionLevel($compressionLevel = ZipFileInterface::LEVEL_DEFAULT_COMPRESSION);
|
||||
|
@@ -8,18 +8,15 @@ namespace PhpZip\Model;
|
||||
*/
|
||||
class ZipEntryMatcher implements \Countable
|
||||
{
|
||||
/**
|
||||
* @var ZipModel
|
||||
*/
|
||||
/** @var ZipModel */
|
||||
protected $zipModel;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @var array */
|
||||
protected $matches = [];
|
||||
|
||||
/**
|
||||
* ZipEntryMatcher constructor.
|
||||
*
|
||||
* @param ZipModel $zipModel
|
||||
*/
|
||||
public function __construct(ZipModel $zipModel)
|
||||
@@ -29,44 +26,59 @@ class ZipEntryMatcher implements \Countable
|
||||
|
||||
/**
|
||||
* @param string|array $entries
|
||||
*
|
||||
* @return ZipEntryMatcher
|
||||
*/
|
||||
public function add($entries)
|
||||
{
|
||||
$entries = (array)$entries;
|
||||
$entries = array_map(function ($entry) {
|
||||
return $entry instanceof ZipEntry ? $entry->getName() : $entry;
|
||||
}, $entries);
|
||||
$this->matches = array_unique(
|
||||
array_merge(
|
||||
$this->matches,
|
||||
array_keys(
|
||||
array_intersect_key(
|
||||
$this->zipModel->getEntries(),
|
||||
array_flip($entries)
|
||||
$entries = (array) $entries;
|
||||
$entries = array_map(
|
||||
static function ($entry) {
|
||||
return $entry instanceof ZipEntry ? $entry->getName() : (string) $entry;
|
||||
},
|
||||
$entries
|
||||
);
|
||||
$this->matches = array_values(
|
||||
array_map(
|
||||
'strval',
|
||||
array_unique(
|
||||
array_merge(
|
||||
$this->matches,
|
||||
array_keys(
|
||||
array_intersect_key(
|
||||
$this->zipModel->getEntries(),
|
||||
array_flip($entries)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $regexp
|
||||
*
|
||||
* @return ZipEntryMatcher
|
||||
*/
|
||||
public function match($regexp)
|
||||
{
|
||||
array_walk($this->zipModel->getEntries(), function (
|
||||
/** @noinspection PhpUnusedParameterInspection */
|
||||
$entry,
|
||||
$entryName
|
||||
) use ($regexp) {
|
||||
if (preg_match($regexp, $entryName)) {
|
||||
$this->matches[] = $entryName;
|
||||
array_walk(
|
||||
$this->zipModel->getEntries(),
|
||||
function (
|
||||
/** @noinspection PhpUnusedParameterInspection */
|
||||
$entry,
|
||||
$entryName
|
||||
) use ($regexp) {
|
||||
if (preg_match($regexp, $entryName)) {
|
||||
$this->matches[] = (string) $entryName;
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
$this->matches = array_unique($this->matches);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -76,6 +88,7 @@ class ZipEntryMatcher implements \Countable
|
||||
public function all()
|
||||
{
|
||||
$this->matches = array_keys($this->zipModel->getEntries());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -90,9 +103,12 @@ class ZipEntryMatcher implements \Countable
|
||||
public function invoke(callable $callable)
|
||||
{
|
||||
if (!empty($this->matches)) {
|
||||
array_walk($this->matches, function ($entryName) use ($callable) {
|
||||
call_user_func($callable, $entryName);
|
||||
});
|
||||
array_walk(
|
||||
$this->matches,
|
||||
static function ($entryName) use ($callable) {
|
||||
$callable($entryName);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,24 +122,31 @@ class ZipEntryMatcher implements \Countable
|
||||
|
||||
public function delete()
|
||||
{
|
||||
array_walk($this->matches, function ($entry) {
|
||||
$this->zipModel->deleteEntry($entry);
|
||||
});
|
||||
array_walk(
|
||||
$this->matches,
|
||||
function ($entry) {
|
||||
$this->zipModel->deleteEntry($entry);
|
||||
}
|
||||
);
|
||||
$this->matches = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $password
|
||||
* @param int|null $encryptionMethod
|
||||
* @param int|null $encryptionMethod
|
||||
*/
|
||||
public function setPassword($password, $encryptionMethod = null)
|
||||
{
|
||||
array_walk($this->matches, function ($entry) use ($password, $encryptionMethod) {
|
||||
$entry = $this->zipModel->getEntry($entry);
|
||||
if (!$entry->isDirectory()) {
|
||||
$this->zipModel->getEntryForChanges($entry)->setPassword($password, $encryptionMethod);
|
||||
array_walk(
|
||||
$this->matches,
|
||||
function ($entry) use ($password, $encryptionMethod) {
|
||||
$entry = $this->zipModel->getEntry($entry);
|
||||
|
||||
if (!$entry->isDirectory()) {
|
||||
$this->zipModel->getEntryForChanges($entry)->setPassword($password, $encryptionMethod);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,36 +154,47 @@ class ZipEntryMatcher implements \Countable
|
||||
*/
|
||||
public function setEncryptionMethod($encryptionMethod)
|
||||
{
|
||||
array_walk($this->matches, function ($entry) use ($encryptionMethod) {
|
||||
$entry = $this->zipModel->getEntry($entry);
|
||||
if (!$entry->isDirectory()) {
|
||||
$this->zipModel->getEntryForChanges($entry)->setEncryptionMethod($encryptionMethod);
|
||||
array_walk(
|
||||
$this->matches,
|
||||
function ($entry) use ($encryptionMethod) {
|
||||
$entry = $this->zipModel->getEntry($entry);
|
||||
|
||||
if (!$entry->isDirectory()) {
|
||||
$this->zipModel->getEntryForChanges($entry)->setEncryptionMethod($encryptionMethod);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
public function disableEncryption()
|
||||
{
|
||||
array_walk($this->matches, function ($entry) {
|
||||
$entry = $this->zipModel->getEntry($entry);
|
||||
if (!$entry->isDirectory()) {
|
||||
$entry = $this->zipModel->getEntryForChanges($entry);
|
||||
$entry->disableEncryption();
|
||||
array_walk(
|
||||
$this->matches,
|
||||
function ($entry) {
|
||||
$entry = $this->zipModel->getEntry($entry);
|
||||
|
||||
if (!$entry->isDirectory()) {
|
||||
$entry = $this->zipModel->getEntryForChanges($entry);
|
||||
$entry->disableEncryption();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
* @link http://php.net/manual/en/countable.count.php
|
||||
* Count elements of an object.
|
||||
*
|
||||
* @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.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value is cast to an integer.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->matches);
|
||||
return \count($this->matches);
|
||||
}
|
||||
}
|
||||
|
@@ -1,14 +1,15 @@
|
||||
<?php /** @noinspection PhpMissingBreakStatementInspection */
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Model;
|
||||
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Extra\Fields\NtfsExtraField;
|
||||
use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
|
||||
use PhpZip\Util\FilesUtil;
|
||||
use PhpZip\ZipFileInterface;
|
||||
|
||||
/**
|
||||
* Zip info
|
||||
* Zip info.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
@@ -17,51 +18,94 @@ class ZipInfo
|
||||
{
|
||||
// made by constants
|
||||
const MADE_BY_MS_DOS = 0;
|
||||
|
||||
const MADE_BY_AMIGA = 1;
|
||||
|
||||
const MADE_BY_OPEN_VMS = 2;
|
||||
|
||||
const MADE_BY_UNIX = 3;
|
||||
|
||||
const MADE_BY_VM_CMS = 4;
|
||||
|
||||
const MADE_BY_ATARI = 5;
|
||||
|
||||
const MADE_BY_OS_2 = 6;
|
||||
|
||||
const MADE_BY_MACINTOSH = 7;
|
||||
|
||||
const MADE_BY_Z_SYSTEM = 8;
|
||||
|
||||
const MADE_BY_CP_M = 9;
|
||||
|
||||
const MADE_BY_WINDOWS_NTFS = 10;
|
||||
|
||||
const MADE_BY_MVS = 11;
|
||||
|
||||
const MADE_BY_VSE = 12;
|
||||
|
||||
const MADE_BY_ACORN_RISC = 13;
|
||||
|
||||
const MADE_BY_VFAT = 14;
|
||||
|
||||
const MADE_BY_ALTERNATE_MVS = 15;
|
||||
|
||||
const MADE_BY_BEOS = 16;
|
||||
|
||||
const MADE_BY_TANDEM = 17;
|
||||
|
||||
const MADE_BY_OS_400 = 18;
|
||||
|
||||
const MADE_BY_OS_X = 19;
|
||||
|
||||
const MADE_BY_UNKNOWN = 20;
|
||||
|
||||
const UNX_IFMT = 0170000; /* Unix file type mask */
|
||||
const UNX_IFREG = 0100000; /* Unix regular file */
|
||||
const UNX_IFSOCK = 0140000; /* Unix socket (BSD, not SysV or Amiga) */
|
||||
const UNX_IFLNK = 0120000; /* Unix symbolic link (not SysV, Amiga) */
|
||||
const UNX_IFBLK = 0060000; /* Unix block special (not Amiga) */
|
||||
const UNX_IFDIR = 0040000; /* Unix directory */
|
||||
const UNX_IFCHR = 0020000; /* Unix character special (not Amiga) */
|
||||
const UNX_IFIFO = 0010000; /* Unix fifo (BCC, not MSC or Amiga) */
|
||||
const UNX_ISUID = 04000; /* Unix set user id on execution */
|
||||
const UNX_ISGID = 02000; /* Unix set group id on execution */
|
||||
const UNX_ISVTX = 01000; /* Unix directory permissions control */
|
||||
const UNX_ENFMT = self::UNX_ISGID; /* Unix record locking enforcement flag */
|
||||
const UNX_IRWXU = 00700; /* Unix read, write, execute: owner */
|
||||
const UNX_IRUSR = 00400; /* Unix read permission: owner */
|
||||
const UNX_IWUSR = 00200; /* Unix write permission: owner */
|
||||
const UNX_IXUSR = 00100; /* Unix execute permission: owner */
|
||||
const UNX_IRWXG = 00070; /* Unix read, write, execute: group */
|
||||
const UNX_IRGRP = 00040; /* Unix read permission: group */
|
||||
const UNX_IWGRP = 00020; /* Unix write permission: group */
|
||||
const UNX_IXGRP = 00010; /* Unix execute permission: group */
|
||||
const UNX_IRWXO = 00007; /* Unix read, write, execute: other */
|
||||
const UNX_IROTH = 00004; /* Unix read permission: other */
|
||||
const UNX_IWOTH = 00002; /* Unix write permission: other */
|
||||
const UNX_IXOTH = 00001; /* Unix execute permission: other */
|
||||
const UNX_IFMT = 0170000; // Unix file type mask
|
||||
|
||||
const UNX_IFREG = 0100000; // Unix regular file
|
||||
|
||||
const UNX_IFSOCK = 0140000; // Unix socket (BSD, not SysV or Amiga)
|
||||
|
||||
const UNX_IFLNK = 0120000; // Unix symbolic link (not SysV, Amiga)
|
||||
|
||||
const UNX_IFBLK = 0060000; // Unix block special (not Amiga)
|
||||
|
||||
const UNX_IFDIR = 0040000; // Unix directory
|
||||
|
||||
const UNX_IFCHR = 0020000; // Unix character special (not Amiga)
|
||||
|
||||
const UNX_IFIFO = 0010000; // Unix fifo (BCC, not MSC or Amiga)
|
||||
|
||||
const UNX_ISUID = 04000; // Unix set user id on execution
|
||||
|
||||
const UNX_ISGID = 02000; // Unix set group id on execution
|
||||
|
||||
const UNX_ISVTX = 01000; // Unix directory permissions control
|
||||
|
||||
const UNX_ENFMT = self::UNX_ISGID; // Unix record locking enforcement flag
|
||||
|
||||
const UNX_IRWXU = 00700; // Unix read, write, execute: owner
|
||||
|
||||
const UNX_IRUSR = 00400; // Unix read permission: owner
|
||||
|
||||
const UNX_IWUSR = 00200; // Unix write permission: owner
|
||||
|
||||
const UNX_IXUSR = 00100; // Unix execute permission: owner
|
||||
|
||||
const UNX_IRWXG = 00070; // Unix read, write, execute: group
|
||||
|
||||
const UNX_IRGRP = 00040; // Unix read permission: group
|
||||
|
||||
const UNX_IWGRP = 00020; // Unix write permission: group
|
||||
|
||||
const UNX_IXGRP = 00010; // Unix execute permission: group
|
||||
|
||||
const UNX_IRWXO = 00007; // Unix read, write, execute: other
|
||||
|
||||
const UNX_IROTH = 00004; // Unix read permission: other
|
||||
|
||||
const UNX_IWOTH = 00002; // Unix write permission: other
|
||||
|
||||
const UNX_IXOTH = 00001; // Unix execute permission: other
|
||||
|
||||
private static $valuesMadeBy = [
|
||||
self::MADE_BY_MS_DOS => 'FAT',
|
||||
@@ -113,80 +157,63 @@ class ZipInfo
|
||||
ZipEntry::METHOD_WINZIP_AES => 'WinZip AES',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $name;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $folder;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $size;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $compressedSize;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $mtime;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
|
||||
/** @var int|null */
|
||||
private $ctime;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
|
||||
/** @var int|null */
|
||||
private $atime;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $encrypted;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
|
||||
/** @var string|null */
|
||||
private $comment;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $crc;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
||||
/** @var string */
|
||||
private $methodName;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $compressionMethod;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
||||
/** @var string */
|
||||
private $platform;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $version;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
||||
/** @var string */
|
||||
private $attributes;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
|
||||
/** @var int|null */
|
||||
private $encryptionMethod;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
|
||||
/** @var int|null */
|
||||
private $compressionLevel;
|
||||
|
||||
/**
|
||||
* ZipInfo constructor.
|
||||
*
|
||||
* @param ZipEntry $entry
|
||||
* @throws \PhpZip\Exception\ZipException
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function __construct(ZipEntry $entry)
|
||||
{
|
||||
@@ -195,7 +222,8 @@ class ZipInfo
|
||||
$ctime = null;
|
||||
|
||||
$field = $entry->getExtraFieldsCollection()->get(NtfsExtraField::getHeaderId());
|
||||
if (null !== $field && $field instanceof NtfsExtraField) {
|
||||
|
||||
if ($field instanceof NtfsExtraField) {
|
||||
/**
|
||||
* @var NtfsExtraField $field
|
||||
*/
|
||||
@@ -206,10 +234,10 @@ class ZipInfo
|
||||
|
||||
$this->name = $entry->getName();
|
||||
$this->folder = $entry->isDirectory();
|
||||
$this->size = PHP_INT_SIZE === 4 ?
|
||||
$this->size = \PHP_INT_SIZE === 4 ?
|
||||
sprintf('%u', $entry->getSize()) :
|
||||
$entry->getSize();
|
||||
$this->compressedSize = PHP_INT_SIZE === 4 ?
|
||||
$this->compressedSize = \PHP_INT_SIZE === 4 ?
|
||||
sprintf('%u', $entry->getCompressedSize()) :
|
||||
$entry->getCompressedSize();
|
||||
$this->mtime = $mtime;
|
||||
@@ -225,65 +253,74 @@ class ZipInfo
|
||||
$this->version = $entry->getVersionNeededToExtract();
|
||||
$this->compressionLevel = $entry->getCompressionLevel();
|
||||
|
||||
$attributes = str_repeat(" ", 12);
|
||||
$attributes = str_repeat(' ', 12);
|
||||
$externalAttributes = $entry->getExternalAttributes();
|
||||
$externalAttributes = PHP_INT_SIZE === 4 ?
|
||||
$externalAttributes = \PHP_INT_SIZE === 4 ?
|
||||
sprintf('%u', $externalAttributes) :
|
||||
$externalAttributes;
|
||||
$xattr = (($externalAttributes >> 16) & 0xFFFF);
|
||||
switch ($entry->getPlatform()) {
|
||||
case self::MADE_BY_MS_DOS:
|
||||
case self::MADE_BY_WINDOWS_NTFS:
|
||||
if ($entry->getPlatform() != self::MADE_BY_MS_DOS ||
|
||||
($xattr & 0700) !=
|
||||
if ($entry->getPlatform() !== self::MADE_BY_MS_DOS ||
|
||||
($xattr & 0700) !==
|
||||
(0400 |
|
||||
(!($externalAttributes & 1) << 7) |
|
||||
(($externalAttributes & 0x10) << 2))
|
||||
) {
|
||||
$xattr = $externalAttributes & 0xFF;
|
||||
$attributes = ".r.-... ";
|
||||
$attributes = '.r.-... ';
|
||||
$attributes[2] = ($xattr & 0x01) ? '-' : 'w';
|
||||
$attributes[5] = ($xattr & 0x02) ? 'h' : '-';
|
||||
$attributes[6] = ($xattr & 0x04) ? 's' : '-';
|
||||
$attributes[4] = ($xattr & 0x20) ? 'a' : '-';
|
||||
|
||||
if ($xattr & 0x10) {
|
||||
$attributes[0] = 'd';
|
||||
$attributes[3] = 'x';
|
||||
} else {
|
||||
$attributes[0] = '-';
|
||||
}
|
||||
|
||||
if ($xattr & 0x08) {
|
||||
$attributes[0] = 'V';
|
||||
} else {
|
||||
$ext = strtolower(pathinfo($entry->getName(), PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ["com", "exe", "btm", "cmd", "bat"])) {
|
||||
$ext = strtolower(pathinfo($entry->getName(), \PATHINFO_EXTENSION));
|
||||
|
||||
if (\in_array($ext, ['com', 'exe', 'btm', 'cmd', 'bat'])) {
|
||||
$attributes[3] = 'x';
|
||||
}
|
||||
}
|
||||
break;
|
||||
} /* else: fall through! */
|
||||
} // else: fall through!
|
||||
|
||||
// no break
|
||||
default: /* assume Unix-like */
|
||||
default: // assume Unix-like
|
||||
switch ($xattr & self::UNX_IFMT) {
|
||||
case self::UNX_IFDIR:
|
||||
$attributes[0] = 'd';
|
||||
break;
|
||||
|
||||
case self::UNX_IFREG:
|
||||
$attributes[0] = '-';
|
||||
break;
|
||||
|
||||
case self::UNX_IFLNK:
|
||||
$attributes[0] = 'l';
|
||||
break;
|
||||
|
||||
case self::UNX_IFBLK:
|
||||
$attributes[0] = 'b';
|
||||
break;
|
||||
|
||||
case self::UNX_IFCHR:
|
||||
$attributes[0] = 'c';
|
||||
break;
|
||||
|
||||
case self::UNX_IFIFO:
|
||||
$attributes[0] = 'p';
|
||||
break;
|
||||
|
||||
case self::UNX_IFSOCK:
|
||||
$attributes[0] = 's';
|
||||
break;
|
||||
@@ -302,68 +339,75 @@ class ZipInfo
|
||||
$attributes[3] = ($xattr & self::UNX_ISUID) ? 's' : 'x';
|
||||
} else {
|
||||
$attributes[3] = ($xattr & self::UNX_ISUID) ? 'S' : '-';
|
||||
} /* S==undefined */
|
||||
} // S==undefined
|
||||
if ($xattr & self::UNX_IXGRP) {
|
||||
$attributes[6] = ($xattr & self::UNX_ISGID) ? 's' : 'x';
|
||||
} /* == UNX_ENFMT */
|
||||
} // == UNX_ENFMT
|
||||
else {
|
||||
$attributes[6] = ($xattr & self::UNX_ISGID) ? 'S' : '-';
|
||||
} /* SunOS 4.1.x */
|
||||
} // SunOS 4.1.x
|
||||
if ($xattr & self::UNX_IXOTH) {
|
||||
$attributes[9] = ($xattr & self::UNX_ISVTX) ? 't' : 'x';
|
||||
} /* "sticky bit" */
|
||||
} // "sticky bit"
|
||||
else {
|
||||
$attributes[9] = ($xattr & self::UNX_ISVTX) ? 'T' : '-';
|
||||
} /* T==undefined */
|
||||
} // T==undefined
|
||||
}
|
||||
$this->attributes = trim($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return int
|
||||
* @throws \PhpZip\Exception\ZipException
|
||||
*/
|
||||
private static function getMethodId(ZipEntry $entry)
|
||||
{
|
||||
$method = $entry->getMethod();
|
||||
if ($entry->isEncrypted()) {
|
||||
if ($entry->getMethod() === ZipEntry::METHOD_WINZIP_AES) {
|
||||
$field = $entry->getExtraFieldsCollection()->get(WinZipAesEntryExtraField::getHeaderId());
|
||||
if (null !== $field) {
|
||||
/**
|
||||
* @var WinZipAesEntryExtraField $field
|
||||
*/
|
||||
$method = $field->getMethod();
|
||||
}
|
||||
|
||||
if ($entry->isEncrypted() && $entry->getMethod() === ZipEntry::METHOD_WINZIP_AES) {
|
||||
$field = $entry->getExtraFieldsCollection()->get(WinZipAesEntryExtraField::getHeaderId());
|
||||
|
||||
if ($field !== null) {
|
||||
/**
|
||||
* @var WinZipAesEntryExtraField $field
|
||||
*/
|
||||
$method = $field->getMethod();
|
||||
}
|
||||
}
|
||||
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
* @throws \PhpZip\Exception\ZipException
|
||||
*/
|
||||
private static function getEntryMethodName(ZipEntry $entry)
|
||||
{
|
||||
$return = '';
|
||||
|
||||
if ($entry->isEncrypted()) {
|
||||
if ($entry->getMethod() === ZipEntry::METHOD_WINZIP_AES) {
|
||||
$return = ucfirst(self::$valuesCompressionMethod[$entry->getMethod()]);
|
||||
/** @var WinZipAesEntryExtraField|null $field */
|
||||
$field = $entry->getExtraFieldsCollection()->get(WinZipAesEntryExtraField::getHeaderId());
|
||||
if (null !== $field) {
|
||||
/**
|
||||
* @var WinZipAesEntryExtraField $field
|
||||
*/
|
||||
|
||||
if ($field !== null) {
|
||||
$return .= '-' . $field->getKeyStrength();
|
||||
|
||||
if (isset(self::$valuesCompressionMethod[$field->getMethod()])) {
|
||||
$return .= ' ' . ucfirst(self::$valuesCompressionMethod[$field->getMethod()]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$return .= 'ZipCrypto';
|
||||
|
||||
if (isset(self::$valuesCompressionMethod[$entry->getMethod()])) {
|
||||
$return .= ' ' . ucfirst(self::$valuesCompressionMethod[$entry->getMethod()]);
|
||||
}
|
||||
@@ -373,20 +417,22 @@ class ZipInfo
|
||||
} else {
|
||||
$return = 'unknown';
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getPlatformName(ZipEntry $entry)
|
||||
{
|
||||
if (isset(self::$valuesMadeBy[$entry->getPlatform()])) {
|
||||
return self::$valuesMadeBy[$entry->getPlatform()];
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,6 +445,7 @@ class ZipInfo
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @deprecated use \PhpZip\Model\ZipInfo::getName()
|
||||
*/
|
||||
public function getPath()
|
||||
@@ -407,7 +454,7 @@ class ZipInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isFolder()
|
||||
{
|
||||
@@ -463,7 +510,7 @@ class ZipInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isEncrypted()
|
||||
{
|
||||
@@ -471,7 +518,7 @@ class ZipInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
@@ -488,6 +535,7 @@ class ZipInfo
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @deprecated use \PhpZip\Model\ZipInfo::getMethodName()
|
||||
*/
|
||||
public function getMethod()
|
||||
@@ -566,7 +614,7 @@ class ZipInfo
|
||||
'method_name' => $this->getMethodName(),
|
||||
'compression_method' => $this->getCompressionMethod(),
|
||||
'platform' => $this->getPlatform(),
|
||||
'version' => $this->getVersion()
|
||||
'version' => $this->getVersion(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -580,9 +628,9 @@ class ZipInfo
|
||||
. ($this->isFolder() ? 'Folder, ' : '')
|
||||
. 'Size="' . FilesUtil::humanSize($this->getSize()) . '"'
|
||||
. ', Compressed size="' . FilesUtil::humanSize($this->getCompressedSize()) . '"'
|
||||
. ', Modified time="' . date(DATE_W3C, $this->getMtime()) . '", '
|
||||
. ($this->getCtime() !== null ? 'Created time="' . date(DATE_W3C, $this->getCtime()) . '", ' : '')
|
||||
. ($this->getAtime() !== null ? 'Accessed time="' . date(DATE_W3C, $this->getAtime()) . '", ' : '')
|
||||
. ', Modified time="' . date(\DATE_W3C, $this->getMtime()) . '", '
|
||||
. ($this->getCtime() !== null ? 'Created time="' . date(\DATE_W3C, $this->getCtime()) . '", ' : '')
|
||||
. ($this->getAtime() !== null ? 'Accessed time="' . date(\DATE_W3C, $this->getAtime()) . '", ' : '')
|
||||
. ($this->isEncrypted() ? 'Encrypted, ' : '')
|
||||
. (!empty($this->comment) ? 'Comment="' . $this->getComment() . '", ' : '')
|
||||
. (!empty($this->crc) ? 'Crc=0x' . dechex($this->getCrc()) . ', ' : '')
|
||||
|
@@ -10,65 +10,60 @@ use PhpZip\Model\Entry\ZipSourceEntry;
|
||||
use PhpZip\ZipFileInterface;
|
||||
|
||||
/**
|
||||
* Zip Model
|
||||
* Zip Model.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class ZipModel implements \Countable
|
||||
{
|
||||
/**
|
||||
* @var ZipSourceEntry[]
|
||||
*/
|
||||
/** @var ZipSourceEntry[] */
|
||||
protected $inputEntries = [];
|
||||
/**
|
||||
* @var ZipEntry[]
|
||||
*/
|
||||
|
||||
/** @var ZipEntry[] */
|
||||
protected $outEntries = [];
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
|
||||
/** @var string|null */
|
||||
protected $archiveComment;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
|
||||
/** @var string|null */
|
||||
protected $archiveCommentChanges;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
protected $archiveCommentChanged = false;
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
|
||||
/** @var int|null */
|
||||
protected $zipAlign;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $zip64;
|
||||
|
||||
/**
|
||||
* @param ZipSourceEntry[] $entries
|
||||
* @param ZipSourceEntry[] $entries
|
||||
* @param EndOfCentralDirectory $endOfCentralDirectory
|
||||
*
|
||||
* @return ZipModel
|
||||
*/
|
||||
public static function newSourceModel(array $entries, EndOfCentralDirectory $endOfCentralDirectory)
|
||||
{
|
||||
$model = new self;
|
||||
$model = new self();
|
||||
$model->inputEntries = $entries;
|
||||
$model->outEntries = $entries;
|
||||
$model->archiveComment = $endOfCentralDirectory->getComment();
|
||||
$model->zip64 = $endOfCentralDirectory->isZip64();
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getArchiveComment()
|
||||
{
|
||||
if ($this->archiveCommentChanged) {
|
||||
return $this->archiveCommentChanges;
|
||||
}
|
||||
|
||||
return $this->archiveComment;
|
||||
}
|
||||
|
||||
@@ -77,13 +72,15 @@ class ZipModel implements \Countable
|
||||
*/
|
||||
public function setArchiveComment($comment)
|
||||
{
|
||||
if ($comment !== null && strlen($comment) !== 0) {
|
||||
$comment = (string)$comment;
|
||||
$length = strlen($comment);
|
||||
if (0x0000 > $length || $length > 0xffff) {
|
||||
if ($comment !== null && $comment !== '') {
|
||||
$comment = (string) $comment;
|
||||
$length = \strlen($comment);
|
||||
|
||||
if ($length > 0xffff) {
|
||||
throw new InvalidArgumentException('Length comment out of range');
|
||||
}
|
||||
}
|
||||
|
||||
if ($comment !== $this->archiveComment) {
|
||||
$this->archiveCommentChanges = $comment;
|
||||
$this->archiveCommentChanged = true;
|
||||
@@ -95,7 +92,8 @@ class ZipModel implements \Countable
|
||||
/**
|
||||
* Specify a password for extracting files.
|
||||
*
|
||||
* @param null|string $password
|
||||
* @param string|null $password
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function setReadPassword($password)
|
||||
@@ -110,6 +108,7 @@ class ZipModel implements \Countable
|
||||
/**
|
||||
* @param string $entryName
|
||||
* @param string $password
|
||||
*
|
||||
* @throws ZipEntryNotFoundException
|
||||
* @throws ZipException
|
||||
*/
|
||||
@@ -118,6 +117,7 @@ class ZipModel implements \Countable
|
||||
if (!isset($this->inputEntries[$entryName])) {
|
||||
throw new ZipEntryNotFoundException($entryName);
|
||||
}
|
||||
|
||||
if ($this->inputEntries[$entryName]->isEncrypted()) {
|
||||
$this->inputEntries[$entryName]->setPassword($password);
|
||||
}
|
||||
@@ -136,7 +136,7 @@ class ZipModel implements \Countable
|
||||
*/
|
||||
public function setZipAlign($zipAlign)
|
||||
{
|
||||
$this->zipAlign = $zipAlign === null ? null : (int)$zipAlign;
|
||||
$this->zipAlign = $zipAlign === null ? null : (int) $zipAlign;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,11 +144,11 @@ class ZipModel implements \Countable
|
||||
*/
|
||||
public function isZipAlign()
|
||||
{
|
||||
return $this->zipAlign != null;
|
||||
return $this->zipAlign !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $writePassword
|
||||
* @param string|null $writePassword
|
||||
*/
|
||||
public function setWritePassword($writePassword)
|
||||
{
|
||||
@@ -156,7 +156,7 @@ class ZipModel implements \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove password
|
||||
* Remove password.
|
||||
*/
|
||||
public function removePassword()
|
||||
{
|
||||
@@ -182,15 +182,16 @@ class ZipModel implements \Countable
|
||||
/**
|
||||
* @param string|ZipEntry $old
|
||||
* @param string|ZipEntry $new
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function renameEntry($old, $new)
|
||||
{
|
||||
$old = $old instanceof ZipEntry ? $old->getName() : (string)$old;
|
||||
$new = $new instanceof ZipEntry ? $new->getName() : (string)$new;
|
||||
$old = $old instanceof ZipEntry ? $old->getName() : (string) $old;
|
||||
$new = $new instanceof ZipEntry ? $new->getName() : (string) $new;
|
||||
|
||||
if (isset($this->outEntries[$new])) {
|
||||
throw new InvalidArgumentException("New entry name " . $new . ' is exists.');
|
||||
throw new InvalidArgumentException('New entry name ' . $new . ' is exists.');
|
||||
}
|
||||
|
||||
$entry = $this->getEntryForChanges($old);
|
||||
@@ -201,45 +202,57 @@ class ZipModel implements \Countable
|
||||
|
||||
/**
|
||||
* @param string|ZipEntry $entry
|
||||
* @return ZipChangesEntry|ZipEntry
|
||||
*
|
||||
* @throws ZipException
|
||||
* @throws ZipEntryNotFoundException
|
||||
*
|
||||
* @return ZipChangesEntry|ZipEntry
|
||||
*/
|
||||
public function getEntryForChanges($entry)
|
||||
{
|
||||
$entry = $this->getEntry($entry);
|
||||
|
||||
if ($entry instanceof ZipSourceEntry) {
|
||||
$entry = new ZipChangesEntry($entry);
|
||||
$this->addEntry($entry);
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|ZipEntry $entryName
|
||||
* @return ZipEntry
|
||||
*
|
||||
* @throws ZipEntryNotFoundException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function getEntry($entryName)
|
||||
{
|
||||
$entryName = $entryName instanceof ZipEntry ? $entryName->getName() : (string)$entryName;
|
||||
$entryName = $entryName instanceof ZipEntry ? $entryName->getName() : (string) $entryName;
|
||||
|
||||
if (isset($this->outEntries[$entryName])) {
|
||||
return $this->outEntries[$entryName];
|
||||
}
|
||||
|
||||
throw new ZipEntryNotFoundException($entryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|ZipEntry $entry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteEntry($entry)
|
||||
{
|
||||
$entry = $entry instanceof ZipEntry ? $entry->getName() : (string)$entry;
|
||||
$entry = $entry instanceof ZipEntry ? $entry->getName() : (string) $entry;
|
||||
|
||||
if (isset($this->outEntries[$entry])) {
|
||||
unset($this->outEntries[$entry]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -263,11 +276,13 @@ class ZipModel implements \Countable
|
||||
|
||||
/**
|
||||
* @param string|ZipEntry $entryName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasEntry($entryName)
|
||||
{
|
||||
$entryName = $entryName instanceof ZipEntry ? $entryName->getName() : (string)$entryName;
|
||||
$entryName = $entryName instanceof ZipEntry ? $entryName->getName() : (string) $entryName;
|
||||
|
||||
return isset($this->outEntries[$entryName]);
|
||||
}
|
||||
|
||||
@@ -280,21 +295,24 @@ class ZipModel implements \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
* @link http://php.net/manual/en/countable.count.php
|
||||
* Count elements of an object.
|
||||
*
|
||||
* @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.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value is cast to an integer.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return sizeof($this->outEntries);
|
||||
return \count($this->outEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo all changes done in the archive
|
||||
* Undo all changes done in the archive.
|
||||
*/
|
||||
public function unchangeAll()
|
||||
{
|
||||
@@ -303,7 +321,7 @@ class ZipModel implements \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo change archive comment
|
||||
* Undo change archive comment.
|
||||
*/
|
||||
public function unchangeArchiveComment()
|
||||
{
|
||||
@@ -315,15 +333,19 @@ class ZipModel implements \Countable
|
||||
* Revert all changes done to an entry with the given name.
|
||||
*
|
||||
* @param string|ZipEntry $entry Entry name or ZipEntry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unchangeEntry($entry)
|
||||
{
|
||||
$entry = $entry instanceof ZipEntry ? $entry->getName() : (string)$entry;
|
||||
if (isset($this->outEntries[$entry]) && isset($this->inputEntries[$entry])) {
|
||||
$entry = $entry instanceof ZipEntry ? $entry->getName() : (string) $entry;
|
||||
|
||||
if (isset($this->outEntries[$entry], $this->inputEntries[$entry])) {
|
||||
$this->outEntries[$entry] = $this->inputEntries[$entry];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -5,59 +5,77 @@ namespace PhpZip\Stream;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/**
|
||||
* Implement PSR Message Stream
|
||||
* Implement PSR Message Stream.
|
||||
*/
|
||||
class ResponseStream implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @var array */
|
||||
private static $readWriteHash = [
|
||||
'read' => [
|
||||
'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
|
||||
'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
|
||||
'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
|
||||
'x+t' => true, 'c+t' => true, 'a+' => true,
|
||||
'r' => true,
|
||||
'w+' => true,
|
||||
'r+' => true,
|
||||
'x+' => true,
|
||||
'c+' => true,
|
||||
'rb' => true,
|
||||
'w+b' => true,
|
||||
'r+b' => true,
|
||||
'x+b' => true,
|
||||
'c+b' => true,
|
||||
'rt' => true,
|
||||
'w+t' => true,
|
||||
'r+t' => true,
|
||||
'x+t' => true,
|
||||
'c+t' => true,
|
||||
'a+' => true,
|
||||
],
|
||||
'write' => [
|
||||
'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
|
||||
'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true,
|
||||
'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
|
||||
'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true,
|
||||
'w' => true,
|
||||
'w+' => true,
|
||||
'rw' => true,
|
||||
'r+' => true,
|
||||
'x+' => true,
|
||||
'c+' => true,
|
||||
'wb' => true,
|
||||
'w+b' => true,
|
||||
'r+b' => true,
|
||||
'x+b' => true,
|
||||
'c+b' => true,
|
||||
'w+t' => true,
|
||||
'r+t' => true,
|
||||
'x+t' => true,
|
||||
'c+t' => true,
|
||||
'a' => true,
|
||||
'a+' => true,
|
||||
],
|
||||
];
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
|
||||
/** @var resource */
|
||||
private $stream;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
||||
/** @var int */
|
||||
private $size;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $seekable;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $readable;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
||||
/** @var bool */
|
||||
private $writable;
|
||||
/**
|
||||
* @var array|mixed|null
|
||||
*/
|
||||
|
||||
/** @var array|mixed|null */
|
||||
private $uri;
|
||||
|
||||
/**
|
||||
* @param resource $stream Stream resource to wrap.
|
||||
* @param resource $stream stream resource to wrap
|
||||
*
|
||||
* @throws \InvalidArgumentException if the stream is not a stream resource
|
||||
*/
|
||||
public function __construct($stream)
|
||||
{
|
||||
if (!is_resource($stream)) {
|
||||
if (!\is_resource($stream)) {
|
||||
throw new \InvalidArgumentException('Stream must be a resource');
|
||||
}
|
||||
$this->stream = $stream;
|
||||
@@ -74,11 +92,13 @@ class ResponseStream implements StreamInterface
|
||||
* The keys returned are identical to the keys returned from PHP's
|
||||
* stream_get_meta_data() function.
|
||||
*
|
||||
* @link http://php.net/manual/en/function.stream-get-meta-data.php
|
||||
* @param string $key Specific metadata to retrieve.
|
||||
* @see http://php.net/manual/en/function.stream-get-meta-data.php
|
||||
*
|
||||
* @param string $key specific metadata to retrieve
|
||||
*
|
||||
* @return array|mixed|null Returns an associative array if no key is
|
||||
* provided. Returns a specific key value if a key is provided and the
|
||||
* value is found, or null if the key is not found.
|
||||
* provided. Returns a specific key value if a key is provided and the
|
||||
* value is found, or null if the key is not found.
|
||||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
@@ -86,6 +106,7 @@ class ResponseStream implements StreamInterface
|
||||
return $key ? null : [];
|
||||
}
|
||||
$meta = stream_get_meta_data($this->stream);
|
||||
|
||||
return isset($meta[$key]) ? $meta[$key] : null;
|
||||
}
|
||||
|
||||
@@ -101,6 +122,7 @@ class ResponseStream implements StreamInterface
|
||||
* string casting operations.
|
||||
*
|
||||
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
@@ -109,7 +131,8 @@ class ResponseStream implements StreamInterface
|
||||
return '';
|
||||
}
|
||||
$this->rewind();
|
||||
return (string)stream_get_contents($this->stream);
|
||||
|
||||
return (string) stream_get_contents($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,9 +141,10 @@ class ResponseStream implements StreamInterface
|
||||
* If the stream is not seekable, this method will raise an exception;
|
||||
* otherwise, it will perform a seek(0).
|
||||
*
|
||||
* @throws \RuntimeException on failure
|
||||
*
|
||||
* @see http://www.php.net/manual/en/function.fseek.php
|
||||
* @see seek()
|
||||
* @link http://www.php.net/manual/en/function.fseek.php
|
||||
* @throws \RuntimeException on failure.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
@@ -130,13 +154,14 @@ class ResponseStream implements StreamInterface
|
||||
/**
|
||||
* Get the size of the stream if known.
|
||||
*
|
||||
* @return int|null Returns the size in bytes if known, or null if unknown.
|
||||
* @return int|null returns the size in bytes if known, or null if unknown
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
if ($this->size !== null) {
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
if (!$this->stream) {
|
||||
return null;
|
||||
}
|
||||
@@ -145,18 +170,22 @@ class ResponseStream implements StreamInterface
|
||||
clearstatcache(true, $this->uri);
|
||||
}
|
||||
$stats = fstat($this->stream);
|
||||
|
||||
if (isset($stats['size'])) {
|
||||
$this->size = $stats['size'];
|
||||
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current position of the file read/write pointer
|
||||
* Returns the current position of the file read/write pointer.
|
||||
*
|
||||
* @throws \RuntimeException on error
|
||||
*
|
||||
* @return int Position of the file pointer
|
||||
* @throws \RuntimeException on error.
|
||||
*/
|
||||
public function tell()
|
||||
{
|
||||
@@ -186,16 +215,18 @@ class ResponseStream implements StreamInterface
|
||||
/**
|
||||
* Seek to a position in the stream.
|
||||
*
|
||||
* @link http://www.php.net/manual/en/function.fseek.php
|
||||
* @see http://www.php.net/manual/en/function.fseek.php
|
||||
*
|
||||
* @param int $offset Stream offset
|
||||
* @param int $whence Specifies how the cursor position will be calculated
|
||||
* based on the seek offset. Valid values are identical to the built-in
|
||||
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
|
||||
* offset bytes SEEK_CUR: Set position to current location plus offset
|
||||
* SEEK_END: Set position to end-of-stream plus offset.
|
||||
* @throws \RuntimeException on failure.
|
||||
* based on the seek offset. Valid values are identical to the built-in
|
||||
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
|
||||
* offset bytes SEEK_CUR: Set position to current location plus offset
|
||||
* SEEK_END: Set position to end-of-stream plus offset.
|
||||
*
|
||||
* @throws \RuntimeException on failure
|
||||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
public function seek($offset, $whence = \SEEK_SET)
|
||||
{
|
||||
$this->seekable && fseek($this->stream, $offset, $whence);
|
||||
}
|
||||
@@ -213,13 +244,16 @@ class ResponseStream implements StreamInterface
|
||||
/**
|
||||
* Write data to the stream.
|
||||
*
|
||||
* @param string $string The string that is to be written.
|
||||
* @return int Returns the number of bytes written to the stream.
|
||||
* @throws \RuntimeException on failure.
|
||||
* @param string $string the string that is to be written
|
||||
*
|
||||
* @throws \RuntimeException on failure
|
||||
*
|
||||
* @return int returns the number of bytes written to the stream
|
||||
*/
|
||||
public function write($string)
|
||||
{
|
||||
$this->size = null;
|
||||
|
||||
return $this->writable ? fwrite($this->stream, $string) : false;
|
||||
}
|
||||
|
||||
@@ -237,23 +271,26 @@ class ResponseStream implements StreamInterface
|
||||
* Read data from the stream.
|
||||
*
|
||||
* @param int $length Read up to $length bytes from the object and return
|
||||
* them. Fewer than $length bytes may be returned if underlying stream
|
||||
* call returns fewer bytes.
|
||||
* @return string Returns the data read from the stream, or an empty string
|
||||
* if no bytes are available.
|
||||
* @throws \RuntimeException if an error occurs.
|
||||
* them. Fewer than $length bytes may be returned if underlying stream
|
||||
* call returns fewer bytes.
|
||||
*
|
||||
* @throws \RuntimeException if an error occurs
|
||||
*
|
||||
* @return string returns the data read from the stream, or an empty string
|
||||
* if no bytes are available
|
||||
*/
|
||||
public function read($length)
|
||||
{
|
||||
return $this->readable ? fread($this->stream, $length) : "";
|
||||
return $this->readable ? fread($this->stream, $length) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remaining contents in a string
|
||||
* Returns the remaining contents in a string.
|
||||
*
|
||||
* @throws \RuntimeException if unable to read or an error occurs while
|
||||
* reading
|
||||
*
|
||||
* @return string
|
||||
* @throws \RuntimeException if unable to read or an error occurs while
|
||||
* reading.
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
@@ -261,7 +298,7 @@ class ResponseStream implements StreamInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the stream when the destructed
|
||||
* Closes the stream when the destructed.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
@@ -270,12 +307,10 @@ class ResponseStream implements StreamInterface
|
||||
|
||||
/**
|
||||
* Closes the stream and any underlying resources.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (is_resource($this->stream)) {
|
||||
if (\is_resource($this->stream)) {
|
||||
fclose($this->stream);
|
||||
}
|
||||
$this->detach();
|
||||
@@ -293,6 +328,7 @@ class ResponseStream implements StreamInterface
|
||||
$result = $this->stream;
|
||||
$this->stream = $this->size = $this->uri = null;
|
||||
$this->readable = $this->writable = $this->seekable = false;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@@ -25,41 +25,36 @@ use PhpZip\Util\StringUtil;
|
||||
use PhpZip\ZipFileInterface;
|
||||
|
||||
/**
|
||||
* Read zip file
|
||||
* Read zip file.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class ZipInputStream implements ZipInputStreamInterface
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
/** @var resource */
|
||||
protected $in;
|
||||
/**
|
||||
* @var PositionMapper
|
||||
*/
|
||||
|
||||
/** @var PositionMapper */
|
||||
protected $mapper;
|
||||
/**
|
||||
* @var int The number of bytes in the preamble of this ZIP file.
|
||||
*/
|
||||
|
||||
/** @var int the number of bytes in the preamble of this ZIP file */
|
||||
protected $preamble = 0;
|
||||
/**
|
||||
* @var int The number of bytes in the postamble of this ZIP file.
|
||||
*/
|
||||
|
||||
/** @var int the number of bytes in the postamble of this ZIP file */
|
||||
protected $postamble = 0;
|
||||
/**
|
||||
* @var ZipModel
|
||||
*/
|
||||
|
||||
/** @var ZipModel */
|
||||
protected $zipModel;
|
||||
|
||||
/**
|
||||
* ZipInputStream constructor.
|
||||
*
|
||||
* @param resource $in
|
||||
*/
|
||||
public function __construct($in)
|
||||
{
|
||||
if (!is_resource($in)) {
|
||||
if (!\is_resource($in)) {
|
||||
throw new RuntimeException('$in must be resource');
|
||||
}
|
||||
$this->in = $in;
|
||||
@@ -67,8 +62,9 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZipModel
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipModel
|
||||
*/
|
||||
public function readZip()
|
||||
{
|
||||
@@ -76,11 +72,12 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
$endOfCentralDirectory = $this->readEndOfCentralDirectory();
|
||||
$entries = $this->mountCentralDirectory($endOfCentralDirectory);
|
||||
$this->zipModel = ZipModel::newSourceModel($entries, $endOfCentralDirectory);
|
||||
|
||||
return $this->zipModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check zip file signature
|
||||
* Check zip file signature.
|
||||
*
|
||||
* @throws ZipException if this not .ZIP file.
|
||||
*/
|
||||
@@ -90,22 +87,27 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
// Constraint: A ZIP file must start with a Local File Header
|
||||
// or a (ZIP64) End Of Central Directory Record if it's empty.
|
||||
$signatureBytes = fread($this->in, 4);
|
||||
if (strlen($signatureBytes) < 4) {
|
||||
throw new ZipException("Invalid zip file.");
|
||||
|
||||
if (\strlen($signatureBytes) < 4) {
|
||||
throw new ZipException('Invalid zip file.');
|
||||
}
|
||||
$signature = unpack('V', $signatureBytes)[1];
|
||||
|
||||
if (
|
||||
ZipEntry::LOCAL_FILE_HEADER_SIG !== $signature
|
||||
&& EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $signature
|
||||
&& EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $signature
|
||||
$signature !== ZipEntry::LOCAL_FILE_HEADER_SIG
|
||||
&& $signature !== EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG
|
||||
&& $signature !== EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG
|
||||
) {
|
||||
throw new ZipException("Expected Local File Header or (ZIP64) End Of Central Directory Record! Signature: " . $signature);
|
||||
throw new ZipException(
|
||||
'Expected Local File Header or (ZIP64) End Of Central Directory Record! Signature: ' . $signature
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EndOfCentralDirectory
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return EndOfCentralDirectory
|
||||
*/
|
||||
protected function readEndOfCentralDirectory()
|
||||
{
|
||||
@@ -116,9 +118,9 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
$max = $size - EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN;
|
||||
$min = $max >= 0xffff ? $max - 0xffff : 0;
|
||||
for ($endOfCentralDirRecordPos = $max; $endOfCentralDirRecordPos >= $min; $endOfCentralDirRecordPos--) {
|
||||
fseek($this->in, $endOfCentralDirRecordPos, SEEK_SET);
|
||||
fseek($this->in, $endOfCentralDirRecordPos, \SEEK_SET);
|
||||
// end of central dir signature 4 bytes (0x06054b50)
|
||||
if (EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== unpack('V', fread($this->in, 4))[1]) {
|
||||
if (unpack('V', fread($this->in, 4))[1] !== EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -138,15 +140,16 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
fread($this->in, 18)
|
||||
);
|
||||
|
||||
if (0 !== $data['diskNo'] || 0 !== $data['cdDiskNo'] || $data['cdEntriesDisk'] !== $data['cdEntries']) {
|
||||
if ($data['diskNo'] !== 0 || $data['cdDiskNo'] !== 0 || $data['cdEntriesDisk'] !== $data['cdEntries']) {
|
||||
throw new ZipException(
|
||||
"ZIP file spanning/splitting is not supported!"
|
||||
'ZIP file spanning/splitting is not supported!'
|
||||
);
|
||||
}
|
||||
// .ZIP file comment (variable size)
|
||||
if ($data['commentLength'] > 0) {
|
||||
$comment = '';
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < $data['commentLength']) {
|
||||
$read = min(8192 /* chunk size */, $data['commentLength'] - $offset);
|
||||
$comment .= fread($this->in, $read);
|
||||
@@ -159,22 +162,27 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
// Check for ZIP64 End Of Central Directory Locator.
|
||||
$endOfCentralDirLocatorPos = $endOfCentralDirRecordPos - EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_LEN;
|
||||
|
||||
fseek($this->in, $endOfCentralDirLocatorPos, SEEK_SET);
|
||||
fseek($this->in, $endOfCentralDirLocatorPos, \SEEK_SET);
|
||||
// zip64 end of central dir locator
|
||||
// signature 4 bytes (0x07064b50)
|
||||
if (
|
||||
0 > $endOfCentralDirLocatorPos ||
|
||||
$endOfCentralDirLocatorPos < 0 ||
|
||||
ftell($this->in) === $size ||
|
||||
EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG !== unpack('V', fread($this->in, 4))[1]
|
||||
unpack(
|
||||
'V',
|
||||
fread($this->in, 4)
|
||||
)[1] !== EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG
|
||||
) {
|
||||
// Seek and check first CFH, probably requiring an offset mapper.
|
||||
$offset = $endOfCentralDirRecordPos - $data['cdSize'];
|
||||
fseek($this->in, $offset, SEEK_SET);
|
||||
fseek($this->in, $offset, \SEEK_SET);
|
||||
$offset -= $data['cdPos'];
|
||||
|
||||
if ($offset !== 0) {
|
||||
$this->mapper = new OffsetPositionMapper($offset);
|
||||
}
|
||||
$entryCount = $data['cdEntries'];
|
||||
|
||||
return new EndOfCentralDirectory($entryCount, $comment);
|
||||
}
|
||||
|
||||
@@ -187,21 +195,23 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
$zip64EndOfCentralDirectoryRecordPos = PackUtil::unpackLongLE(fread($this->in, 8));
|
||||
// total number of disks 4 bytes
|
||||
$totalDisks = unpack('V', fread($this->in, 4))[1];
|
||||
if (0 !== $zip64EndOfCentralDirectoryRecordDisk || 1 !== $totalDisks) {
|
||||
throw new ZipException("ZIP file spanning/splitting is not supported!");
|
||||
|
||||
if ($zip64EndOfCentralDirectoryRecordDisk !== 0 || $totalDisks !== 1) {
|
||||
throw new ZipException('ZIP file spanning/splitting is not supported!');
|
||||
}
|
||||
fseek($this->in, $zip64EndOfCentralDirectoryRecordPos, SEEK_SET);
|
||||
fseek($this->in, $zip64EndOfCentralDirectoryRecordPos, \SEEK_SET);
|
||||
// zip64 end of central dir
|
||||
// signature 4 bytes (0x06064b50)
|
||||
$zip64EndOfCentralDirSig = unpack('V', fread($this->in, 4))[1];
|
||||
if (EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $zip64EndOfCentralDirSig) {
|
||||
throw new ZipException("Expected ZIP64 End Of Central Directory Record!");
|
||||
|
||||
if ($zip64EndOfCentralDirSig !== EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG) {
|
||||
throw new ZipException('Expected ZIP64 End Of Central Directory Record!');
|
||||
}
|
||||
// size of zip64 end of central
|
||||
// directory record 8 bytes
|
||||
// version made by 2 bytes
|
||||
// version needed to extract 2 bytes
|
||||
fseek($this->in, 12, SEEK_CUR);
|
||||
fseek($this->in, 12, \SEEK_CUR);
|
||||
// number of this disk 4 bytes
|
||||
$diskNo = unpack('V', fread($this->in, 4))[1];
|
||||
// number of the disk with the
|
||||
@@ -213,28 +223,32 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
// total number of entries in the
|
||||
// central directory 8 bytes
|
||||
$cdEntries = PackUtil::unpackLongLE(fread($this->in, 8));
|
||||
if (0 !== $diskNo || 0 !== $cdDiskNo || $cdEntriesDisk !== $cdEntries) {
|
||||
throw new ZipException("ZIP file spanning/splitting is not supported!");
|
||||
|
||||
if ($diskNo !== 0 || $cdDiskNo !== 0 || $cdEntriesDisk !== $cdEntries) {
|
||||
throw new ZipException('ZIP file spanning/splitting is not supported!');
|
||||
}
|
||||
if ($cdEntries < 0 || 0x7fffffff < $cdEntries) {
|
||||
throw new ZipException("Total Number Of Entries In The Central Directory out of range!");
|
||||
|
||||
if ($cdEntries < 0 || $cdEntries > 0x7fffffff) {
|
||||
throw new ZipException('Total Number Of Entries In The Central Directory out of range!');
|
||||
}
|
||||
// size of the central directory 8 bytes
|
||||
fseek($this->in, 8, SEEK_CUR);
|
||||
fseek($this->in, 8, \SEEK_CUR);
|
||||
// offset of start of central
|
||||
// directory with respect to
|
||||
// the starting disk number 8 bytes
|
||||
$cdPos = PackUtil::unpackLongLE(fread($this->in, 8));
|
||||
// zip64 extensible data sector (variable size)
|
||||
fseek($this->in, $cdPos, SEEK_SET);
|
||||
fseek($this->in, $cdPos, \SEEK_SET);
|
||||
$this->preamble = $zip64EndOfCentralDirectoryRecordPos;
|
||||
$entryCount = $cdEntries;
|
||||
$zip64 = true;
|
||||
|
||||
return new EndOfCentralDirectory($entryCount, $comment, $zip64);
|
||||
}
|
||||
// Start recovering file entries from min.
|
||||
$this->preamble = $min;
|
||||
$this->postamble = $size - $min;
|
||||
|
||||
return new EndOfCentralDirectory(0, $comment);
|
||||
}
|
||||
|
||||
@@ -247,8 +261,10 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
* file header or additional data to be read.
|
||||
*
|
||||
* @param EndOfCentralDirectory $endOfCentralDirectory
|
||||
* @return ZipEntry[]
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry[]
|
||||
*/
|
||||
protected function mountCentralDirectory(EndOfCentralDirectory $endOfCentralDirectory)
|
||||
{
|
||||
@@ -261,7 +277,8 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
// Extra Field may have been parsed, map it to the real
|
||||
// offset and conditionally update the preamble size from it.
|
||||
$lfhOff = $this->mapper->map($entry->getOffset());
|
||||
$lfhOff = PHP_INT_SIZE === 4 ? sprintf('%u', $lfhOff) : $lfhOff;
|
||||
$lfhOff = \PHP_INT_SIZE === 4 ? sprintf('%u', $lfhOff) : $lfhOff;
|
||||
|
||||
if ($lfhOff < $this->preamble) {
|
||||
$this->preamble = $lfhOff;
|
||||
}
|
||||
@@ -269,9 +286,11 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
|
||||
if (($numEntries % 0x10000) !== 0) {
|
||||
throw new ZipException("Expected " . abs($numEntries) .
|
||||
($numEntries > 0 ? " more" : " less") .
|
||||
" entries in the Central Directory!");
|
||||
throw new ZipException(
|
||||
'Expected ' . abs($numEntries) .
|
||||
($numEntries > 0 ? ' more' : ' less') .
|
||||
' entries in the Central Directory!'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->preamble + $this->postamble >= fstat($this->in)['size']) {
|
||||
@@ -282,15 +301,17 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ZipEntry
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipEntry
|
||||
*/
|
||||
public function readEntry()
|
||||
{
|
||||
// central file header signature 4 bytes (0x02014b50)
|
||||
$fileHeaderSig = unpack('V', fread($this->in, 4))[1];
|
||||
|
||||
if ($fileHeaderSig !== ZipOutputStreamInterface::CENTRAL_FILE_HEADER_SIG) {
|
||||
throw new InvalidArgumentException("Corrupt zip file. Can not read zip entry.");
|
||||
throw new InvalidArgumentException('Corrupt zip file. Can not read zip entry.');
|
||||
}
|
||||
|
||||
// version made by 2 bytes
|
||||
@@ -322,6 +343,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
// See appendix D of PKWARE's ZIP File Format Specification.
|
||||
$name = '';
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < $data['fileLength']) {
|
||||
$read = min(8192 /* chunk size */, $data['fileLength'] - $offset);
|
||||
$name .= fread($this->in, $read);
|
||||
@@ -343,6 +365,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
if ($data['extraLength'] > 0) {
|
||||
$extra = '';
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < $data['extraLength']) {
|
||||
$read = min(8192 /* chunk size */, $data['extraLength'] - $offset);
|
||||
$extra .= fread($this->in, $read);
|
||||
@@ -350,9 +373,11 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
$entry->setExtra($extra);
|
||||
}
|
||||
|
||||
if ($data['commentLength'] > 0) {
|
||||
$comment = '';
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < $data['commentLength']) {
|
||||
$read = min(8192 /* chunk size */, $data['commentLength'] - $offset);
|
||||
$comment .= fread($this->in, $read);
|
||||
@@ -360,29 +385,34 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
$entry->setComment($comment);
|
||||
}
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function readEntryContent(ZipEntry $entry)
|
||||
{
|
||||
if ($entry->isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!($entry instanceof ZipSourceEntry)) {
|
||||
throw new InvalidArgumentException('entry must be ' . ZipSourceEntry::class);
|
||||
}
|
||||
$isEncrypted = $entry->isEncrypted();
|
||||
|
||||
if ($isEncrypted && $entry->getPassword() === null) {
|
||||
throw new ZipException("Can not password from entry " . $entry->getName());
|
||||
throw new ZipException('Can not password from entry ' . $entry->getName());
|
||||
}
|
||||
|
||||
$pos = $entry->getOffset();
|
||||
$pos = PHP_INT_SIZE === 4
|
||||
$pos = \PHP_INT_SIZE === 4
|
||||
? sprintf('%u', $pos) // PHP 32-Bit
|
||||
: $pos; // PHP 64-Bit
|
||||
|
||||
@@ -391,7 +421,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
|
||||
// local file header signature 4 bytes (0x04034b50)
|
||||
if (unpack('V', fread($this->in, 4))[1] !== ZipEntry::LOCAL_FILE_HEADER_SIG) {
|
||||
throw new ZipException($entry->getName() . " (expected Local File Header)");
|
||||
throw new ZipException($entry->getName() . ' (expected Local File Header)');
|
||||
}
|
||||
fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_FILE_NAME_LENGTH_POS);
|
||||
// file name length 2 bytes
|
||||
@@ -399,7 +429,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
$data = unpack('vfileLength/vextraLength', fread($this->in, 4));
|
||||
$pos += ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $data['fileLength'] + $data['extraLength'];
|
||||
|
||||
assert(ZipEntry::UNKNOWN !== $entry->getCrc());
|
||||
\assert($entry->getCrc() !== ZipEntry::UNKNOWN);
|
||||
|
||||
$method = $entry->getMethod();
|
||||
|
||||
@@ -407,10 +437,12 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
|
||||
// Get raw entry content
|
||||
$compressedSize = $entry->getCompressedSize();
|
||||
$compressedSize = PHP_INT_SIZE === 4 ? sprintf('%u', $compressedSize) : $compressedSize;
|
||||
$compressedSize = \PHP_INT_SIZE === 4 ? sprintf('%u', $compressedSize) : $compressedSize;
|
||||
$content = '';
|
||||
|
||||
if ($compressedSize > 0) {
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < $compressedSize) {
|
||||
$read = min(8192 /* chunk size */, $compressedSize - $offset);
|
||||
$content .= fread($this->in, $read);
|
||||
@@ -419,6 +451,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
|
||||
$skipCheckCrc = false;
|
||||
|
||||
if ($isEncrypted) {
|
||||
if ($method === ZipEntry::METHOD_WINZIP_AES) {
|
||||
// Strong Encryption Specification - WinZip AES
|
||||
@@ -441,6 +474,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
if (!$skipCheckCrc) {
|
||||
// Check CRC32 in the Local File Header or Data Descriptor.
|
||||
$localCrc = null;
|
||||
|
||||
if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
|
||||
// The CRC32 is in the Data Descriptor after the compressed size.
|
||||
// Note the Data Descriptor's Signature is optional:
|
||||
@@ -448,20 +482,22 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
// but older apps might not.
|
||||
fseek($this->in, $pos + $compressedSize);
|
||||
$localCrc = unpack('V', fread($this->in, 4))[1];
|
||||
|
||||
if ($localCrc === ZipEntry::DATA_DESCRIPTOR_SIG) {
|
||||
$localCrc = unpack('V', fread($this->in, 4))[1];
|
||||
}
|
||||
} else {
|
||||
fseek($this->in, $startPos + 14);
|
||||
// The CRC32 in the Local File Header.
|
||||
$localCrc = sprintf('%u', fread($this->in, 4)[1]);
|
||||
$localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc;
|
||||
$localCrc = fread($this->in, 4)[1];
|
||||
}
|
||||
|
||||
$crc = PHP_INT_SIZE === 4 ? sprintf('%u', $entry->getCrc()) : $entry->getCrc();
|
||||
|
||||
if ($crc != $localCrc) {
|
||||
throw new Crc32Exception($entry->getName(), $crc, $localCrc);
|
||||
if (\PHP_INT_SIZE === 4) {
|
||||
if (sprintf('%u', $entry->getCrc()) === sprintf('%u', $localCrc)) {
|
||||
throw new Crc32Exception($entry->getName(), $entry->getCrc(), $localCrc);
|
||||
}
|
||||
} elseif ($localCrc !== $entry->getCrc()) {
|
||||
throw new Crc32Exception($entry->getName(), $entry->getCrc(), $localCrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -469,51 +505,64 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
switch ($method) {
|
||||
case ZipFileInterface::METHOD_STORED:
|
||||
break;
|
||||
|
||||
case ZipFileInterface::METHOD_DEFLATED:
|
||||
$content = @gzinflate($content);
|
||||
break;
|
||||
|
||||
case ZipFileInterface::METHOD_BZIP2:
|
||||
if (!extension_loaded('bz2')) {
|
||||
if (!\extension_loaded('bz2')) {
|
||||
throw new ZipException('Extension bzip2 not install');
|
||||
}
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
$content = bzdecompress($content);
|
||||
if (is_int($content)) { // decompress error
|
||||
|
||||
if (\is_int($content)) { // decompress error
|
||||
$content = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ZipUnsupportMethodException($entry->getName() .
|
||||
" (compression method " . $method . " is not supported)");
|
||||
throw new ZipUnsupportMethodException(
|
||||
$entry->getName() .
|
||||
' (compression method ' . $method . ' is not supported)'
|
||||
);
|
||||
}
|
||||
|
||||
if ($content === false) {
|
||||
if ($isEncrypted) {
|
||||
throw new ZipAuthenticationException(sprintf(
|
||||
'Invalid password for zip entry "%s"',
|
||||
$entry->getName()
|
||||
));
|
||||
throw new ZipAuthenticationException(
|
||||
sprintf(
|
||||
'Invalid password for zip entry "%s"',
|
||||
$entry->getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
throw new ZipException(sprintf(
|
||||
'Failed to get the contents of the zip entry "%s"',
|
||||
$entry->getName()
|
||||
));
|
||||
|
||||
throw new ZipException(
|
||||
sprintf(
|
||||
'Failed to get the contents of the zip entry "%s"',
|
||||
$entry->getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!$skipCheckCrc) {
|
||||
$localCrc = crc32($content);
|
||||
$localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc;
|
||||
$crc = PHP_INT_SIZE === 4 ? sprintf('%u', $entry->getCrc()) : $entry->getCrc();
|
||||
if ($crc != $localCrc) {
|
||||
|
||||
if (sprintf('%u', $entry->getCrc()) !== sprintf('%u', $localCrc)) {
|
||||
if ($isEncrypted) {
|
||||
throw new ZipAuthenticationException(sprintf(
|
||||
'Invalid password for zip entry "%s"',
|
||||
$entry->getName()
|
||||
));
|
||||
throw new ZipAuthenticationException(
|
||||
sprintf(
|
||||
'Invalid password for zip entry "%s"',
|
||||
$entry->getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
throw new Crc32Exception($entry->getName(), $crc, $localCrc);
|
||||
|
||||
throw new Crc32Exception($entry->getName(), $entry->getCrc(), $localCrc);
|
||||
}
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
@@ -529,36 +578,39 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
* Copy the input stream of the LOC entry zip and the data into
|
||||
* the output stream and zip the alignment if necessary.
|
||||
*
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipOutputStreamInterface $out
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function copyEntry(ZipEntry $entry, ZipOutputStreamInterface $out)
|
||||
{
|
||||
$pos = $entry->getOffset();
|
||||
assert(ZipEntry::UNKNOWN !== $pos);
|
||||
$pos = PHP_INT_SIZE === 4 ? sprintf('%u', $pos) : $pos;
|
||||
\assert($pos !== ZipEntry::UNKNOWN);
|
||||
$pos = \PHP_INT_SIZE === 4 ? sprintf('%u', $pos) : $pos;
|
||||
$pos = $this->mapper->map($pos);
|
||||
|
||||
$nameLength = strlen($entry->getName());
|
||||
$nameLength = \strlen($entry->getName());
|
||||
|
||||
fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, SEEK_SET);
|
||||
fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, \SEEK_SET);
|
||||
$sourceExtraLength = $destExtraLength = unpack('v', fread($this->in, 2))[1];
|
||||
|
||||
if ($sourceExtraLength > 0) {
|
||||
// read Local File Header extra fields
|
||||
fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength, SEEK_SET);
|
||||
fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength, \SEEK_SET);
|
||||
$extra = '';
|
||||
$offset = 0;
|
||||
|
||||
while ($offset < $sourceExtraLength) {
|
||||
$read = min(8192 /* chunk size */, $sourceExtraLength - $offset);
|
||||
$extra .= fread($this->in, $read);
|
||||
$offset += $read;
|
||||
}
|
||||
$extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($extra, $entry);
|
||||
|
||||
if (isset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]) && $this->zipModel->isZipAlign()) {
|
||||
unset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]);
|
||||
$destExtraLength = strlen(ExtraFieldsFactory::createSerializedData($extraFieldsCollection));
|
||||
$destExtraLength = \strlen(ExtraFieldsFactory::createSerializedData($extraFieldsCollection));
|
||||
}
|
||||
} else {
|
||||
$extraFieldsCollection = new ExtraFieldsCollection();
|
||||
@@ -567,7 +619,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
$dataAlignmentMultiple = $this->zipModel->getZipAlign();
|
||||
$copyInToOutLength = $entry->getCompressedSize();
|
||||
|
||||
fseek($this->in, $pos, SEEK_SET);
|
||||
fseek($this->in, $pos, \SEEK_SET);
|
||||
|
||||
if (
|
||||
$this->zipModel->isZipAlign() &&
|
||||
@@ -599,23 +651,25 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
// from input stream to output stream
|
||||
stream_copy_to_stream($this->in, $out->getStream(), ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2);
|
||||
// write new extra field length (2 bytes) to output stream
|
||||
fwrite($out->getStream(), pack('v', strlen($extra)));
|
||||
fwrite($out->getStream(), pack('v', \strlen($extra)));
|
||||
// skip 2 bytes to input stream
|
||||
fseek($this->in, 2, SEEK_CUR);
|
||||
fseek($this->in, 2, \SEEK_CUR);
|
||||
// copy name from input stream to output stream
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $nameLength);
|
||||
// write extra field to output stream
|
||||
fwrite($out->getStream(), $extra);
|
||||
// skip source extraLength from input stream
|
||||
fseek($this->in, $sourceExtraLength, SEEK_CUR);
|
||||
fseek($this->in, $sourceExtraLength, \SEEK_CUR);
|
||||
} else {
|
||||
$copyInToOutLength += ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $sourceExtraLength + $nameLength;
|
||||
}
|
||||
|
||||
if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
|
||||
// crc-32 4 bytes
|
||||
// compressed size 4 bytes
|
||||
// uncompressed size 4 bytes
|
||||
$copyInToOutLength += 12;
|
||||
|
||||
if ($entry->isZip64ExtensionsRequired()) {
|
||||
// compressed size +4 bytes
|
||||
// uncompressed size +4 bytes
|
||||
@@ -627,20 +681,20 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipOutputStreamInterface $out
|
||||
*/
|
||||
public function copyEntryData(ZipEntry $entry, ZipOutputStreamInterface $out)
|
||||
{
|
||||
$offset = $entry->getOffset();
|
||||
$offset = PHP_INT_SIZE === 4 ? sprintf('%u', $offset) : $offset;
|
||||
$offset = \PHP_INT_SIZE === 4 ? sprintf('%u', $offset) : $offset;
|
||||
$offset = $this->mapper->map($offset);
|
||||
$nameLength = strlen($entry->getName());
|
||||
$nameLength = \strlen($entry->getName());
|
||||
|
||||
fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, SEEK_SET);
|
||||
fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, \SEEK_SET);
|
||||
$extraLength = unpack('v', fread($this->in, 2))[1];
|
||||
|
||||
fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength + $extraLength, SEEK_SET);
|
||||
fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength + $extraLength, \SEEK_SET);
|
||||
// copy raw data from input stream to output stream
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $entry->getCompressedSize());
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ use PhpZip\Model\ZipEntry;
|
||||
use PhpZip\Model\ZipModel;
|
||||
|
||||
/**
|
||||
* Read zip file
|
||||
* Read zip file.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
@@ -26,8 +26,10 @@ interface ZipInputStreamInterface
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function readEntryContent(ZipEntry $entry);
|
||||
|
||||
@@ -40,13 +42,13 @@ interface ZipInputStreamInterface
|
||||
* Copy the input stream of the LOC entry zip and the data into
|
||||
* the output stream and zip the alignment if necessary.
|
||||
*
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipOutputStreamInterface $out
|
||||
*/
|
||||
public function copyEntry(ZipEntry $entry, ZipOutputStreamInterface $out);
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipOutputStreamInterface $out
|
||||
*/
|
||||
public function copyEntryData(ZipEntry $entry, ZipOutputStreamInterface $out);
|
||||
|
@@ -22,30 +22,28 @@ use PhpZip\Util\StringUtil;
|
||||
use PhpZip\ZipFileInterface;
|
||||
|
||||
/**
|
||||
* Write zip file
|
||||
* Write zip file.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class ZipOutputStream implements ZipOutputStreamInterface
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
/** @var resource */
|
||||
protected $out;
|
||||
/**
|
||||
* @var ZipModel
|
||||
*/
|
||||
|
||||
/** @var ZipModel */
|
||||
protected $zipModel;
|
||||
|
||||
/**
|
||||
* ZipOutputStream constructor.
|
||||
*
|
||||
* @param resource $out
|
||||
* @param ZipModel $zipModel
|
||||
*/
|
||||
public function __construct($out, ZipModel $zipModel)
|
||||
{
|
||||
if (!is_resource($out)) {
|
||||
if (!\is_resource($out)) {
|
||||
throw new InvalidArgumentException('$out must be resource');
|
||||
}
|
||||
$this->out = $out;
|
||||
@@ -59,11 +57,13 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
{
|
||||
$entries = $this->zipModel->getEntries();
|
||||
$outPosEntries = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$outPosEntries[] = new OutputOffsetEntry(ftell($this->out), $entry);
|
||||
$this->writeEntry($entry);
|
||||
}
|
||||
$centralDirectoryOffset = ftell($this->out);
|
||||
|
||||
foreach ($outPosEntries as $outputEntry) {
|
||||
$this->writeCentralDirectoryHeader($outputEntry);
|
||||
}
|
||||
@@ -72,12 +72,14 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function writeEntry(ZipEntry $entry)
|
||||
{
|
||||
if ($entry instanceof ZipSourceEntry) {
|
||||
$entry->getInputStream()->copyEntry($entry, $this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -88,8 +90,8 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
|
||||
$extra = $entry->getExtra();
|
||||
|
||||
$nameLength = strlen($entry->getName());
|
||||
$extraLength = strlen($extra);
|
||||
$nameLength = \strlen($entry->getName());
|
||||
$extraLength = \strlen($extra);
|
||||
|
||||
// zip align
|
||||
if (
|
||||
@@ -98,6 +100,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
$entry->getMethod() === ZipFileInterface::METHOD_STORED
|
||||
) {
|
||||
$dataAlignmentMultiple = $this->zipModel->getZipAlign();
|
||||
|
||||
if (StringUtil::endsWith($entry->getName(), '.so')) {
|
||||
$dataAlignmentMultiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
|
||||
}
|
||||
@@ -120,15 +123,16 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
$extraFieldsCollection->add($alignExtra);
|
||||
|
||||
$extra = ExtraFieldsFactory::createSerializedData($extraFieldsCollection);
|
||||
$extraLength = strlen($extra);
|
||||
$extraLength = \strlen($extra);
|
||||
}
|
||||
|
||||
$size = $nameLength + $extraLength;
|
||||
|
||||
if ($size > 0xffff) {
|
||||
throw new ZipException(
|
||||
$entry->getName() . " (the total size of " . $size .
|
||||
" bytes for the name, extra fields and comment " .
|
||||
"exceeds the maximum size of " . 0xffff . " bytes)"
|
||||
$entry->getName() . ' (the total size of ' . $size .
|
||||
' bytes for the name, extra fields and comment ' .
|
||||
'exceeds the maximum size of ' . 0xffff . ' bytes)'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,9 +164,11 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
$extraLength
|
||||
)
|
||||
);
|
||||
|
||||
if ($nameLength > 0) {
|
||||
fwrite($this->out, $entry->getName());
|
||||
}
|
||||
|
||||
if ($extraLength > 0) {
|
||||
fwrite($this->out, $extra);
|
||||
}
|
||||
@@ -173,8 +179,9 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
fwrite($this->out, $entryContent);
|
||||
}
|
||||
|
||||
assert(ZipEntry::UNKNOWN !== $entry->getCrc());
|
||||
assert(ZipEntry::UNKNOWN !== $entry->getSize());
|
||||
\assert($entry->getCrc() !== ZipEntry::UNKNOWN);
|
||||
\assert($entry->getSize() !== ZipEntry::UNKNOWN);
|
||||
|
||||
if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
|
||||
// data descriptor signature 4 bytes (0x08074b50)
|
||||
// crc-32 4 bytes
|
||||
@@ -187,25 +194,28 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
} else {
|
||||
fwrite($this->out, pack('VV', $entry->getCompressedSize(), $entry->getSize()));
|
||||
}
|
||||
} elseif ($compressedSize != $entry->getCompressedSize()) {
|
||||
} elseif ($compressedSize !== $entry->getCompressedSize()) {
|
||||
throw new ZipException(
|
||||
$entry->getName() . " (expected compressed entry size of "
|
||||
. $entry->getCompressedSize() . " bytes, " .
|
||||
"but is actually " . $compressedSize . " bytes)"
|
||||
$entry->getName() . ' (expected compressed entry size of '
|
||||
. $entry->getCompressedSize() . ' bytes, ' .
|
||||
'but is actually ' . $compressedSize . ' bytes)'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @return null|string
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function entryCommitChangesAndReturnContent(ZipEntry $entry)
|
||||
{
|
||||
if ($entry->getPlatform() === ZipEntry::UNKNOWN) {
|
||||
$entry->setPlatform(ZipEntry::PLATFORM_UNIX);
|
||||
}
|
||||
|
||||
if ($entry->getTime() === ZipEntry::UNKNOWN) {
|
||||
$entry->setTime(time());
|
||||
}
|
||||
@@ -216,7 +226,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
$utf8 = true;
|
||||
|
||||
if ($encrypted && $entry->getPassword() === null) {
|
||||
throw new ZipException("Can not password from entry " . $entry->getName());
|
||||
throw new ZipException('Can not password from entry ' . $entry->getName());
|
||||
}
|
||||
|
||||
// Compose General Purpose Bit Flag.
|
||||
@@ -226,11 +236,12 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
|
||||
$entryContent = null;
|
||||
$extraFieldsCollection = $entry->getExtraFieldsCollection();
|
||||
|
||||
if (!($entry instanceof ZipChangesEntry && !$entry->isChangedContent())) {
|
||||
$entryContent = $entry->getEntryContent();
|
||||
|
||||
if ($entryContent !== null) {
|
||||
$entry->setSize(strlen($entryContent));
|
||||
$entry->setSize(\strlen($entryContent));
|
||||
$entry->setCrc(crc32($entryContent));
|
||||
|
||||
if ($encrypted && $method === ZipEntry::METHOD_WINZIP_AES) {
|
||||
@@ -238,6 +249,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
* @var WinZipAesEntryExtraField $field
|
||||
*/
|
||||
$field = $extraFieldsCollection->get(WinZipAesEntryExtraField::getHeaderId());
|
||||
|
||||
if ($field !== null) {
|
||||
$method = $field->getMethod();
|
||||
}
|
||||
@@ -252,12 +264,14 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
break;
|
||||
|
||||
case ZipFileInterface::METHOD_BZIP2:
|
||||
$compressionLevel = $entry->getCompressionLevel() === ZipFileInterface::LEVEL_DEFAULT_COMPRESSION ?
|
||||
$compressionLevel = $entry->getCompressionLevel(
|
||||
) === ZipFileInterface::LEVEL_DEFAULT_COMPRESSION ?
|
||||
ZipEntry::LEVEL_DEFAULT_BZIP2_COMPRESSION :
|
||||
$entry->getCompressionLevel();
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
$entryContent = bzcompress($entryContent, $compressionLevel);
|
||||
if (is_int($entryContent)) {
|
||||
|
||||
if (\is_int($entryContent)) {
|
||||
throw new ZipException('Error bzip2 compress. Error code: ' . $entryContent);
|
||||
}
|
||||
break;
|
||||
@@ -268,7 +282,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ZipException($entry->getName() . " (unsupported compression method " . $method . ")");
|
||||
throw new ZipException($entry->getName() . ' (unsupported compression method ' . $method . ')');
|
||||
}
|
||||
|
||||
if ($method === ZipFileInterface::METHOD_DEFLATED) {
|
||||
@@ -294,16 +308,23 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
}
|
||||
|
||||
if ($encrypted) {
|
||||
if (in_array($entry->getEncryptionMethod(), [
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128,
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192,
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256,
|
||||
], true)) {
|
||||
$keyStrength = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod($entry->getEncryptionMethod()); // size bits
|
||||
if (\in_array(
|
||||
$entry->getEncryptionMethod(),
|
||||
[
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128,
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192,
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256,
|
||||
],
|
||||
true
|
||||
)) {
|
||||
$keyStrength = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod(
|
||||
$entry->getEncryptionMethod()
|
||||
); // size bits
|
||||
$field = ExtraFieldsFactory::createWinZipAesEntryExtra();
|
||||
$field->setKeyStrength($keyStrength);
|
||||
$field->setMethod($method);
|
||||
$size = $entry->getSize();
|
||||
|
||||
if ($size >= 20 && $method !== ZipFileInterface::METHOD_BZIP2) {
|
||||
$field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_1);
|
||||
} else {
|
||||
@@ -321,7 +342,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
}
|
||||
}
|
||||
|
||||
$compressedSize = strlen($entryContent);
|
||||
$compressedSize = \strlen($entryContent);
|
||||
$entry->setCompressedSize($compressedSize);
|
||||
}
|
||||
}
|
||||
@@ -334,25 +355,31 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
} elseif ($extraFieldsCollection->has(Zip64ExtraField::getHeaderId())) {
|
||||
$extraFieldsCollection->remove(Zip64ExtraField::getHeaderId());
|
||||
}
|
||||
|
||||
return $entryContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @param string $content
|
||||
* @return string
|
||||
* @param string $content
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function determineBestCompressionMethod(ZipEntry $entry, $content)
|
||||
{
|
||||
if ($content !== null) {
|
||||
$entryContent = gzdeflate($content, $entry->getCompressionLevel());
|
||||
if (strlen($entryContent) < strlen($content)) {
|
||||
|
||||
if (\strlen($entryContent) < \strlen($content)) {
|
||||
$entry->setMethod(ZipFileInterface::METHOD_DEFLATED);
|
||||
|
||||
return $entryContent;
|
||||
}
|
||||
$entry->setMethod(ZipFileInterface::METHOD_STORED);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
@@ -369,12 +396,12 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
// This test MUST NOT include the CRC-32 because VV_AE_2 sets it to
|
||||
// UNKNOWN!
|
||||
if (($compressedSize | $size) === ZipEntry::UNKNOWN) {
|
||||
throw new RuntimeException("invalid entry");
|
||||
throw new RuntimeException('invalid entry');
|
||||
}
|
||||
$extra = $entry->getExtra();
|
||||
$extraSize = strlen($extra);
|
||||
$extraSize = \strlen($extra);
|
||||
|
||||
$commentLength = strlen($entry->getComment());
|
||||
$commentLength = \strlen($entry->getComment());
|
||||
fwrite(
|
||||
$this->out,
|
||||
pack(
|
||||
@@ -398,7 +425,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
// uncompressed size 4 bytes
|
||||
$entry->getSize(),
|
||||
// file name length 2 bytes
|
||||
strlen($entry->getName()),
|
||||
\strlen($entry->getName()),
|
||||
// extra field length 2 bytes
|
||||
$extraSize,
|
||||
// file comment length 2 bytes
|
||||
@@ -415,31 +442,38 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
);
|
||||
// file name (variable size)
|
||||
fwrite($this->out, $entry->getName());
|
||||
|
||||
if ($extraSize > 0) {
|
||||
// extra field (variable size)
|
||||
fwrite($this->out, $extra);
|
||||
}
|
||||
|
||||
if ($commentLength > 0) {
|
||||
// file comment (variable size)
|
||||
fwrite($this->out, $entry->getComment());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $centralDirectoryOffset
|
||||
*/
|
||||
protected function writeEndOfCentralDirectoryRecord($centralDirectoryOffset)
|
||||
{
|
||||
$centralDirectoryEntriesCount = count($this->zipModel);
|
||||
$centralDirectoryOffset = (int) $centralDirectoryOffset;
|
||||
$centralDirectoryEntriesCount = \count($this->zipModel);
|
||||
$position = ftell($this->out);
|
||||
$centralDirectorySize = $position - $centralDirectoryOffset;
|
||||
$centralDirectoryEntriesZip64 = $centralDirectoryEntriesCount > 0xffff;
|
||||
$centralDirectorySizeZip64 = $centralDirectorySize > 0xffffffff;
|
||||
$centralDirectoryOffsetZip64 = $centralDirectoryOffset > 0xffffffff;
|
||||
$centralDirectoryEntries16 = $centralDirectoryEntriesZip64 ? 0xffff : (int)$centralDirectoryEntriesCount;
|
||||
$centralDirectoryEntries16 = $centralDirectoryEntriesZip64 ? 0xffff : $centralDirectoryEntriesCount;
|
||||
$centralDirectorySize32 = $centralDirectorySizeZip64 ? 0xffffffff : $centralDirectorySize;
|
||||
$centralDirectoryOffset32 = $centralDirectoryOffsetZip64 ? 0xffffffff : $centralDirectoryOffset;
|
||||
$zip64 // ZIP64 extensions?
|
||||
= $centralDirectoryEntriesZip64
|
||||
|| $centralDirectorySizeZip64
|
||||
|| $centralDirectoryOffsetZip64;
|
||||
|
||||
if ($zip64) {
|
||||
// [zip64 end of central directory record]
|
||||
// relative offset of the zip64 end of central directory record
|
||||
@@ -449,7 +483,10 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
fwrite($this->out, pack('V', EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG));
|
||||
// size of zip64 end of central
|
||||
// directory record 8 bytes
|
||||
fwrite($this->out, PackUtil::packLongLE(EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN - 12));
|
||||
fwrite(
|
||||
$this->out,
|
||||
PackUtil::packLongLE(EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN - 12)
|
||||
);
|
||||
// version made by 2 bytes
|
||||
// version needed to extract 2 bytes
|
||||
// due to potential use of BZIP2 compression
|
||||
@@ -484,7 +521,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
fwrite($this->out, pack('V', 1));
|
||||
}
|
||||
$comment = $this->zipModel->getArchiveComment();
|
||||
$commentLength = strlen($comment);
|
||||
$commentLength = \strlen($comment);
|
||||
fwrite(
|
||||
$this->out,
|
||||
pack(
|
||||
@@ -512,6 +549,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
$commentLength
|
||||
)
|
||||
);
|
||||
|
||||
if ($commentLength > 0) {
|
||||
// .ZIP file comment (variable size)
|
||||
fwrite($this->out, $comment);
|
||||
|
@@ -5,7 +5,7 @@ namespace PhpZip\Stream;
|
||||
use PhpZip\Model\ZipEntry;
|
||||
|
||||
/**
|
||||
* Write zip file
|
||||
* Write zip file.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
|
@@ -5,31 +5,31 @@ namespace PhpZip\Util;
|
||||
use PhpZip\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Crypto Utils
|
||||
* Crypto Utils.
|
||||
*/
|
||||
class CryptoUtil
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns random bytes.
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public static function randomBytes($length)
|
||||
{
|
||||
$length = (int)$length;
|
||||
if (function_exists('random_bytes')) {
|
||||
$length = (int) $length;
|
||||
|
||||
if (\function_exists('random_bytes')) {
|
||||
try {
|
||||
return random_bytes($length);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException("Could not generate a random string.");
|
||||
throw new \RuntimeException('Could not generate a random string.');
|
||||
}
|
||||
} elseif (function_exists('openssl_random_pseudo_bytes')) {
|
||||
} elseif (\function_exists('openssl_random_pseudo_bytes')) {
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return openssl_random_pseudo_bytes($length);
|
||||
} elseif (function_exists('mcrypt_create_iv')) {
|
||||
/** @noinspection PhpDeprecationInspection */
|
||||
} elseif (\function_exists('mcrypt_create_iv')) {
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return mcrypt_create_iv($length);
|
||||
} else {
|
||||
|
@@ -28,13 +28,14 @@ class DateTimeConverter
|
||||
* Convert a 32 bit integer DOS date/time value to a UNIX timestamp value.
|
||||
*
|
||||
* @param int $dosTime Dos date/time
|
||||
*
|
||||
* @return int Unix timestamp
|
||||
*/
|
||||
public static function toUnixTimestamp($dosTime)
|
||||
{
|
||||
if (self::MIN_DOS_TIME > $dosTime) {
|
||||
if ($dosTime < self::MIN_DOS_TIME) {
|
||||
$dosTime = self::MIN_DOS_TIME;
|
||||
} elseif (self::MAX_DOS_TIME < $dosTime) {
|
||||
} elseif ($dosTime > self::MAX_DOS_TIME) {
|
||||
$dosTime = self::MAX_DOS_TIME;
|
||||
}
|
||||
|
||||
@@ -51,17 +52,19 @@ class DateTimeConverter
|
||||
/**
|
||||
* Converts a UNIX timestamp value to a DOS date/time value.
|
||||
*
|
||||
* @param int $unixTimestamp The number of seconds since midnight, January 1st,
|
||||
* 1970 AD UTC.
|
||||
* @return int A DOS date/time value reflecting the local time zone and
|
||||
* rounded down to even seconds
|
||||
* and is in between DateTimeConverter::MIN_DOS_TIME and DateTimeConverter::MAX_DOS_TIME.
|
||||
* @throws ZipException If unix timestamp is negative.
|
||||
* @param int $unixTimestamp the number of seconds since midnight, January 1st,
|
||||
* 1970 AD UTC
|
||||
*
|
||||
* @throws ZipException if unix timestamp is negative
|
||||
*
|
||||
* @return int a DOS date/time value reflecting the local time zone and
|
||||
* rounded down to even seconds
|
||||
* and is in between DateTimeConverter::MIN_DOS_TIME and DateTimeConverter::MAX_DOS_TIME
|
||||
*/
|
||||
public static function toDosTime($unixTimestamp)
|
||||
{
|
||||
if (0 > $unixTimestamp) {
|
||||
throw new ZipException("Negative unix timestamp: " . $unixTimestamp);
|
||||
if ($unixTimestamp < 0) {
|
||||
throw new ZipException('Negative unix timestamp: ' . $unixTimestamp);
|
||||
}
|
||||
|
||||
$date = getdate($unixTimestamp);
|
||||
@@ -71,8 +74,9 @@ class DateTimeConverter
|
||||
}
|
||||
|
||||
$date['year'] -= 1980;
|
||||
return ($date['year'] << 25 | $date['mon'] << 21 |
|
||||
|
||||
return $date['year'] << 25 | $date['mon'] << 21 |
|
||||
$date['mday'] << 16 | $date['hours'] << 11 |
|
||||
$date['minutes'] << 5 | $date['seconds'] >> 1);
|
||||
$date['minutes'] << 5 | $date['seconds'] >> 1;
|
||||
}
|
||||
}
|
||||
|
@@ -10,14 +10,16 @@ use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FilesUtil
|
||||
final class FilesUtil
|
||||
{
|
||||
|
||||
/**
|
||||
* Is empty directory
|
||||
* Is empty directory.
|
||||
*
|
||||
* @param string $dir Directory
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEmptyDir($dir)
|
||||
@@ -25,13 +27,14 @@ class FilesUtil
|
||||
if (!is_readable($dir)) {
|
||||
return false;
|
||||
}
|
||||
return count(scandir($dir)) === 2;
|
||||
|
||||
return \count(scandir($dir)) === 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove recursive directory.
|
||||
*
|
||||
* @param string $dir Directory path.
|
||||
* @param string $dir directory path
|
||||
*/
|
||||
public static function removeDir($dir)
|
||||
{
|
||||
@@ -39,6 +42,7 @@ class FilesUtil
|
||||
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($files as $fileInfo) {
|
||||
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
|
||||
$function($fileInfo->getRealPath());
|
||||
@@ -46,11 +50,11 @@ class FilesUtil
|
||||
rmdir($dir);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert glob pattern to regex pattern.
|
||||
*
|
||||
* @param string $globPattern
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function convertGlobToRegEx($globPattern)
|
||||
@@ -61,16 +65,19 @@ class FilesUtil
|
||||
$inCurrent = 0;
|
||||
$chars = str_split($globPattern);
|
||||
$regexPattern = '';
|
||||
|
||||
foreach ($chars as $currentChar) {
|
||||
switch ($currentChar) {
|
||||
case '*':
|
||||
$regexPattern .= ($escaping ? "\\*" : '.*');
|
||||
$regexPattern .= ($escaping ? '\\*' : '.*');
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
$regexPattern .= ($escaping ? "\\?" : '.');
|
||||
$regexPattern .= ($escaping ? '\\?' : '.');
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
case '(':
|
||||
case ')':
|
||||
@@ -83,41 +90,45 @@ class FilesUtil
|
||||
$regexPattern .= '\\' . $currentChar;
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if ($escaping) {
|
||||
$regexPattern .= "\\\\";
|
||||
$regexPattern .= '\\\\';
|
||||
$escaping = false;
|
||||
} else {
|
||||
$escaping = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '{':
|
||||
if ($escaping) {
|
||||
$regexPattern .= "\\{";
|
||||
$regexPattern .= '\\{';
|
||||
} else {
|
||||
$regexPattern = '(';
|
||||
$inCurrent++;
|
||||
}
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
if ($inCurrent > 0 && !$escaping) {
|
||||
$regexPattern .= ')';
|
||||
$inCurrent--;
|
||||
} elseif ($escaping) {
|
||||
$regexPattern = "\\}";
|
||||
$regexPattern = '\\}';
|
||||
} else {
|
||||
$regexPattern = "}";
|
||||
$regexPattern = '}';
|
||||
}
|
||||
$escaping = false;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if ($inCurrent > 0 && !$escaping) {
|
||||
$regexPattern .= '|';
|
||||
} elseif ($escaping) {
|
||||
$regexPattern .= "\\,";
|
||||
$regexPattern .= '\\,';
|
||||
} else {
|
||||
$regexPattern = ",";
|
||||
$regexPattern = ',';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -125,6 +136,7 @@ class FilesUtil
|
||||
$regexPattern .= $currentChar;
|
||||
}
|
||||
}
|
||||
|
||||
return $regexPattern;
|
||||
}
|
||||
|
||||
@@ -132,8 +144,9 @@ class FilesUtil
|
||||
* Search files.
|
||||
*
|
||||
* @param string $inputDir
|
||||
* @param bool $recursive
|
||||
* @param array $ignoreFiles
|
||||
* @param bool $recursive
|
||||
* @param array $ignoreFiles
|
||||
*
|
||||
* @return array Searched file list
|
||||
*/
|
||||
public static function fileSearchWithIgnore($inputDir, $recursive = true, array $ignoreFiles = [])
|
||||
@@ -153,11 +166,13 @@ class FilesUtil
|
||||
new \IteratorIterator($directoryIterator);
|
||||
|
||||
$fileList = [];
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file instanceof \SplFileInfo) {
|
||||
$fileList[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
@@ -165,21 +180,27 @@ class FilesUtil
|
||||
* Search files from glob pattern.
|
||||
*
|
||||
* @param string $globPattern
|
||||
* @param int $flags
|
||||
* @param bool $recursive
|
||||
* @param int $flags
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array Searched file list
|
||||
*/
|
||||
public static function globFileSearch($globPattern, $flags = 0, $recursive = true)
|
||||
{
|
||||
$flags = (int)$flags;
|
||||
$recursive = (bool)$recursive;
|
||||
$flags = (int) $flags;
|
||||
$recursive = (bool) $recursive;
|
||||
$files = glob($globPattern, $flags);
|
||||
|
||||
if (!$recursive) {
|
||||
return $files;
|
||||
}
|
||||
foreach (glob(dirname($globPattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
|
||||
|
||||
foreach (glob(\dirname($globPattern) . '/*', \GLOB_ONLYDIR | \GLOB_NOSORT) as $dir) {
|
||||
// Unpacking the argument via ... is supported starting from php 5.6 only
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
$files = array_merge($files, self::globFileSearch($dir . '/' . basename($globPattern), $flags, $recursive));
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
@@ -188,48 +209,58 @@ class FilesUtil
|
||||
*
|
||||
* @param string $folder
|
||||
* @param string $pattern
|
||||
* @param bool $recursive
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return array Searched file list
|
||||
*/
|
||||
public static function regexFileSearch($folder, $pattern, $recursive = true)
|
||||
{
|
||||
$directoryIterator = $recursive ? new \RecursiveDirectoryIterator($folder) : new \DirectoryIterator($folder);
|
||||
$iterator = $recursive ? new \RecursiveIteratorIterator($directoryIterator) : new \IteratorIterator($directoryIterator);
|
||||
$iterator = $recursive ? new \RecursiveIteratorIterator($directoryIterator) : new \IteratorIterator(
|
||||
$directoryIterator
|
||||
);
|
||||
$regexIterator = new \RegexIterator($iterator, $pattern, \RegexIterator::MATCH);
|
||||
$fileList = [];
|
||||
|
||||
foreach ($regexIterator as $file) {
|
||||
if ($file instanceof \SplFileInfo) {
|
||||
$fileList[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bytes to human size.
|
||||
*
|
||||
* @param int $size Size bytes
|
||||
* @param int $size Size bytes
|
||||
* @param string|null $unit Unit support 'GB', 'MB', 'KB'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function humanSize($size, $unit = null)
|
||||
{
|
||||
if (($unit === null && $size >= 1 << 30) || $unit === "GB") {
|
||||
return number_format($size / (1 << 30), 2) . "GB";
|
||||
if (($unit === null && $size >= 1 << 30) || $unit === 'GB') {
|
||||
return number_format($size / (1 << 30), 2) . 'GB';
|
||||
}
|
||||
if (($unit === null && $size >= 1 << 20) || $unit === "MB") {
|
||||
return number_format($size / (1 << 20), 2) . "MB";
|
||||
|
||||
if (($unit === null && $size >= 1 << 20) || $unit === 'MB') {
|
||||
return number_format($size / (1 << 20), 2) . 'MB';
|
||||
}
|
||||
if (($unit === null && $size >= 1 << 10) || $unit === "KB") {
|
||||
return number_format($size / (1 << 10), 2) . "KB";
|
||||
|
||||
if (($unit === null && $size >= 1 << 10) || $unit === 'KB') {
|
||||
return number_format($size / (1 << 10), 2) . 'KB';
|
||||
}
|
||||
return number_format($size) . " bytes";
|
||||
|
||||
return number_format($size) . ' bytes';
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes zip path.
|
||||
*
|
||||
* @param string $path Zip path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function normalizeZipPath($path)
|
||||
@@ -237,7 +268,7 @@ class FilesUtil
|
||||
return implode(
|
||||
'/',
|
||||
array_filter(
|
||||
explode('/', (string)$path),
|
||||
explode('/', (string) $path),
|
||||
static function ($part) {
|
||||
return $part !== '.' && $part !== '..';
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ use PhpZip\Util\StringUtil;
|
||||
class IgnoreFilesFilterIterator extends \FilterIterator
|
||||
{
|
||||
/**
|
||||
* Ignore list files
|
||||
* Ignore list files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -21,7 +21,7 @@ class IgnoreFilesFilterIterator extends \FilterIterator
|
||||
|
||||
/**
|
||||
* @param \Iterator $iterator
|
||||
* @param array $ignoreFiles
|
||||
* @param array $ignoreFiles
|
||||
*/
|
||||
public function __construct(\Iterator $iterator, array $ignoreFiles)
|
||||
{
|
||||
@@ -30,9 +30,12 @@ class IgnoreFilesFilterIterator extends \FilterIterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable
|
||||
* @link http://php.net/manual/en/filteriterator.accept.php
|
||||
* @return bool true if the current element is acceptable, otherwise false.
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @see http://php.net/manual/en/filteriterator.accept.php
|
||||
*
|
||||
* @return bool true if the current element is acceptable, otherwise false
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function accept()
|
||||
@@ -42,6 +45,7 @@ class IgnoreFilesFilterIterator extends \FilterIterator
|
||||
*/
|
||||
$fileInfo = $this->current();
|
||||
$pathname = str_replace('\\', '/', $fileInfo->getPathname());
|
||||
|
||||
foreach ($this->ignoreFiles as $ignoreFile) {
|
||||
// handler dir and sub dir
|
||||
if ($fileInfo->isDir()
|
||||
@@ -56,6 +60,7 @@ class IgnoreFilesFilterIterator extends \FilterIterator
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ use PhpZip\Util\StringUtil;
|
||||
class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
{
|
||||
/**
|
||||
* Ignore list files
|
||||
* Ignore list files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -21,7 +21,7 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
|
||||
/**
|
||||
* @param \RecursiveIterator $iterator
|
||||
* @param array $ignoreFiles
|
||||
* @param array $ignoreFiles
|
||||
*/
|
||||
public function __construct(\RecursiveIterator $iterator, array $ignoreFiles)
|
||||
{
|
||||
@@ -30,9 +30,12 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current element of the iterator is acceptable
|
||||
* @link http://php.net/manual/en/filteriterator.accept.php
|
||||
* @return bool true if the current element is acceptable, otherwise false.
|
||||
* Check whether the current element of the iterator is acceptable.
|
||||
*
|
||||
* @see http://php.net/manual/en/filteriterator.accept.php
|
||||
*
|
||||
* @return bool true if the current element is acceptable, otherwise false
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function accept()
|
||||
@@ -42,10 +45,11 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
*/
|
||||
$fileInfo = $this->current();
|
||||
$pathname = str_replace('\\', '/', $fileInfo->getPathname());
|
||||
|
||||
foreach ($this->ignoreFiles as $ignoreFile) {
|
||||
// handler dir and sub dir
|
||||
if ($fileInfo->isDir()
|
||||
&& $ignoreFile[strlen($ignoreFile) - 1] === '/'
|
||||
&& $ignoreFile[\strlen($ignoreFile) - 1] === '/'
|
||||
&& StringUtil::endsWith($pathname, substr($ignoreFile, 0, -1))
|
||||
) {
|
||||
return false;
|
||||
@@ -56,6 +60,7 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,7 +69,6 @@ class IgnoreFilesRecursiveFilterIterator extends \RecursiveFilterIterator
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
/** @noinspection PhpUndefinedMethodInspection */
|
||||
return new self($this->getInnerIterator()->getChildren(), $this->ignoreFiles);
|
||||
}
|
||||
}
|
||||
|
@@ -3,22 +3,24 @@
|
||||
namespace PhpZip\Util;
|
||||
|
||||
/**
|
||||
* Pack util
|
||||
* Pack util.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class PackUtil
|
||||
final class PackUtil
|
||||
{
|
||||
|
||||
/**
|
||||
* @param int|string $longValue
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function packLongLE($longValue)
|
||||
{
|
||||
if (PHP_INT_SIZE === 8 && PHP_VERSION_ID >= 506030) {
|
||||
return pack("P", $longValue);
|
||||
if (\PHP_INT_SIZE === 8 && \PHP_VERSION_ID >= 506030) {
|
||||
return pack('P', $longValue);
|
||||
}
|
||||
|
||||
$left = 0xffffffff00000000;
|
||||
@@ -32,31 +34,36 @@ class PackUtil
|
||||
|
||||
/**
|
||||
* @param string|int $value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function unpackLongLE($value)
|
||||
{
|
||||
if (PHP_INT_SIZE === 8 && PHP_VERSION_ID >= 506030) {
|
||||
if (\PHP_INT_SIZE === 8 && \PHP_VERSION_ID >= 506030) {
|
||||
return unpack('P', $value)[1];
|
||||
}
|
||||
$unpack = unpack('Va/Vb', $value);
|
||||
|
||||
return $unpack['a'] + ($unpack['b'] << 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast to signed int 32-bit
|
||||
* Cast to signed int 32-bit.
|
||||
*
|
||||
* @param int $int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function toSignedInt32($int)
|
||||
{
|
||||
if (PHP_INT_SIZE === 8) {
|
||||
$int = $int & 0xffffffff;
|
||||
if (\PHP_INT_SIZE === 8) {
|
||||
$int &= 0xffffffff;
|
||||
|
||||
if ($int & 0x80000000) {
|
||||
return $int - 0x100000000;
|
||||
}
|
||||
}
|
||||
|
||||
return $int;
|
||||
}
|
||||
}
|
||||
|
@@ -3,54 +3,32 @@
|
||||
namespace PhpZip\Util;
|
||||
|
||||
/**
|
||||
* String Util
|
||||
* String Util.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class StringUtil
|
||||
final class StringUtil
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($haystack, $needle)
|
||||
{
|
||||
return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false;
|
||||
return $needle === '' || strrpos($haystack, $needle, -\strlen($haystack)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function endsWith($haystack, $needle)
|
||||
{
|
||||
return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0
|
||||
return $needle === '' || (($temp = \strlen($haystack) - \strlen($needle)) >= 0
|
||||
&& strpos($haystack, $needle, $temp) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
public static function cp866toUtf8($str)
|
||||
{
|
||||
if (function_exists('iconv')) {
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return iconv('CP866', 'UTF-8//IGNORE', $str);
|
||||
} elseif (function_exists('mb_convert_encoding')) {
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
return mb_convert_encoding($str, 'UTF-8', 'CP866');
|
||||
} elseif (class_exists('UConverter')) {
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
$converter = new \UConverter('UTF-8', 'CP866');
|
||||
return $converter->convert($str, false);
|
||||
} else {
|
||||
static $cp866Utf8Pairs;
|
||||
if (empty($cp866Utf8Pairs)) {
|
||||
$cp866Utf8Pairs = require __DIR__ . '/encodings/cp866-utf8.php';
|
||||
}
|
||||
return strtr($str, $cp866Utf8Pairs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@ use Psr\Http\Message\ResponseInterface;
|
||||
* Support ZipAlign functional.
|
||||
*
|
||||
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
@@ -25,67 +26,66 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
{
|
||||
/**
|
||||
* Method for Stored (uncompressed) entries.
|
||||
*
|
||||
* @see ZipEntry::setMethod()
|
||||
*/
|
||||
const METHOD_STORED = 0;
|
||||
|
||||
/**
|
||||
* Method for Deflated compressed entries.
|
||||
*
|
||||
* @see ZipEntry::setMethod()
|
||||
*/
|
||||
const METHOD_DEFLATED = 8;
|
||||
|
||||
/**
|
||||
* Method for BZIP2 compressed entries.
|
||||
* Require php extension bz2.
|
||||
*
|
||||
* @see ZipEntry::setMethod()
|
||||
*/
|
||||
const METHOD_BZIP2 = 12;
|
||||
|
||||
/**
|
||||
* Default compression level.
|
||||
*/
|
||||
/** Default compression level. */
|
||||
const LEVEL_DEFAULT_COMPRESSION = -1;
|
||||
/**
|
||||
* Compression level for fastest compression.
|
||||
*/
|
||||
|
||||
/** Compression level for fastest compression. */
|
||||
const LEVEL_FAST = 2;
|
||||
/**
|
||||
* Compression level for fastest compression.
|
||||
*/
|
||||
|
||||
/** Compression level for fastest compression. */
|
||||
const LEVEL_BEST_SPEED = 1;
|
||||
|
||||
const LEVEL_SUPER_FAST = self::LEVEL_BEST_SPEED;
|
||||
/**
|
||||
* Compression level for best compression.
|
||||
*/
|
||||
|
||||
/** Compression level for best compression. */
|
||||
const LEVEL_BEST_COMPRESSION = 9;
|
||||
|
||||
/**
|
||||
* No specified method for set encryption method to Traditional PKWARE encryption.
|
||||
*/
|
||||
/** No specified method for set encryption method to Traditional PKWARE encryption. */
|
||||
const ENCRYPTION_METHOD_TRADITIONAL = 0;
|
||||
|
||||
/**
|
||||
* No specified method for set encryption method to WinZip AES encryption.
|
||||
* Default value 256 bit
|
||||
* Default value 256 bit.
|
||||
*/
|
||||
const ENCRYPTION_METHOD_WINZIP_AES = self::ENCRYPTION_METHOD_WINZIP_AES_256;
|
||||
/**
|
||||
* No specified method for set encryption method to WinZip AES encryption 128 bit.
|
||||
*/
|
||||
|
||||
/** No specified method for set encryption method to WinZip AES encryption 128 bit. */
|
||||
const ENCRYPTION_METHOD_WINZIP_AES_128 = 2;
|
||||
/**
|
||||
* No specified method for set encryption method to WinZip AES encryption 194 bit.
|
||||
*/
|
||||
|
||||
/** No specified method for set encryption method to WinZip AES encryption 194 bit. */
|
||||
const ENCRYPTION_METHOD_WINZIP_AES_192 = 3;
|
||||
/**
|
||||
* No specified method for set encryption method to WinZip AES encryption 256 bit.
|
||||
*/
|
||||
|
||||
/** No specified method for set encryption method to WinZip AES encryption 256 bit. */
|
||||
const ENCRYPTION_METHOD_WINZIP_AES_256 = 1;
|
||||
|
||||
/**
|
||||
* Open zip archive from file
|
||||
* Open zip archive from file.
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @throws ZipException if can't open file
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @throws ZipException if can't open file.
|
||||
*/
|
||||
public function openFile($filename);
|
||||
|
||||
@@ -93,35 +93,39 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Open zip archive from raw string data.
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @throws ZipException if can't open temp stream
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @throws ZipException if can't open temp stream.
|
||||
*/
|
||||
public function openFromString($data);
|
||||
|
||||
/**
|
||||
* Open zip archive from stream resource
|
||||
* Open zip archive from stream resource.
|
||||
*
|
||||
* @param resource $handle
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function openFromStream($handle);
|
||||
|
||||
/**
|
||||
* @return string[] Returns the list files.
|
||||
* @return string[] returns the list files
|
||||
*/
|
||||
public function getListFiles();
|
||||
|
||||
/**
|
||||
* Returns the file comment.
|
||||
*
|
||||
* @return string The file comment.
|
||||
* @return string the file comment
|
||||
*/
|
||||
public function getArchiveComment();
|
||||
|
||||
/**
|
||||
* Set archive comment.
|
||||
*
|
||||
* @param null|string $comment
|
||||
* @param string|null $comment
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function setArchiveComment($comment = null);
|
||||
@@ -132,8 +136,10 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* (i.e. end with '/').
|
||||
*
|
||||
* @param string $entryName
|
||||
* @return bool
|
||||
*
|
||||
* @throws ZipEntryNotFoundException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDirectory($entryName);
|
||||
|
||||
@@ -141,18 +147,22 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Returns entry comment.
|
||||
*
|
||||
* @param string $entryName
|
||||
* @return string
|
||||
*
|
||||
* @throws ZipEntryNotFoundException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEntryComment($entryName);
|
||||
|
||||
/**
|
||||
* Set entry comment.
|
||||
*
|
||||
* @param string $entryName
|
||||
* @param string $entryName
|
||||
* @param string|null $comment
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @throws ZipEntryNotFoundException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function setEntryComment($entryName, $comment = null);
|
||||
|
||||
@@ -160,6 +170,7 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Returns the entry contents.
|
||||
*
|
||||
* @param string $entryName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEntryContents($entryName);
|
||||
@@ -168,6 +179,7 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Checks if there is an entry in the archive.
|
||||
*
|
||||
* @param string $entryName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasEntry($entryName);
|
||||
@@ -176,8 +188,10 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Get info by entry.
|
||||
*
|
||||
* @param string|ZipEntry $entryName
|
||||
* @return ZipInfo
|
||||
*
|
||||
* @throws ZipEntryNotFoundException
|
||||
*
|
||||
* @return ZipInfo
|
||||
*/
|
||||
public function getEntryInfo($entryName);
|
||||
|
||||
@@ -194,27 +208,31 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
public function matcher();
|
||||
|
||||
/**
|
||||
* Extract the archive contents
|
||||
* Extract the archive contents.
|
||||
*
|
||||
* Extract the complete archive or the given files to the specified destination.
|
||||
*
|
||||
* @param string $destination 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.
|
||||
* @return ZipFileInterface
|
||||
* @param string $destination 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.
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function extractTo($destination, $entries = null);
|
||||
|
||||
/**
|
||||
* Add entry from the string.
|
||||
*
|
||||
* @param string $localName Zip entry name.
|
||||
* @param string $contents String contents.
|
||||
* @param string $localName zip entry name
|
||||
* @param string $contents string contents
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::METHOD_STORED
|
||||
* @see ZipFileInterface::METHOD_DEFLATED
|
||||
* @see ZipFileInterface::METHOD_BZIP2
|
||||
@@ -224,12 +242,14 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Add entry from the file.
|
||||
*
|
||||
* @param string $filename Destination file.
|
||||
* @param string|null $localName Zip Entry name.
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* @param string $filename destination file
|
||||
* @param string|null $localName zip Entry name
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or
|
||||
* ZipFile::METHOD_BZIP2. If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::METHOD_STORED
|
||||
* @see ZipFileInterface::METHOD_DEFLATED
|
||||
* @see ZipFileInterface::METHOD_BZIP2
|
||||
@@ -239,12 +259,14 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Add entry from the stream.
|
||||
*
|
||||
* @param resource $stream Stream resource.
|
||||
* @param string $localName Zip Entry name.
|
||||
* @param resource $stream stream resource
|
||||
* @param string $localName zip Entry name
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::METHOD_STORED
|
||||
* @see ZipFileInterface::METHOD_DEFLATED
|
||||
* @see ZipFileInterface::METHOD_BZIP2
|
||||
@@ -255,6 +277,7 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Add an empty directory in the zip archive.
|
||||
*
|
||||
* @param string $dirName
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function addEmptyDir($dirName);
|
||||
@@ -262,39 +285,44 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Add directory not recursively to the zip archive.
|
||||
*
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $localPath Add files to this directory, or the root.
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $localPath add files to this directory, or the root
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function addDir($inputDir, $localPath = "/", $compressionMethod = null);
|
||||
public function addDir($inputDir, $localPath = '/', $compressionMethod = null);
|
||||
|
||||
/**
|
||||
* Add recursive directory to the zip archive.
|
||||
*
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $localPath Add files to this directory, or the root.
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $localPath add files to this directory, or the root
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::METHOD_STORED
|
||||
* @see ZipFileInterface::METHOD_DEFLATED
|
||||
* @see ZipFileInterface::METHOD_BZIP2
|
||||
*/
|
||||
public function addDirRecursive($inputDir, $localPath = "/", $compressionMethod = null);
|
||||
public function addDirRecursive($inputDir, $localPath = '/', $compressionMethod = null);
|
||||
|
||||
/**
|
||||
* Add directories from directory iterator.
|
||||
*
|
||||
* @param \Iterator $iterator Directory iterator.
|
||||
* @param string $localPath Add files to this directory, or the root.
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* @param \Iterator $iterator directory iterator
|
||||
* @param string $localPath add files to this directory, or the root
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or
|
||||
* ZipFile::METHOD_BZIP2. If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::METHOD_STORED
|
||||
* @see ZipFileInterface::METHOD_DEFLATED
|
||||
* @see ZipFileInterface::METHOD_BZIP2
|
||||
@@ -304,12 +332,13 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Add files from glob pattern.
|
||||
*
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $globPattern Glob pattern.
|
||||
* @param string|null $localPath Add files to this directory, or the root.
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $globPattern glob pattern
|
||||
* @param string|null $localPath add files to this directory, or the root
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or
|
||||
* ZipFile::METHOD_BZIP2. If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
|
||||
*/
|
||||
@@ -318,12 +347,13 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Add files recursively from glob pattern.
|
||||
*
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $globPattern Glob pattern.
|
||||
* @param string|null $localPath Add files to this directory, or the root.
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* @param string $inputDir Input directory
|
||||
* @param string $globPattern glob pattern
|
||||
* @param string|null $localPath add files to this directory, or the root
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or
|
||||
* ZipFile::METHOD_BZIP2. If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
|
||||
*/
|
||||
@@ -332,56 +362,64 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Add files from regex pattern.
|
||||
*
|
||||
* @param string $inputDir Search files in this directory.
|
||||
* @param string $regexPattern Regex pattern.
|
||||
* @param string|null $localPath Add files to this directory, or the root.
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* @param string $inputDir search files in this directory
|
||||
* @param string $regexPattern regex pattern
|
||||
* @param string|null $localPath add files to this directory, or the root
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or
|
||||
* ZipFile::METHOD_BZIP2. If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @internal param bool $recursive Recursive search.
|
||||
*
|
||||
* @internal param bool $recursive Recursive search
|
||||
*/
|
||||
public function addFilesFromRegex($inputDir, $regexPattern, $localPath = "/", $compressionMethod = null);
|
||||
public function addFilesFromRegex($inputDir, $regexPattern, $localPath = '/', $compressionMethod = null);
|
||||
|
||||
/**
|
||||
* Add files recursively from regex pattern.
|
||||
*
|
||||
* @param string $inputDir Search files in this directory.
|
||||
* @param string $regexPattern Regex pattern.
|
||||
* @param string|null $localPath Add files to this directory, or the root.
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or ZipFile::METHOD_BZIP2.
|
||||
* If null, then auto choosing method.
|
||||
* @param string $inputDir search files in this directory
|
||||
* @param string $regexPattern regex pattern
|
||||
* @param string|null $localPath add files to this directory, or the root
|
||||
* @param int|null $compressionMethod Compression method.
|
||||
* Use ZipFile::METHOD_STORED, ZipFile::METHOD_DEFLATED or
|
||||
* ZipFile::METHOD_BZIP2. If null, then auto choosing method.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @internal param bool $recursive Recursive search.
|
||||
*
|
||||
* @internal param bool $recursive Recursive search
|
||||
*/
|
||||
public function addFilesFromRegexRecursive($inputDir, $regexPattern, $localPath = "/", $compressionMethod = null);
|
||||
public function addFilesFromRegexRecursive($inputDir, $regexPattern, $localPath = '/', $compressionMethod = null);
|
||||
|
||||
/**
|
||||
* Add array data to archive.
|
||||
* Keys is local names.
|
||||
* Values is contents.
|
||||
*
|
||||
* @param array $mapData Associative array for added to zip.
|
||||
* @param array $mapData associative array for added to zip
|
||||
*/
|
||||
public function addAll(array $mapData);
|
||||
|
||||
/**
|
||||
* Rename the entry.
|
||||
*
|
||||
* @param string $oldName Old entry name.
|
||||
* @param string $newName New entry name.
|
||||
* @return ZipFileInterface
|
||||
* @param string $oldName old entry name
|
||||
* @param string $newName new entry name
|
||||
*
|
||||
* @throws ZipEntryNotFoundException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function rename($oldName, $newName);
|
||||
|
||||
/**
|
||||
* Delete entry by name.
|
||||
*
|
||||
* @param string $entryName Zip Entry name.
|
||||
* @param string $entryName zip Entry name
|
||||
*
|
||||
* @throws ZipEntryNotFoundException if entry not found
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @throws ZipEntryNotFoundException If entry not found.
|
||||
*/
|
||||
public function deleteFromName($entryName);
|
||||
|
||||
@@ -389,6 +427,7 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Delete entries by glob pattern.
|
||||
*
|
||||
* @param string $globPattern Glob pattern
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @sse https://en.wikipedia.org/wiki/Glob_(programming) Glob pattern syntax
|
||||
*/
|
||||
@@ -398,12 +437,14 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Delete entries by regex pattern.
|
||||
*
|
||||
* @param string $regexPattern Regex pattern
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function deleteFromRegex($regexPattern);
|
||||
|
||||
/**
|
||||
* Delete all entries
|
||||
* Delete all entries.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function deleteAll();
|
||||
@@ -412,19 +453,24 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Set compression level for new entries.
|
||||
*
|
||||
* @param int $compressionLevel
|
||||
* @see ZipFileInterface::LEVEL_DEFAULT_COMPRESSION
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::LEVEL_SUPER_FAST
|
||||
* @see ZipFileInterface::LEVEL_FAST
|
||||
* @see ZipFileInterface::LEVEL_BEST_COMPRESSION
|
||||
* @return ZipFileInterface
|
||||
* @see ZipFileInterface::LEVEL_DEFAULT_COMPRESSION
|
||||
*/
|
||||
public function setCompressionLevel($compressionLevel = self::LEVEL_DEFAULT_COMPRESSION);
|
||||
|
||||
/**
|
||||
* @param string $entryName
|
||||
* @param int $compressionLevel
|
||||
* @return ZipFileInterface
|
||||
* @param int $compressionLevel
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::LEVEL_DEFAULT_COMPRESSION
|
||||
* @see ZipFileInterface::LEVEL_SUPER_FAST
|
||||
* @see ZipFileInterface::LEVEL_FAST
|
||||
@@ -434,9 +480,12 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
|
||||
/**
|
||||
* @param string $entryName
|
||||
* @param int $compressionMethod
|
||||
* @return ZipFileInterface
|
||||
* @param int $compressionMethod
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @see ZipFileInterface::METHOD_STORED
|
||||
* @see ZipFileInterface::METHOD_DEFLATED
|
||||
* @see ZipFileInterface::METHOD_BZIP2
|
||||
@@ -447,8 +496,10 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* zipalign is optimization to Android application (APK) files.
|
||||
*
|
||||
* @param int|null $align
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
* @link https://developer.android.com/studio/command-line/zipalign.html
|
||||
*
|
||||
* @see https://developer.android.com/studio/command-line/zipalign.html
|
||||
*/
|
||||
public function setZipAlign($align = null);
|
||||
|
||||
@@ -456,7 +507,9 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Set password to all input encrypted entries.
|
||||
*
|
||||
* @param string $password Password
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @deprecated using ZipFileInterface::setReadPassword()
|
||||
*/
|
||||
public function withReadPassword($password);
|
||||
@@ -465,6 +518,7 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Set password to all input encrypted entries.
|
||||
*
|
||||
* @param string $password Password
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function setReadPassword($password);
|
||||
@@ -473,7 +527,8 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Set password to concrete input entry.
|
||||
*
|
||||
* @param string $entryName
|
||||
* @param string $password Password
|
||||
* @param string $password Password
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function setReadPasswordEntry($entryName, $password);
|
||||
@@ -481,9 +536,11 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Set password for all entries for update.
|
||||
*
|
||||
* @param string $password If password null then encryption clear
|
||||
* @param string $password If password null then encryption clear
|
||||
* @param int|null $encryptionMethod Encryption method
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @deprecated using ZipFileInterface::setPassword()
|
||||
*/
|
||||
public function withNewPassword($password, $encryptionMethod = self::ENCRYPTION_METHOD_WINZIP_AES_256);
|
||||
@@ -491,8 +548,9 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Sets a new password for all files in the archive.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $password
|
||||
* @param int|null $encryptionMethod Encryption method
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function setPassword($password, $encryptionMethod = self::ENCRYPTION_METHOD_WINZIP_AES_256);
|
||||
@@ -500,41 +558,49 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
/**
|
||||
* Sets a new password of an entry defined by its name.
|
||||
*
|
||||
* @param string $entryName
|
||||
* @param string $password
|
||||
* @param string $entryName
|
||||
* @param string $password
|
||||
* @param int|null $encryptionMethod
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function setPasswordEntry($entryName, $password, $encryptionMethod = null);
|
||||
|
||||
/**
|
||||
* Remove password for all entries for update.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @deprecated using ZipFileInterface::disableEncryption()
|
||||
*/
|
||||
public function withoutPassword();
|
||||
|
||||
/**
|
||||
* Disable encryption for all entries that are already in the archive.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function disableEncryption();
|
||||
|
||||
/**
|
||||
* Disable encryption of an entry defined by its name.
|
||||
*
|
||||
* @param string $entryName
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function disableEncryptionEntry($entryName);
|
||||
|
||||
/**
|
||||
* Undo all changes done in the archive
|
||||
* Undo all changes done in the archive.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function unchangeAll();
|
||||
|
||||
/**
|
||||
* Undo change archive comment
|
||||
* Undo change archive comment.
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function unchangeArchiveComment();
|
||||
@@ -543,6 +609,7 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Revert all changes done to an entry with the given name.
|
||||
*
|
||||
* @param string|ZipEntry $entry Entry name or ZipEntry
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function unchangeEntry($entry);
|
||||
@@ -551,8 +618,10 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Save as file.
|
||||
*
|
||||
* @param string $filename Output filename
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function saveAsFile($filename);
|
||||
|
||||
@@ -560,8 +629,10 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Save as stream.
|
||||
*
|
||||
* @param resource $handle Output stream resource
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function saveAsStream($handle);
|
||||
|
||||
@@ -569,33 +640,42 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
|
||||
* Output .ZIP archive as attachment.
|
||||
* Die after output.
|
||||
*
|
||||
* @param string $outputFilename Output filename
|
||||
* @param string|null $mimeType Mime-Type
|
||||
* @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline
|
||||
* @param string $outputFilename Output filename
|
||||
* @param string|null $mimeType Mime-Type
|
||||
* @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline
|
||||
*/
|
||||
public function outputAsAttachment($outputFilename, $mimeType = null, $attachment = true);
|
||||
|
||||
/**
|
||||
* Output .ZIP archive as PSR-7 Response.
|
||||
*
|
||||
* @param ResponseInterface $response Instance PSR-7 Response
|
||||
* @param string $outputFilename Output filename
|
||||
* @param string|null $mimeType Mime-Type
|
||||
* @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline
|
||||
* @param ResponseInterface $response Instance PSR-7 Response
|
||||
* @param string $outputFilename Output filename
|
||||
* @param string|null $mimeType Mime-Type
|
||||
* @param bool $attachment Http Header 'Content-Disposition' if true then attachment otherwise inline
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function outputAsResponse(ResponseInterface $response, $outputFilename, $mimeType = null, $attachment = true);
|
||||
public function outputAsResponse(
|
||||
ResponseInterface $response,
|
||||
$outputFilename,
|
||||
$mimeType = null,
|
||||
$attachment = true
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the zip archive as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function outputAsString();
|
||||
|
||||
/**
|
||||
* Save and reopen zip archive.
|
||||
* @return ZipFileInterface
|
||||
*
|
||||
* @throws ZipException
|
||||
*
|
||||
* @return ZipFileInterface
|
||||
*/
|
||||
public function rewrite();
|
||||
|
||||
|
57
tests/PhpZip/Internal/DummyFileSystemStream.php
Normal file
57
tests/PhpZip/Internal/DummyFileSystemStream.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Internal;
|
||||
|
||||
/**
|
||||
* Try to load using dummy stream.
|
||||
*/
|
||||
class DummyFileSystemStream
|
||||
{
|
||||
/** @var resource */
|
||||
private $fp;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_open($path, $mode, $options)" . PHP_EOL;
|
||||
|
||||
$parsedUrl = parse_url($path);
|
||||
$path = $parsedUrl['path'];
|
||||
$this->fp = fopen($path, $mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_read($count)" . PHP_EOL;
|
||||
$position = ftell($this->fp);
|
||||
|
||||
// echo "Loading chunk " . $position . " to " . ($position + $count - 1) . PHP_EOL;
|
||||
return fread($this->fp, $count);
|
||||
// echo "String length: " . strlen($ret) . PHP_EOL;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_tell()" . PHP_EOL;
|
||||
return ftell($this->fp);
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_eof()" . PHP_EOL;
|
||||
return feof($this->fp);
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_seek($offset, $whence)" . PHP_EOL;
|
||||
fseek($this->fp, $offset, $whence);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_stat()" . PHP_EOL;
|
||||
return fstat($this->fp);
|
||||
}
|
||||
}
|
18
tests/PhpZip/Internal/ZipFileExtended.php
Normal file
18
tests/PhpZip/Internal/ZipFileExtended.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Internal;
|
||||
|
||||
use PhpZip\ZipFile;
|
||||
|
||||
/**
|
||||
* Class ZipFileExtended.
|
||||
*/
|
||||
class ZipFileExtended extends ZipFile
|
||||
{
|
||||
protected function onBeforeSave()
|
||||
{
|
||||
parent::onBeforeSave();
|
||||
$this->setZipAlign(4);
|
||||
$this->deleteFromRegex('~^META\-INF/~i');
|
||||
}
|
||||
}
|
@@ -5,6 +5,12 @@ namespace PhpZip;
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Util\CryptoUtil;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class Issue24Test extends ZipTestCase
|
||||
{
|
||||
/**
|
||||
@@ -12,7 +18,7 @@ class Issue24Test extends ZipTestCase
|
||||
*/
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
stream_wrapper_register("dummyfs", DummyFileSystemStream::class);
|
||||
stream_wrapper_register('dummyfs', Internal\DummyFileSystemStream::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,73 +38,13 @@ class Issue24Test extends ZipTestCase
|
||||
$zip->saveAsFile($this->outputFilename);
|
||||
$zip->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$stream = fopen('dummyfs://localhost/' . $this->outputFilename, 'rb');
|
||||
$this->assertNotFalse($stream);
|
||||
static::assertNotFalse($stream);
|
||||
$zip->openFromStream($stream);
|
||||
$this->assertEquals($zip->getListFiles(), ['file.txt']);
|
||||
$this->assertEquals($zip['file.txt'], $fileContents);
|
||||
static::assertSame($zip->getListFiles(), ['file.txt']);
|
||||
static::assertSame($zip['file.txt'], $fileContents);
|
||||
$zip->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to load using dummy stream
|
||||
*/
|
||||
class DummyFileSystemStream
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $fp;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_open($path, $mode, $options)" . PHP_EOL;
|
||||
|
||||
$parsedUrl = parse_url($path);
|
||||
$path = $parsedUrl['path'];
|
||||
$this->fp = fopen($path, $mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_read($count)" . PHP_EOL;
|
||||
$position = ftell($this->fp);
|
||||
|
||||
// echo "Loading chunk " . $position . " to " . ($position + $count - 1) . PHP_EOL;
|
||||
$ret = fread($this->fp, $count);
|
||||
|
||||
// echo "String length: " . strlen($ret) . PHP_EOL;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_tell()" . PHP_EOL;
|
||||
return ftell($this->fp);
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_eof()" . PHP_EOL;
|
||||
$isfeof = feof($this->fp);
|
||||
return $isfeof;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_seek($offset, $whence)" . PHP_EOL;
|
||||
fseek($this->fp, $offset, $whence);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
// echo "DummyFileSystemStream->stream_stat()" . PHP_EOL;
|
||||
return fstat($this->fp);
|
||||
}
|
||||
}
|
||||
|
@@ -2,16 +2,25 @@
|
||||
|
||||
namespace PhpZip;
|
||||
|
||||
use PhpZip\Exception\Crc32Exception;
|
||||
use PhpZip\Exception\ZipAuthenticationException;
|
||||
use PhpZip\Exception\ZipException;
|
||||
|
||||
/**
|
||||
* Some tests from the official extension of php-zip.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class PhpZipExtResourceTest extends ZipTestCase
|
||||
{
|
||||
/**
|
||||
* Bug #7214 (zip_entry_read() binary safe)
|
||||
* Bug #7214 (zip_entry_read() binary safe).
|
||||
*
|
||||
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/bug7214.phpt
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testBinaryNull()
|
||||
@@ -20,18 +29,21 @@ class PhpZipExtResourceTest extends ZipTestCase
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->openFile($filename);
|
||||
|
||||
foreach ($zipFile as $name => $contents) {
|
||||
$info = $zipFile->getEntryInfo($name);
|
||||
$this->assertEquals(strlen($contents), $info->getSize());
|
||||
static::assertSame(\strlen($contents), $info->getSize());
|
||||
}
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($filename);
|
||||
static::assertCorrectZipArchive($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug #8009 (cannot add again same entry to an archive)
|
||||
* Bug #8009 (cannot add again same entry to an archive).
|
||||
*
|
||||
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/bug8009.phpt
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testBug8009()
|
||||
@@ -44,36 +56,42 @@ class PhpZipExtResourceTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertCount(2, $zipFile);
|
||||
$this->assertTrue(isset($zipFile['1.txt']));
|
||||
$this->assertTrue(isset($zipFile['2.txt']));
|
||||
$this->assertEquals($zipFile['2.txt'], $zipFile['1.txt']);
|
||||
static::assertCount(2, $zipFile);
|
||||
static::assertTrue(isset($zipFile['1.txt']));
|
||||
static::assertTrue(isset($zipFile['2.txt']));
|
||||
static::assertSame($zipFile['2.txt'], $zipFile['1.txt']);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug #40228 (extractTo does not create recursive empty path)
|
||||
* Bug #40228 (extractTo does not create recursive empty path).
|
||||
*
|
||||
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/bug40228.phpt
|
||||
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/bug40228-mb.phpt
|
||||
* @dataProvider provideBug40228
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testBug40228($filename)
|
||||
{
|
||||
$this->assertTrue(mkdir($this->outputDirname, 0755, true));
|
||||
static::assertTrue(mkdir($this->outputDirname, 0755, true));
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->openFile($filename);
|
||||
$zipFile->extractTo($this->outputDirname);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertTrue(is_dir($this->outputDirname . '/test/empty'));
|
||||
static::assertTrue(is_dir($this->outputDirname . '/test/empty'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function provideBug40228()
|
||||
{
|
||||
return [
|
||||
@@ -82,14 +100,16 @@ class PhpZipExtResourceTest extends ZipTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug #49072 (feof never returns true for damaged file in zip)
|
||||
* Bug #49072 (feof never returns true for damaged file in zip).
|
||||
*
|
||||
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/bug49072.phpt
|
||||
* @expectedException \PhpZip\Exception\Crc32Exception
|
||||
* @expectedExceptionMessage file1
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testBug49072()
|
||||
{
|
||||
$this->setExpectedException(Crc32Exception::class, 'file1');
|
||||
|
||||
$filename = __DIR__ . '/php-zip-ext-test-resources/bug49072.zip';
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
@@ -98,34 +118,40 @@ class PhpZipExtResourceTest extends ZipTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug #70752 (Depacking with wrong password leaves 0 length files)
|
||||
* Bug #70752 (Depacking with wrong password leaves 0 length files).
|
||||
*
|
||||
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/bug70752.phpt
|
||||
* @expectedException \PhpZip\Exception\ZipAuthenticationException
|
||||
* @expectedExceptionMessage nvalid password for zip entry "bug70752.txt"
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testBug70752()
|
||||
{
|
||||
$this->setExpectedException(ZipAuthenticationException::class, 'nvalid password for zip entry "bug70752.txt"');
|
||||
|
||||
$filename = __DIR__ . '/php-zip-ext-test-resources/bug70752.zip';
|
||||
|
||||
$this->assertTrue(mkdir($this->outputDirname, 0755, true));
|
||||
static::assertTrue(mkdir($this->outputDirname, 0755, true));
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
|
||||
try {
|
||||
$zipFile->openFile($filename);
|
||||
$zipFile->setReadPassword('bar');
|
||||
$zipFile->extractTo($this->outputDirname);
|
||||
$this->markTestIncomplete('failed test');
|
||||
static::markTestIncomplete('failed test');
|
||||
} catch (ZipException $exception) {
|
||||
$this->assertFalse(file_exists($this->outputDirname . '/bug70752.txt'));
|
||||
static::assertFileNotExists($this->outputDirname . '/bug70752.txt');
|
||||
$zipFile->close();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug #12414 ( extracting files from damaged archives)
|
||||
* Bug #12414 ( extracting files from damaged archives).
|
||||
*
|
||||
* @see https://github.com/php/php-src/blob/master/ext/zip/tests/pecl12414.phpt
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testPecl12414()
|
||||
@@ -138,10 +164,10 @@ class PhpZipExtResourceTest extends ZipTestCase
|
||||
$zipFile->openFile($filename);
|
||||
|
||||
$info = $zipFile->getEntryInfo($entryName);
|
||||
$this->assertTrue($info->getSize() > 0);
|
||||
static::assertTrue($info->getSize() > 0);
|
||||
|
||||
$contents = $zipFile[$entryName];
|
||||
$this->assertEquals(strlen($contents), $info->getSize());
|
||||
static::assertSame(\strlen($contents), $info->getSize());
|
||||
|
||||
$zipFile->close();
|
||||
}
|
||||
|
45
tests/PhpZip/Zip64Test.php
Normal file
45
tests/PhpZip/Zip64Test.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip;
|
||||
|
||||
use PhpZip\Exception\ZipException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @large
|
||||
* @covers
|
||||
*/
|
||||
class Zip64Test extends ZipTestCase
|
||||
{
|
||||
/**
|
||||
* Test support ZIP64 ext (slow test - normal).
|
||||
* Create > 65535 files in archive and open and extract to /dev/null.
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testCreateAndOpenZip64Ext()
|
||||
{
|
||||
$countFiles = 0xffff + 1;
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
for ($i = 0; $i < $countFiles; $i++) {
|
||||
$zipFile[$i . '.txt'] = (string) $i;
|
||||
}
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
static::assertSame($zipFile->count(), $countFiles);
|
||||
$i = 0;
|
||||
|
||||
foreach ($zipFile as $entry => $content) {
|
||||
static::assertSame($entry, $i . '.txt');
|
||||
static::assertSame($content, (string) $i);
|
||||
$i++;
|
||||
}
|
||||
$zipFile->close();
|
||||
}
|
||||
}
|
@@ -6,7 +6,12 @@ use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Util\CryptoUtil;
|
||||
|
||||
/**
|
||||
* Test ZipAlign
|
||||
* Test ZipAlign.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class ZipAlignTest extends ZipTestCase
|
||||
{
|
||||
@@ -17,10 +22,11 @@ class ZipAlignTest extends ZipTestCase
|
||||
{
|
||||
$filename = __DIR__ . '/resources/apk.zip';
|
||||
|
||||
$this->assertCorrectZipArchive($filename);
|
||||
$result = $this->assertVerifyZipAlign($filename);
|
||||
if (null !== $result) {
|
||||
$this->assertTrue($result);
|
||||
static::assertCorrectZipArchive($filename);
|
||||
$result = static::assertVerifyZipAlign($filename);
|
||||
|
||||
if ($result !== null) {
|
||||
static::assertTrue($result);
|
||||
}
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
@@ -29,15 +35,17 @@ class ZipAlignTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
$result = $this->assertVerifyZipAlign($this->outputFilename, true);
|
||||
if (null !== $result) {
|
||||
$this->assertTrue($result);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
$result = static::assertVerifyZipAlign($this->outputFilename, true);
|
||||
|
||||
if ($result !== null) {
|
||||
static::assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test zip alignment.
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testZipAlignSourceZip()
|
||||
@@ -53,28 +61,29 @@ class ZipAlignTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$result = static::assertVerifyZipAlign($this->outputFilename);
|
||||
|
||||
$result = $this->assertVerifyZipAlign($this->outputFilename);
|
||||
if ($result === null) {
|
||||
return;
|
||||
} // zip align not installed
|
||||
|
||||
// check not zip align
|
||||
$this->assertFalse($result);
|
||||
static::assertFalse($result);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$zipFile->setZipAlign(4);
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$result = $this->assertVerifyZipAlign($this->outputFilename, true);
|
||||
$this->assertNotNull($result);
|
||||
$result = static::assertVerifyZipAlign($this->outputFilename, true);
|
||||
static::assertNotNull($result);
|
||||
|
||||
// check zip align
|
||||
$this->assertTrue($result);
|
||||
static::assertTrue($result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,14 +103,15 @@ class ZipAlignTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$result = static::assertVerifyZipAlign($this->outputFilename);
|
||||
|
||||
$result = $this->assertVerifyZipAlign($this->outputFilename);
|
||||
if ($result === null) {
|
||||
return;
|
||||
} // zip align not installed
|
||||
// check not zip align
|
||||
$this->assertTrue($result);
|
||||
static::assertTrue($result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,20 +130,21 @@ class ZipAlignTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$result = static::assertVerifyZipAlign($this->outputFilename);
|
||||
|
||||
$result = $this->assertVerifyZipAlign($this->outputFilename);
|
||||
if ($result === null) {
|
||||
return;
|
||||
} // zip align not installed
|
||||
|
||||
// check not zip align
|
||||
$this->assertFalse($result);
|
||||
static::assertFalse($result);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$zipFile->deleteFromRegex("~entry2[\d]+\.txt$~s");
|
||||
$zipFile->deleteFromRegex('~entry2[\\d]+\\.txt$~s');
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$isStored = (bool)mt_rand(0, 1);
|
||||
$isStored = (bool) mt_rand(0, 1);
|
||||
|
||||
$zipFile->addFromString(
|
||||
'entry_new_' . ($isStored ? 'stored' : 'deflated') . '_' . $i . '.txt',
|
||||
@@ -147,12 +158,12 @@ class ZipAlignTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$result = $this->assertVerifyZipAlign($this->outputFilename, true);
|
||||
$this->assertNotNull($result);
|
||||
$result = static::assertVerifyZipAlign($this->outputFilename, true);
|
||||
static::assertNotNull($result);
|
||||
|
||||
// check zip align
|
||||
$this->assertTrue($result);
|
||||
static::assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
@@ -4,16 +4,12 @@ namespace PhpZip;
|
||||
|
||||
use PhpZip\Exception\ZipException;
|
||||
|
||||
class ZipFileExtended extends ZipFile
|
||||
{
|
||||
protected function onBeforeSave()
|
||||
{
|
||||
parent::onBeforeSave();
|
||||
$this->setZipAlign(4);
|
||||
$this->deleteFromRegex('~^META\-INF/~i');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class ZipEventTest extends ZipTestCase
|
||||
{
|
||||
/**
|
||||
@@ -21,27 +17,28 @@ class ZipEventTest extends ZipTestCase
|
||||
*/
|
||||
public function testBeforeSave()
|
||||
{
|
||||
$zipFile = new ZipFileExtended();
|
||||
$zipFile = new Internal\ZipFileExtended();
|
||||
$zipFile->openFile(__DIR__ . '/resources/apk.zip');
|
||||
$this->assertTrue(isset($zipFile['META-INF/MANIFEST.MF']));
|
||||
$this->assertTrue(isset($zipFile['META-INF/CERT.SF']));
|
||||
$this->assertTrue(isset($zipFile['META-INF/CERT.RSA']));
|
||||
static::assertTrue(isset($zipFile['META-INF/MANIFEST.MF']));
|
||||
static::assertTrue(isset($zipFile['META-INF/CERT.SF']));
|
||||
static::assertTrue(isset($zipFile['META-INF/CERT.RSA']));
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$this->assertFalse(isset($zipFile['META-INF/MANIFEST.MF']));
|
||||
$this->assertFalse(isset($zipFile['META-INF/CERT.SF']));
|
||||
$this->assertFalse(isset($zipFile['META-INF/CERT.RSA']));
|
||||
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();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
$result = $this->assertVerifyZipAlign($this->outputFilename);
|
||||
if (null !== $result) {
|
||||
$this->assertTrue($result);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
$result = static::assertVerifyZipAlign($this->outputFilename);
|
||||
|
||||
if ($result !== null) {
|
||||
static::assertTrue($result);
|
||||
}
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFalse(isset($zipFile['META-INF/MANIFEST.MF']));
|
||||
$this->assertFalse(isset($zipFile['META-INF/CERT.SF']));
|
||||
$this->assertFalse(isset($zipFile['META-INF/CERT.RSA']));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,11 @@ use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
|
||||
|
||||
/**
|
||||
* Test add directory to zip archive.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class ZipFileAddDirTest extends ZipTestCase
|
||||
{
|
||||
@@ -28,7 +33,7 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
];
|
||||
|
||||
/**
|
||||
* Before test
|
||||
* Before test.
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
@@ -40,12 +45,14 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
{
|
||||
foreach (self::$files as $name => $content) {
|
||||
$fullName = $this->outputDirname . '/' . $name;
|
||||
|
||||
if ($content === null) {
|
||||
if (!is_dir($fullName)) {
|
||||
mkdir($fullName, 0755, true);
|
||||
}
|
||||
} else {
|
||||
$dirname = dirname($fullName);
|
||||
$dirname = \dirname($fullName);
|
||||
|
||||
if (!is_dir($dirname)) {
|
||||
mkdir($dirname, 0755, true);
|
||||
}
|
||||
@@ -54,23 +61,33 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
}
|
||||
}
|
||||
|
||||
protected static function assertFilesResult(ZipFileInterface $zipFile, array $actualResultFiles = [], $localPath = '/')
|
||||
{
|
||||
/**
|
||||
* @param ZipFileInterface $zipFile
|
||||
* @param array $actualResultFiles
|
||||
* @param string $localPath
|
||||
*/
|
||||
protected static function assertFilesResult(
|
||||
ZipFileInterface $zipFile,
|
||||
array $actualResultFiles = [],
|
||||
$localPath = '/'
|
||||
) {
|
||||
$localPath = rtrim($localPath, '/');
|
||||
$localPath = empty($localPath) ? "" : $localPath . '/';
|
||||
self::assertEquals(sizeof($zipFile), sizeof($actualResultFiles));
|
||||
$localPath = empty($localPath) ? '' : $localPath . '/';
|
||||
static::assertCount(\count($zipFile), $actualResultFiles);
|
||||
$actualResultFiles = array_flip($actualResultFiles);
|
||||
|
||||
foreach (self::$files as $file => $content) {
|
||||
$zipEntryName = $localPath . $file;
|
||||
|
||||
if (isset($actualResultFiles[$file])) {
|
||||
self::assertTrue(isset($zipFile[$zipEntryName]));
|
||||
self::assertEquals($zipFile[$zipEntryName], $content);
|
||||
static::assertTrue(isset($zipFile[$zipEntryName]));
|
||||
static::assertSame($zipFile[$zipEntryName], $content);
|
||||
unset($actualResultFiles[$file]);
|
||||
} else {
|
||||
self::assertFalse(isset($zipFile[$zipEntryName]));
|
||||
static::assertFalse(isset($zipFile[$zipEntryName]));
|
||||
}
|
||||
}
|
||||
self::assertEmpty($actualResultFiles);
|
||||
static::assertEmpty($actualResultFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,15 +102,19 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -107,15 +128,18 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -133,15 +157,19 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -159,15 +187,18 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -185,10 +216,10 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, array_keys(self::$files), $localPath);
|
||||
static::assertFilesResult($zipFile, array_keys(self::$files), $localPath);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -204,10 +235,10 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, array_keys(self::$files), $localPath);
|
||||
static::assertFilesResult($zipFile, array_keys(self::$files), $localPath);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -221,10 +252,10 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, array_keys(self::$files));
|
||||
static::assertFilesResult($zipFile, array_keys(self::$files));
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -236,7 +267,7 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$localPath = 'to/project';
|
||||
$ignoreFiles = [
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/'
|
||||
'empty dir/',
|
||||
];
|
||||
|
||||
$directoryIterator = new \DirectoryIterator($this->outputDirname);
|
||||
@@ -247,13 +278,17 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -278,24 +313,29 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
'catalog/New File',
|
||||
'catalog/New File 2',
|
||||
'catalog/Empty Dir/',
|
||||
'category/Pictures/128x160/Car/01.jpg',
|
||||
'category/Pictures/128x160/Car/02.jpg',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
'catalog/New File',
|
||||
'catalog/New File 2',
|
||||
'catalog/Empty Dir/',
|
||||
'category/Pictures/128x160/Car/01.jpg',
|
||||
'category/Pictures/128x160/Car/02.jpg',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create archive and add files from glob pattern
|
||||
* Create archive and add files from glob pattern.
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testAddFilesFromGlob()
|
||||
@@ -307,18 +347,23 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create archive and add recursively files from glob pattern
|
||||
* Create archive and add recursively files from glob pattern.
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testAddFilesFromGlobRecursive()
|
||||
@@ -330,23 +375,28 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'category/list.txt',
|
||||
'category/Pictures/128x160/Car/01.jpg',
|
||||
'category/Pictures/128x160/Car/02.jpg',
|
||||
'category/Pictures/240x320/Car/01.jpg',
|
||||
'category/Pictures/240x320/Car/02.jpg',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'category/list.txt',
|
||||
'category/Pictures/128x160/Car/01.jpg',
|
||||
'category/Pictures/128x160/Car/02.jpg',
|
||||
'category/Pictures/240x320/Car/01.jpg',
|
||||
'category/Pictures/240x320/Car/02.jpg',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create archive and add files from regex pattern
|
||||
* Create archive and add files from regex pattern.
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testAddFilesFromRegex()
|
||||
@@ -358,18 +408,23 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create archive and add files recursively from regex pattern
|
||||
* Create archive and add files recursively from regex pattern.
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testAddFilesFromRegexRecursive()
|
||||
@@ -381,18 +436,22 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, [
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'category/list.txt',
|
||||
'category/Pictures/128x160/Car/01.jpg',
|
||||
'category/Pictures/128x160/Car/02.jpg',
|
||||
'category/Pictures/240x320/Car/01.jpg',
|
||||
'category/Pictures/240x320/Car/02.jpg',
|
||||
], $localPath);
|
||||
static::assertFilesResult(
|
||||
$zipFile,
|
||||
[
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'category/list.txt',
|
||||
'category/Pictures/128x160/Car/01.jpg',
|
||||
'category/Pictures/128x160/Car/02.jpg',
|
||||
'category/Pictures/240x320/Car/01.jpg',
|
||||
'category/Pictures/240x320/Car/02.jpg',
|
||||
],
|
||||
$localPath
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -409,10 +468,10 @@ class ZipFileAddDirTest extends ZipTestCase
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertFilesResult($zipFile, array_keys(self::$files), $localPath);
|
||||
static::assertFilesResult($zipFile, array_keys(self::$files), $localPath);
|
||||
$zipFile->close();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,18 @@
|
||||
|
||||
namespace PhpZip;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PhpZip\Model\ZipEntryMatcher;
|
||||
use PhpZip\Model\ZipInfo;
|
||||
use PhpZip\Util\CryptoUtil;
|
||||
|
||||
class ZipMatcherTest extends \PHPUnit_Framework_TestCase
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class ZipMatcherTest extends TestCase
|
||||
{
|
||||
public function testMatcher()
|
||||
{
|
||||
@@ -16,41 +23,45 @@ class ZipMatcherTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
$matcher = $zipFile->matcher();
|
||||
$this->assertInstanceOf(ZipEntryMatcher::class, $matcher);
|
||||
static::assertInstanceOf(ZipEntryMatcher::class, $matcher);
|
||||
|
||||
$this->assertTrue(is_array($matcher->getMatches()));
|
||||
$this->assertCount(0, $matcher);
|
||||
static::assertInternalType('array', $matcher->getMatches());
|
||||
static::assertCount(0, $matcher);
|
||||
|
||||
$matcher->add(1)->add(10)->add(20);
|
||||
$this->assertCount(3, $matcher);
|
||||
$this->assertEquals($matcher->getMatches(), ['1', '10', '20']);
|
||||
static::assertCount(3, $matcher);
|
||||
static::assertEquals($matcher->getMatches(), ['1', '10', '20']);
|
||||
|
||||
$matcher->delete();
|
||||
$this->assertCount(97, $zipFile);
|
||||
$this->assertCount(0, $matcher);
|
||||
static::assertCount(97, $zipFile);
|
||||
static::assertCount(0, $matcher);
|
||||
|
||||
$matcher->match('~^[2][1-5]|[3][6-9]|40$~s');
|
||||
$this->assertCount(10, $matcher);
|
||||
static::assertCount(10, $matcher);
|
||||
$actualMatches = [
|
||||
'21', '22', '23', '24', '25',
|
||||
'36', '37', '38', '39',
|
||||
'40'
|
||||
'36', '37', '38', '39', '40',
|
||||
];
|
||||
$this->assertEquals($matcher->getMatches(), $actualMatches);
|
||||
static::assertSame($matcher->getMatches(), $actualMatches);
|
||||
$matcher->setPassword('qwerty');
|
||||
$info = $zipFile->getAllInfo();
|
||||
array_walk($info, function (ZipInfo $zipInfo) use ($actualMatches) {
|
||||
$this->assertEquals($zipInfo->isEncrypted(), in_array($zipInfo->getName(), $actualMatches));
|
||||
});
|
||||
array_walk(
|
||||
$info,
|
||||
function (ZipInfo $zipInfo) use ($actualMatches) {
|
||||
$this->assertSame($zipInfo->isEncrypted(), \in_array($zipInfo->getName(), $actualMatches, true));
|
||||
}
|
||||
);
|
||||
|
||||
$matcher->all();
|
||||
$this->assertCount(count($zipFile), $matcher);
|
||||
static::assertCount(\count($zipFile), $matcher);
|
||||
|
||||
$expectedNames = [];
|
||||
$matcher->invoke(function ($entryName) use (&$expectedNames) {
|
||||
$expectedNames[] = $entryName;
|
||||
});
|
||||
$this->assertEquals($expectedNames, $matcher->getMatches());
|
||||
$matcher->invoke(
|
||||
static function ($entryName) use (&$expectedNames) {
|
||||
$expectedNames[] = $entryName;
|
||||
}
|
||||
);
|
||||
static::assertSame($expectedNames, $matcher->getMatches());
|
||||
|
||||
$zipFile->close();
|
||||
}
|
||||
@@ -59,7 +70,7 @@ class ZipMatcherTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$zipFile = new ZipFile();
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$zipFile['file_'.$i.'.jpg'] = CryptoUtil::randomBytes(100);
|
||||
$zipFile['file_' . $i . '.jpg'] = CryptoUtil::randomBytes(100);
|
||||
}
|
||||
|
||||
$renameEntriesArray = [
|
||||
@@ -86,24 +97,26 @@ class ZipMatcherTest extends \PHPUnit_Framework_TestCase
|
||||
];
|
||||
|
||||
foreach ($renameEntriesArray as $name) {
|
||||
$this->assertTrue(isset($zipFile[$name]));
|
||||
static::assertTrue(isset($zipFile[$name]));
|
||||
}
|
||||
|
||||
$matcher = $zipFile->matcher();
|
||||
$matcher->match('~^file_(1|5)\d+~');
|
||||
$this->assertEquals($matcher->getMatches(), $renameEntriesArray);
|
||||
static::assertSame($matcher->getMatches(), $renameEntriesArray);
|
||||
|
||||
$matcher->invoke(function ($entryName) use ($zipFile) {
|
||||
$newName = preg_replace('~\.(jpe?g)$~i', '.no_optimize.$1', $entryName);
|
||||
$zipFile->rename($entryName, $newName);
|
||||
});
|
||||
$matcher->invoke(
|
||||
static function ($entryName) use ($zipFile) {
|
||||
$newName = preg_replace('~\.(jpe?g)$~i', '.no_optimize.$1', $entryName);
|
||||
$zipFile->rename($entryName, $newName);
|
||||
}
|
||||
);
|
||||
|
||||
foreach ($renameEntriesArray as $name) {
|
||||
$this->assertFalse(isset($zipFile[$name]));
|
||||
static::assertFalse(isset($zipFile[$name]));
|
||||
|
||||
$pathInfo = pathinfo($name);
|
||||
$newName = $pathInfo['filename'].'.no_optimize.'.$pathInfo['extension'];
|
||||
$this->assertTrue(isset($zipFile[$newName]));
|
||||
$newName = $pathInfo['filename'] . '.no_optimize.' . $pathInfo['extension'];
|
||||
static::assertTrue(isset($zipFile[$newName]));
|
||||
}
|
||||
|
||||
$zipFile->close();
|
||||
|
@@ -10,21 +10,27 @@ use PhpZip\Util\CryptoUtil;
|
||||
|
||||
/**
|
||||
* Tests with zip password.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class ZipPasswordTest extends ZipFileAddDirTest
|
||||
{
|
||||
/**
|
||||
* Test archive password.
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testSetPassword()
|
||||
{
|
||||
if (PHP_INT_SIZE === 4) {
|
||||
$this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
if (\PHP_INT_SIZE === 4) {
|
||||
static::markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
}
|
||||
|
||||
$password = base64_encode(CryptoUtil::randomBytes(100));
|
||||
$badPassword = "bad password";
|
||||
$badPassword = 'bad password';
|
||||
|
||||
// create encryption password with ZipCrypto
|
||||
$zipFile = new ZipFile();
|
||||
@@ -33,28 +39,30 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, $password);
|
||||
static::assertCorrectZipArchive($this->outputFilename, $password);
|
||||
|
||||
// check bad password for ZipCrypto
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$zipFile->setReadPassword($badPassword);
|
||||
|
||||
foreach ($zipFile->getListFiles() as $entryName) {
|
||||
try {
|
||||
$zipFile[$entryName];
|
||||
$this->fail("Expected Exception has not been raised.");
|
||||
static::fail('Expected Exception has not been raised.');
|
||||
} catch (ZipAuthenticationException $ae) {
|
||||
$this->assertContains('Invalid password for zip entry', $ae->getMessage());
|
||||
static::assertContains('Invalid password for zip entry', $ae->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// check correct password for ZipCrypto
|
||||
$zipFile->setReadPassword($password);
|
||||
|
||||
foreach ($zipFile->getAllInfo() as $info) {
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('ZipCrypto', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('ZipCrypto', $info->getMethodName());
|
||||
$decryptContent = $zipFile[$info->getName()];
|
||||
$this->assertNotEmpty($decryptContent);
|
||||
$this->assertContains('<?php', $decryptContent);
|
||||
static::assertNotEmpty($decryptContent);
|
||||
static::assertContains('<?php', $decryptContent);
|
||||
}
|
||||
|
||||
// change encryption method to WinZip Aes and update file
|
||||
@@ -62,29 +70,31 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, $password);
|
||||
static::assertCorrectZipArchive($this->outputFilename, $password);
|
||||
|
||||
// check from WinZip AES encryption
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
// set bad password WinZip AES
|
||||
$zipFile->setReadPassword($badPassword);
|
||||
|
||||
foreach ($zipFile->getListFiles() as $entryName) {
|
||||
try {
|
||||
$zipFile[$entryName];
|
||||
$this->fail("Expected Exception has not been raised.");
|
||||
static::fail('Expected Exception has not been raised.');
|
||||
} catch (ZipAuthenticationException $ae) {
|
||||
$this->assertNotNull($ae);
|
||||
static::assertNotNull($ae);
|
||||
}
|
||||
}
|
||||
|
||||
// set correct password WinZip AES
|
||||
$zipFile->setReadPassword($password);
|
||||
|
||||
foreach ($zipFile->getAllInfo() as $info) {
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('WinZip', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('WinZip', $info->getMethodName());
|
||||
$decryptContent = $zipFile[$info->getName()];
|
||||
$this->assertNotEmpty($decryptContent);
|
||||
$this->assertContains('<?php', $decryptContent);
|
||||
static::assertNotEmpty($decryptContent);
|
||||
static::assertContains('<?php', $decryptContent);
|
||||
}
|
||||
|
||||
// clear password
|
||||
@@ -94,12 +104,13 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename);
|
||||
static::assertCorrectZipArchive($this->outputFilename);
|
||||
|
||||
// check remove password
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
|
||||
foreach ($zipFile->getAllInfo() as $info) {
|
||||
$this->assertFalse($info->isEncrypted());
|
||||
static::assertFalse($info->isEncrypted());
|
||||
}
|
||||
$zipFile->close();
|
||||
}
|
||||
@@ -109,8 +120,8 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
*/
|
||||
public function testTraditionalEncryption()
|
||||
{
|
||||
if (PHP_INT_SIZE === 4) {
|
||||
$this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
if (\PHP_INT_SIZE === 4) {
|
||||
static::markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
}
|
||||
|
||||
$password = base64_encode(CryptoUtil::randomBytes(50));
|
||||
@@ -121,15 +132,16 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zip->saveAsFile($this->outputFilename);
|
||||
$zip->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, $password);
|
||||
static::assertCorrectZipArchive($this->outputFilename, $password);
|
||||
|
||||
$zip->openFile($this->outputFilename);
|
||||
$zip->setReadPassword($password);
|
||||
$this->assertFilesResult($zip, array_keys(self::$files));
|
||||
static::assertFilesResult($zip, array_keys(self::$files));
|
||||
|
||||
foreach ($zip->getAllInfo() as $info) {
|
||||
if (!$info->isFolder()) {
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('ZipCrypto', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('ZipCrypto', $info->getMethodName());
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
@@ -137,8 +149,10 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
|
||||
/**
|
||||
* @dataProvider winZipKeyStrengthProvider
|
||||
*
|
||||
* @param int $encryptionMethod
|
||||
* @param int $bitSize
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testWinZipAesEncryption($encryptionMethod, $bitSize)
|
||||
@@ -151,16 +165,17 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zip->saveAsFile($this->outputFilename);
|
||||
$zip->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, $password);
|
||||
static::assertCorrectZipArchive($this->outputFilename, $password);
|
||||
|
||||
$zip->openFile($this->outputFilename);
|
||||
$zip->setReadPassword($password);
|
||||
$this->assertFilesResult($zip, array_keys(self::$files));
|
||||
static::assertFilesResult($zip, array_keys(self::$files));
|
||||
|
||||
foreach ($zip->getAllInfo() as $info) {
|
||||
if (!$info->isFolder()) {
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertEquals($info->getEncryptionMethod(), $encryptionMethod);
|
||||
$this->assertContains('WinZip AES-' . $bitSize, $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertSame($info->getEncryptionMethod(), $encryptionMethod);
|
||||
static::assertContains('WinZip AES-' . $bitSize, $info->getMethodName());
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
@@ -185,8 +200,8 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
*/
|
||||
public function testEncryptionEntries()
|
||||
{
|
||||
if (PHP_INT_SIZE === 4) {
|
||||
$this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
if (\PHP_INT_SIZE === 4) {
|
||||
static::markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
}
|
||||
|
||||
$password1 = '353442434235424234';
|
||||
@@ -202,23 +217,26 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zip->openFile($this->outputFilename);
|
||||
$zip->setReadPasswordEntry('.hidden', $password1);
|
||||
$zip->setReadPasswordEntry('text file.txt', $password2);
|
||||
$this->assertFilesResult($zip, [
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]);
|
||||
static::assertFilesResult(
|
||||
$zip,
|
||||
[
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]
|
||||
);
|
||||
|
||||
$info = $zip->getEntryInfo('.hidden');
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('ZipCrypto', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('ZipCrypto', $info->getMethodName());
|
||||
|
||||
$info = $zip->getEntryInfo('text file.txt');
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('WinZip AES', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('WinZip AES', $info->getMethodName());
|
||||
|
||||
$this->assertFalse($zip->getEntryInfo('Текстовый документ.txt')->isEncrypted());
|
||||
$this->assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
|
||||
static::assertFalse($zip->getEntryInfo('Текстовый документ.txt')->isEncrypted());
|
||||
static::assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
|
||||
|
||||
$zip->close();
|
||||
}
|
||||
@@ -229,8 +247,8 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
*/
|
||||
public function testEncryptionEntriesWithDefaultPassword()
|
||||
{
|
||||
if (PHP_INT_SIZE === 4) {
|
||||
$this->markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
if (\PHP_INT_SIZE === 4) {
|
||||
static::markTestSkipped('Skip test for 32-bit system. Not support Traditional PKWARE Encryption.');
|
||||
}
|
||||
|
||||
$password1 = '353442434235424234';
|
||||
@@ -249,36 +267,40 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zip->setReadPassword($defaultPassword);
|
||||
$zip->setReadPasswordEntry('.hidden', $password1);
|
||||
$zip->setReadPasswordEntry('text file.txt', $password2);
|
||||
$this->assertFilesResult($zip, [
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]);
|
||||
static::assertFilesResult(
|
||||
$zip,
|
||||
[
|
||||
'.hidden',
|
||||
'text file.txt',
|
||||
'Текстовый документ.txt',
|
||||
'empty dir/',
|
||||
]
|
||||
);
|
||||
|
||||
$info = $zip->getEntryInfo('.hidden');
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('ZipCrypto', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('ZipCrypto', $info->getMethodName());
|
||||
|
||||
$info = $zip->getEntryInfo('text file.txt');
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('WinZip AES', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('WinZip AES', $info->getMethodName());
|
||||
|
||||
$info = $zip->getEntryInfo('Текстовый документ.txt');
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
$this->assertContains('WinZip AES', $info->getMethodName());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
static::assertContains('WinZip AES', $info->getMethodName());
|
||||
|
||||
$this->assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
|
||||
static::assertFalse($zip->getEntryInfo('empty dir/')->isEncrypted());
|
||||
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \PhpZip\Exception\ZipException
|
||||
* @expectedExceptionMessage Invalid encryption method
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testSetEncryptionMethodInvalid()
|
||||
{
|
||||
$this->setExpectedException(ZipException::class, 'Invalid encryption method');
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$encryptionMethod = 9999;
|
||||
$zipFile->setPassword('pass', $encryptionMethod);
|
||||
@@ -295,33 +317,38 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->setPassword('pass');
|
||||
$zipFile['file'] = 'content';
|
||||
$this->assertFalse($zipFile->getEntryInfo('file')->isEncrypted());
|
||||
static::assertFalse($zipFile->getEntryInfo('file')->isEncrypted());
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
$zipFile['file' . $i] = 'content';
|
||||
|
||||
if ($i < 6) {
|
||||
$zipFile->setPasswordEntry('file' . $i, 'pass');
|
||||
$this->assertTrue($zipFile->getEntryInfo('file' . $i)->isEncrypted());
|
||||
static::assertTrue($zipFile->getEntryInfo('file' . $i)->isEncrypted());
|
||||
} else {
|
||||
$this->assertFalse($zipFile->getEntryInfo('file' . $i)->isEncrypted());
|
||||
static::assertFalse($zipFile->getEntryInfo('file' . $i)->isEncrypted());
|
||||
}
|
||||
}
|
||||
$zipFile->disableEncryptionEntry('file3');
|
||||
$this->assertFalse($zipFile->getEntryInfo('file3')->isEncrypted());
|
||||
$this->asserttrue($zipFile->getEntryInfo('file2')->isEncrypted());
|
||||
static::assertFalse($zipFile->getEntryInfo('file3')->isEncrypted());
|
||||
static::assertTrue($zipFile->getEntryInfo('file2')->isEncrypted());
|
||||
$zipFile->disableEncryption();
|
||||
$infoList = $zipFile->getAllInfo();
|
||||
array_walk($infoList, function (ZipInfo $zipInfo) {
|
||||
$this->assertFalse($zipInfo->isEncrypted());
|
||||
});
|
||||
array_walk(
|
||||
$infoList,
|
||||
function (ZipInfo $zipInfo) {
|
||||
$this->assertFalse($zipInfo->isEncrypted());
|
||||
}
|
||||
);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \PhpZip\Exception\ZipException
|
||||
* @expectedExceptionMessage Invalid encryption method
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testInvalidEncryptionMethodEntry()
|
||||
{
|
||||
$this->setExpectedException(ZipException::class, 'Invalid encryption method');
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->addFromString('file', 'content', ZipFileInterface::METHOD_STORED);
|
||||
$zipFile->setPasswordEntry('file', 'pass', 99);
|
||||
@@ -341,42 +368,44 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, 'password');
|
||||
static::assertCorrectZipArchive($this->outputFilename, 'password');
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$this->assertCount(3, $zipFile);
|
||||
static::assertCount(3, $zipFile);
|
||||
|
||||
foreach ($zipFile->getAllInfo() as $info) {
|
||||
$this->assertTrue($info->isEncrypted());
|
||||
static::assertTrue($info->isEncrypted());
|
||||
}
|
||||
unset($zipFile['file3']);
|
||||
$zipFile['file4'] = 'content';
|
||||
$zipFile->rewrite();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, 'password');
|
||||
static::assertCorrectZipArchive($this->outputFilename, 'password');
|
||||
|
||||
$this->assertCount(3, $zipFile);
|
||||
$this->assertFalse(isset($zipFile['file3']));
|
||||
$this->assertTrue(isset($zipFile['file4']));
|
||||
$this->assertTrue($zipFile->getEntryInfo('file1')->isEncrypted());
|
||||
$this->assertTrue($zipFile->getEntryInfo('file2')->isEncrypted());
|
||||
$this->assertFalse($zipFile->getEntryInfo('file4')->isEncrypted());
|
||||
$this->assertEquals($zipFile['file4'], 'content');
|
||||
static::assertCount(3, $zipFile);
|
||||
static::assertFalse(isset($zipFile['file3']));
|
||||
static::assertTrue(isset($zipFile['file4']));
|
||||
static::assertTrue($zipFile->getEntryInfo('file1')->isEncrypted());
|
||||
static::assertTrue($zipFile->getEntryInfo('file2')->isEncrypted());
|
||||
static::assertFalse($zipFile->getEntryInfo('file4')->isEncrypted());
|
||||
static::assertSame($zipFile['file4'], 'content');
|
||||
|
||||
$zipFile->extractTo($this->outputDirname, ['file4']);
|
||||
|
||||
$this->assertTrue(file_exists($this->outputDirname . DIRECTORY_SEPARATOR . 'file4'));
|
||||
$this->assertEquals(file_get_contents($this->outputDirname . DIRECTORY_SEPARATOR . 'file4'), $zipFile['file4']);
|
||||
static::assertFileExists($this->outputDirname . \DIRECTORY_SEPARATOR . 'file4');
|
||||
static::assertStringEqualsFile($this->outputDirname . \DIRECTORY_SEPARATOR . 'file4', $zipFile['file4']);
|
||||
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/Ne-Lexa/php-zip/issues/9
|
||||
*
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function testIssues9()
|
||||
{
|
||||
$contents = str_pad('', 1000, 'test;test2;test3' . PHP_EOL, STR_PAD_RIGHT);
|
||||
$contents = str_pad('', 1000, 'test;test2;test3' . \PHP_EOL, \STR_PAD_RIGHT);
|
||||
$password = base64_encode(CryptoUtil::randomBytes(20));
|
||||
|
||||
$encryptMethod = ZipFile::ENCRYPTION_METHOD_WINZIP_AES_256;
|
||||
@@ -385,13 +414,14 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
->addFromString('codes.csv', $contents)
|
||||
->setPassword($password, $encryptMethod)
|
||||
->saveAsFile($this->outputFilename)
|
||||
->close();
|
||||
->close()
|
||||
;
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, $password);
|
||||
static::assertCorrectZipArchive($this->outputFilename, $password);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$zipFile->setReadPassword($password);
|
||||
$this->assertEquals($zipFile['codes.csv'], $contents);
|
||||
static::assertSame($zipFile['codes.csv'], $contents);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -413,12 +443,13 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
$zipFile2 = new ZipFile();
|
||||
$zipFile2->openFile($this->outputFilename);
|
||||
$zipFile2->setReadPassword($password);
|
||||
$this->assertEquals($zipFile2->getListFiles(), $zipFile->getListFiles());
|
||||
static::assertSame($zipFile2->getListFiles(), $zipFile->getListFiles());
|
||||
|
||||
foreach ($zipFile as $name => $contents) {
|
||||
$this->assertNotEmpty($name);
|
||||
$this->assertNotEmpty($contents);
|
||||
$this->assertContains('test contents', $contents);
|
||||
$this->assertEquals($zipFile2[$name], $contents);
|
||||
static::assertNotEmpty($name);
|
||||
static::assertNotEmpty($contents);
|
||||
static::assertContains('test contents', $contents);
|
||||
static::assertSame($zipFile2[$name], $contents);
|
||||
}
|
||||
$zipFile2->close();
|
||||
|
||||
|
@@ -3,20 +3,17 @@
|
||||
namespace PhpZip;
|
||||
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Util\Iterator\IgnoreFilesFilterIterator;
|
||||
use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
|
||||
|
||||
/**
|
||||
* Test add remote files to zip archive
|
||||
* Test add remote files to zip archive.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class ZipRemoteFileTest extends ZipTestCase
|
||||
{
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ZipException
|
||||
*/
|
||||
@@ -25,16 +22,28 @@ class ZipRemoteFileTest extends ZipTestCase
|
||||
$zipFile = new ZipFile();
|
||||
$outputZip = $this->outputFilename;
|
||||
$fileUrl = 'https://raw.githubusercontent.com/Ne-Lexa/php-zip/master/README.md';
|
||||
$fp = @fopen($fileUrl, 'rb', false, stream_context_create([
|
||||
'http' => [
|
||||
'timeout' => 3,
|
||||
]
|
||||
]));
|
||||
/** @noinspection PhpUsageOfSilenceOperatorInspection */
|
||||
$fp = @fopen(
|
||||
$fileUrl,
|
||||
'rb',
|
||||
false,
|
||||
stream_context_create(
|
||||
[
|
||||
'http' => [
|
||||
'timeout' => 3,
|
||||
],
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
if ($fp === false) {
|
||||
self::markTestSkipped(sprintf(
|
||||
"Could not fetch remote file: %s",
|
||||
$fileUrl
|
||||
));
|
||||
static::markTestSkipped(
|
||||
sprintf(
|
||||
'Could not fetch remote file: %s',
|
||||
$fileUrl
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -47,9 +56,8 @@ class ZipRemoteFileTest extends ZipTestCase
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->openFile($outputZip);
|
||||
$files = $zipFile->getListFiles();
|
||||
self::assertCount(1, $files);
|
||||
self::assertSame($fileName, $files[0]);
|
||||
static::assertCount(1, $files);
|
||||
static::assertSame($fileName, $files[0]);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,11 +3,15 @@
|
||||
namespace PhpZip;
|
||||
|
||||
/**
|
||||
* Class ZipSlipVulnerabilityTest
|
||||
* Class ZipSlipVulnerabilityTest.
|
||||
*
|
||||
* @package PhpZip
|
||||
* @see https://github.com/Ne-Lexa/php-zip/issues/39 Issue#31
|
||||
* @see https://snyk.io/research/zip-slip-vulnerability Zip Slip Vulnerability
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @small
|
||||
* @covers
|
||||
*/
|
||||
class ZipSlipVulnerabilityTest extends ZipTestCase
|
||||
{
|
||||
@@ -19,7 +23,7 @@ class ZipSlipVulnerabilityTest extends ZipTestCase
|
||||
$localFile = '../dir/./../../file.txt';
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->addFromString($localFile, 'contents');
|
||||
self::assertContains($localFile, $zipFile->getListFiles());
|
||||
static::assertContains($localFile, $zipFile->getListFiles());
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
@@ -28,7 +32,7 @@ class ZipSlipVulnerabilityTest extends ZipTestCase
|
||||
*/
|
||||
public function testUnpack()
|
||||
{
|
||||
$this->assertTrue(mkdir($this->outputDirname, 0755, true));
|
||||
static::assertTrue(mkdir($this->outputDirname, 0755, true));
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->addFromString('../dir/./../../file.txt', 'contents');
|
||||
@@ -36,6 +40,6 @@ class ZipSlipVulnerabilityTest extends ZipTestCase
|
||||
$zipFile->close();
|
||||
|
||||
$expectedExtractedFile = $this->outputDirname . '/dir/file.txt';
|
||||
self::assertTrue(is_file($expectedExtractedFile));
|
||||
static::assertTrue(is_file($expectedExtractedFile));
|
||||
}
|
||||
}
|
||||
|
@@ -2,32 +2,29 @@
|
||||
|
||||
namespace PhpZip;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PhpZip\Model\EndOfCentralDirectory;
|
||||
use PhpZip\Util\FilesUtil;
|
||||
|
||||
/**
|
||||
* PHPUnit test case and helper methods.
|
||||
*/
|
||||
class ZipTestCase extends \PHPUnit_Framework_TestCase
|
||||
abstract class ZipTestCase extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
protected $outputFilename;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
||||
/** @var string */
|
||||
protected $outputDirname;
|
||||
|
||||
/**
|
||||
* Before test
|
||||
* Before test.
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$id = uniqid('phpzip', true);
|
||||
$tempDir = sys_get_temp_dir() . '/phpunit-phpzip';
|
||||
|
||||
if (!is_dir($tempDir) && !mkdir($tempDir, 0755, true) && !is_dir($tempDir)) {
|
||||
throw new \RuntimeException('Dir ' . $tempDir . " can't created");
|
||||
}
|
||||
@@ -36,7 +33,7 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* After test
|
||||
* After test.
|
||||
*/
|
||||
protected function tearDown()
|
||||
{
|
||||
@@ -45,6 +42,7 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
|
||||
if ($this->outputFilename !== null && file_exists($this->outputFilename)) {
|
||||
unlink($this->outputFilename);
|
||||
}
|
||||
|
||||
if ($this->outputDirname !== null && is_dir($this->outputDirname)) {
|
||||
FilesUtil::removeDir($this->outputDirname);
|
||||
}
|
||||
@@ -53,25 +51,29 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
|
||||
/**
|
||||
* Assert correct zip archive.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $filename
|
||||
* @param string|null $password
|
||||
*/
|
||||
public static function assertCorrectZipArchive($filename, $password = null)
|
||||
{
|
||||
if (self::existsProgram('unzip')) {
|
||||
$command = 'unzip';
|
||||
|
||||
if ($password !== null) {
|
||||
$command .= ' -P ' . escapeshellarg($password);
|
||||
}
|
||||
$command .= ' -t ' . escapeshellarg($filename);
|
||||
exec($command, $output, $returnCode);
|
||||
|
||||
$output = implode(PHP_EOL, $output);
|
||||
$output = implode(\PHP_EOL, $output);
|
||||
|
||||
if ($password !== null && $returnCode === 81) {
|
||||
if (self::existsProgram('7z')) {
|
||||
// WinZip 99-character limit
|
||||
// @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
|
||||
/**
|
||||
* WinZip 99-character limit.
|
||||
*
|
||||
* @see https://sourceforge.net/p/p7zip/discussion/383044/thread/c859a2f0/
|
||||
*/
|
||||
$password = substr($password, 0, 99);
|
||||
|
||||
$command = '7z t -p' . escapeshellarg($password) . ' ' . escapeshellarg($filename);
|
||||
@@ -79,31 +81,34 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
|
||||
/**
|
||||
* @var array $output
|
||||
*/
|
||||
$output = implode(PHP_EOL, $output);
|
||||
$output = implode(\PHP_EOL, $output);
|
||||
|
||||
self::assertEquals($returnCode, 0);
|
||||
self::assertNotContains(' Errors', $output);
|
||||
self::assertContains(' Ok', $output);
|
||||
static::assertSame($returnCode, 0);
|
||||
static::assertNotContains(' Errors', $output);
|
||||
static::assertContains(' Ok', $output);
|
||||
} else {
|
||||
fwrite(STDERR, 'Program unzip cannot support this function.' . PHP_EOL);
|
||||
fwrite(STDERR, 'Please install 7z. For Ubuntu-like: sudo apt-get install p7zip-full' . PHP_EOL);
|
||||
fwrite(\STDERR, 'Program unzip cannot support this function.' . \PHP_EOL);
|
||||
fwrite(\STDERR, 'Please install 7z. For Ubuntu-like: sudo apt-get install p7zip-full' . \PHP_EOL);
|
||||
}
|
||||
} else {
|
||||
self::assertEquals($returnCode, 0, $output);
|
||||
self::assertNotContains('incorrect password', $output);
|
||||
self::assertContains(' OK', $output);
|
||||
self::assertContains('No errors', $output);
|
||||
static::assertSame($returnCode, 0, $output);
|
||||
static::assertNotContains('incorrect password', $output);
|
||||
static::assertContains(' OK', $output);
|
||||
static::assertContains('No errors', $output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $program
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function existsProgram($program){
|
||||
if (DIRECTORY_SEPARATOR !== '\\') {
|
||||
private static function existsProgram($program)
|
||||
{
|
||||
if (\DIRECTORY_SEPARATOR !== '\\') {
|
||||
exec('which ' . escapeshellarg($program), $output, $returnCode);
|
||||
|
||||
return $returnCode === 0;
|
||||
}
|
||||
// false for Windows
|
||||
@@ -120,30 +125,34 @@ class ZipTestCase extends \PHPUnit_Framework_TestCase
|
||||
if (self::existsProgram('zipinfo')) {
|
||||
exec('zipinfo ' . escapeshellarg($filename), $output, $returnCode);
|
||||
|
||||
$output = implode(PHP_EOL, $output);
|
||||
$output = implode(\PHP_EOL, $output);
|
||||
|
||||
self::assertContains('Empty zipfile', $output);
|
||||
static::assertContains('Empty zipfile', $output);
|
||||
}
|
||||
$actualEmptyZipData = pack('VVVVVv', EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG, 0, 0, 0, 0, 0);
|
||||
self::assertStringEqualsFile($filename, $actualEmptyZipData);
|
||||
static::assertStringEqualsFile($filename, $actualEmptyZipData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @param bool $showErrors
|
||||
* @return bool|null If null - can not install zipalign
|
||||
* @param bool $showErrors
|
||||
*
|
||||
* @return bool|null If null returned, then the zipalign program is not installed
|
||||
*/
|
||||
public static function assertVerifyZipAlign($filename, $showErrors = false)
|
||||
{
|
||||
if (self::existsProgram('zipalign')) {
|
||||
exec('zipalign -c -v 4 ' . escapeshellarg($filename), $output, $returnCode);
|
||||
|
||||
if ($showErrors && $returnCode !== 0) {
|
||||
fwrite(STDERR, implode(PHP_EOL, $output));
|
||||
fwrite(\STDERR, implode(\PHP_EOL, $output));
|
||||
}
|
||||
|
||||
return $returnCode === 0;
|
||||
}
|
||||
|
||||
fwrite(STDERR, 'Can not find program "zipalign" for test' . PHP_EOL);
|
||||
fwrite(\STDERR, "Cannot find the program 'zipalign' for the test" . \PHP_EOL);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user