🍲dfcv🏰dd⋉(● ∸ ●)⋊@% PNG %k25u25%fgd5n! PNG %k25u25%fgd5n!PK V_]q[' 6 zipstream-php/test/Zip64/EndOfCentralDirectoryTest.phpnu [ assertSame(
bin2hex($descriptor),
'504b0606' . // 4 bytes;zip64 end of central dir signature - 0x06064b50
'2f00000000000000' . // 8 bytes; size of zip64 end of central directory record
'3333' . // 2 bytes; version made by
'4444' . // 2 bytes; version needed to extract
'55555555' . // 4 bytes; number of this disk
'66666666' . // 4 bytes; number of the disk with the start of the central directory
'8888888877777777' . // 8 bytes; total number of entries in the central directory on this disk
'aaaaaaaa99999999' . // 8 bytes; total number of entries in the central directory
'ccccccccbbbbbbbb' . // 8 bytes; size of the central directory
'eeeeeeeedddddddd' . // 8 bytes; offset of start of central directory with respect to the starting disk number
bin2hex('foo')
);
}
}
PK V_]ZXH5 5 / zipstream-php/test/Zip64/DataDescriptorTest.phpnu [ assertSame(
bin2hex($descriptor),
'504b0708' . // 4 bytes; Optional data descriptor signature = 0x08074b50
'11111111' . // 4 bytes; CRC-32 of uncompressed data
'6666666677777777' . // 8 bytes; Compressed size
'8888888899999999' // 8 bytes; Uncompressed size
);
}
}
PK V_]gN > zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.phpnu [ assertSame(
bin2hex($extraField),
'0100' . // 2 bytes; Tag for this "extra" block type
'1c00' . // 2 bytes; Size of this "extra" block
'6666666677777777' . // 8 bytes; Original uncompressed file size
'8888888899999999' . // 8 bytes; Size of compressed data
'1111111122222222' . // 8 bytes; Offset of local header record
'33333333' // 4 bytes; Number of the disk on which this file starts
);
}
public function testSerializesEmptyCorrectly(): void
{
$extraField = ExtendedInformationExtraField::generate();
$this->assertSame(
bin2hex($extraField),
'0100' . // 2 bytes; Tag for this "extra" block type
'0000' // 2 bytes; Size of this "extra" block
);
}
}
PK V_] = zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.phpnu [ assertSame(
bin2hex($descriptor),
'504b0607' . // 4 bytes; zip64 end of central dir locator signature - 0x07064b50
'11111111' . // 4 bytes; number of the disk with the start of the zip64 end of central directory
'3333333322222222' . // 28 bytes; relative offset of the zip64 end of central directory record
'44444444' // 4 bytes;total number of disks
);
}
}
PK V_]R ; zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.phpnu [ assertSame(
bin2hex((string) $extraField),
'5356' . // 2 bytes; Tag for this "extra" block type
'0000' // 2 bytes; TODO: Document
);
}
}
PK V_]Z 5
5
zipstream-php/test/Util.phpnu [ cmdExists('hexdump')) {
return '';
}
$output = [];
if (!exec("hexdump -C \"$path\" | head -n 50", $output)) {
return '';
}
return "\nHexdump:\n" . implode("\n", $output);
}
protected function validateAndExtractZip(string $zipPath): string
{
$tmpDir = $this->getTmpDir();
$zipArchive = new ZipArchive();
$result = $zipArchive->open($zipPath);
if ($result !== true) {
$codeName = $this->zipArchiveOpenErrorCodeName($result);
$debugInformation = $this->dumpZipContents($zipPath);
$this->fail("Failed to open {$zipPath}. Code: $result ($codeName)$debugInformation");
return $tmpDir;
}
$this->assertSame(0, $zipArchive->status);
$this->assertSame(0, $zipArchive->statusSys);
$zipArchive->extractTo($tmpDir);
$zipArchive->close();
return $tmpDir;
}
protected function zipArchiveOpenErrorCodeName(int $code): string
{
switch ($code) {
case ZipArchive::ER_EXISTS: return 'ER_EXISTS';
case ZipArchive::ER_INCONS: return 'ER_INCONS';
case ZipArchive::ER_INVAL: return 'ER_INVAL';
case ZipArchive::ER_MEMORY: return 'ER_MEMORY';
case ZipArchive::ER_NOENT: return 'ER_NOENT';
case ZipArchive::ER_NOZIP: return 'ER_NOZIP';
case ZipArchive::ER_OPEN: return 'ER_OPEN';
case ZipArchive::ER_READ: return 'ER_READ';
case ZipArchive::ER_SEEK: return 'ER_SEEK';
default: return 'unknown';
}
}
protected function getTmpDir(): string
{
$tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
unlink($tmp);
mkdir($tmp) or $this->fail('Failed to make directory');
return $tmp;
}
/**
* @return string[]
*/
protected function getRecursiveFileList(string $path, bool $includeDirectories = false): array
{
$data = [];
$path = (string) realpath($path);
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
$pathLen = strlen($path);
foreach ($files as $file) {
$filePath = $file->getRealPath();
if (is_dir($filePath) && !$includeDirectories) {
continue;
}
$data[] = substr($filePath, $pathLen + 1);
}
sort($data);
return $data;
}
}
PK V_]w,j j 0 zipstream-php/test/EndOfCentralDirectoryTest.phpnu [ assertSame(
bin2hex(EndOfCentralDirectory::generate(
numberOfThisDisk: 0x00,
numberOfTheDiskWithCentralDirectoryStart: 0x00,
numberOfCentralDirectoryEntriesOnThisDisk: 0x10,
numberOfCentralDirectoryEntries: 0x10,
sizeOfCentralDirectory: 0x22,
centralDirectoryStartOffsetOnDisk: 0x33,
zipFileComment: 'foo',
)),
'504b0506' . // 4 bytes; end of central dir signature 0x06054b50
'0000' . // 2 bytes; number of this disk
'0000' . // 2 bytes; number of the disk with the start of the central directory
'1000' . // 2 bytes; total number of entries in the central directory on this disk
'1000' . // 2 bytes; total number of entries in the central directory
'22000000' . // 4 bytes; size of the central directory
'33000000' . // 4 bytes; offset of start of central directory with respect to the starting disk number
'0300' . // 2 bytes; .ZIP file comment length
bin2hex('foo')
);
}
}
PK V_]9 * zipstream-php/test/LocalFileHeaderTest.phpnu [ assertSame(
bin2hex((string) $header),
'504b0304' . // 4 bytes; Local file header signature
'2d00' . // 2 bytes; Version needed to extract (minimum)
'2222' . // 2 bytes; General purpose bit flag
'0800' . // 2 bytes; Compression method; e.g. none = 0, DEFLATE = 8
'2008' . // 2 bytes; File last modification time
'2154' . // 2 bytes; File last modification date
'11111111' . // 4 bytes; CRC-32 of uncompressed data
'77777777' . // 4 bytes; Compressed size (or 0xffffffff for ZIP64)
'99999999' . // 4 bytes; Uncompressed size (or 0xffffffff for ZIP64)
'0800' . // 2 bytes; File name length (n)
'0c00' . // 2 bytes; Extra field length (m)
'746573742e706e67' . // n bytes; File name
'736f6d6520636f6e74656e74' // m bytes; Extra field
);
}
}
PK V_]S ) zipstream-php/test/EndlessCycleStream.phpnu [ detach();
}
/**
* @return null
*/
public function detach()
{
return;
}
public function getSize(): ?int
{
return null;
}
public function tell(): int
{
return $this->offset;
}
public function eof(): bool
{
return false;
}
public function isSeekable(): bool
{
return true;
}
public function seek(int $offset, int $whence = SEEK_SET): void
{
switch ($whence) {
case SEEK_SET:
$this->offset = $offset;
break;
case SEEK_CUR:
$this->offset += $offset;
break;
case SEEK_END:
throw new RuntimeException('Infinite Stream!');
break;
}
}
public function rewind(): void
{
$this->seek(0);
}
public function isWritable(): bool
{
return false;
}
public function write(string $string): int
{
throw new RuntimeException('Not writeable');
}
public function isReadable(): bool
{
return true;
}
public function read(int $length): string
{
$this->offset += $length;
return substr(str_repeat($this->toRepeat, (int) ceil($length / strlen($this->toRepeat))), 0, $length);
}
public function getContents(): string
{
throw new RuntimeException('Infinite Stream!');
}
public function getMetadata(?string $key = null): array|null
{
return $key !== null ? null : [];
}
}
PK V_]Ew ) zipstream-php/test/DataDescriptorTest.phpnu [ assertSame(
bin2hex(DataDescriptor::generate(
crc32UncompressedData: 0x11111111,
compressedSize: 0x77777777,
uncompressedSize: 0x99999999,
)),
'504b0708' . // 4 bytes; Optional data descriptor signature = 0x08074b50
'11111111' . // 4 bytes; CRC-32 of uncompressed data
'77777777' . // 4 bytes; Compressed size
'99999999' // 4 bytes; Uncompressed size
);
}
}
PK V_]q@U zipstream-php/test/TimeTest.phpnu [ assertSame(
Time::dateTimeToDosTime(new DateTimeImmutable('2014-11-17T17:46:08Z')),
1165069764
);
// January 1 1980 - DOS Epoch.
$this->assertSame(
Time::dateTimeToDosTime(new DateTimeImmutable('1980-01-01T00:00:00+00:00')),
2162688
);
// Local timezone different than UTC.
$prevLocalTimezone = date_default_timezone_get();
date_default_timezone_set('Europe/Berlin');
$this->assertSame(
Time::dateTimeToDosTime(new DateTimeImmutable('1980-01-01T00:00:00+00:00')),
2162688
);
date_default_timezone_set($prevLocalTimezone);
}
public function testTooEarlyDateToDosTime(): void
{
$this->expectException(DosTimeOverflowException::class);
// January 1 1980 is the minimum DOS Epoch.
Time::dateTimeToDosTime(new DateTimeImmutable('1970-01-01T00:00:00+00:00'));
}
}
PK V_]q
Pq q zipstream-php/test/bootstrap.phpnu [ assertSame(
bin2hex($header),
'504b0102' . // 4 bytes; central file header signature
'0306' . // 2 bytes; version made by
'2d00' . // 2 bytes; version needed to extract
'2222' . // 2 bytes; general purpose bit flag
'0800' . // 2 bytes; compression method
'2008' . // 2 bytes; last mod file time
'2154' . // 2 bytes; last mod file date
'11111111' . // 4 bytes; crc-32
'77777777' . // 4 bytes; compressed size
'99999999' . // 4 bytes; uncompressed size
'0800' . // 2 bytes; file name length (n)
'0c00' . // 2 bytes; extra field length (m)
'0c00' . // 2 bytes; file comment length (o)
'0000' . // 2 bytes; disk number start
'0000' . // 2 bytes; internal file attributes
'20000000' . // 4 bytes; external file attributes
'34120000' . // 4 bytes; relative offset of local header
'746573742e706e67' . // n bytes; file name
'736f6d6520636f6e74656e74' . // m bytes; extra field
'736f6d6520636f6d6d656e74' // o bytes; file comment
);
}
}
PK V_]>ӿs s - zipstream-php/test/FaultInjectionResource.phpnu [ context);
if (!isset($options[self::NAME]['injectFaults'])) {
return false;
}
$this->mode = $mode;
$this->injectFaults = $options[self::NAME]['injectFaults'];
if ($this->shouldFail(__FUNCTION__)) {
return false;
}
return true;
}
public function stream_write(string $data)
{
if ($this->shouldFail(__FUNCTION__)) {
return false;
}
return true;
}
public function stream_eof()
{
return true;
}
public function stream_seek(int $offset, int $whence): bool
{
if ($this->shouldFail(__FUNCTION__)) {
return false;
}
return true;
}
public function stream_tell(): int
{
if ($this->shouldFail(__FUNCTION__)) {
return false;
}
return 0;
}
public static function register(): void
{
if (!in_array(self::NAME, stream_get_wrappers(), true)) {
stream_wrapper_register(self::NAME, __CLASS__);
}
}
public function stream_stat(): array
{
static $modeMap = [
'r' => 33060,
'rb' => 33060,
'r+' => 33206,
'w' => 33188,
'wb' => 33188,
];
return [
'dev' => 0,
'ino' => 0,
'mode' => $modeMap[$this->mode],
'nlink' => 0,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => 0,
'atime' => 0,
'mtime' => 0,
'ctime' => 0,
'blksize' => 0,
'blocks' => 0,
];
}
public function url_stat(string $path, int $flags): array
{
return [
'dev' => 0,
'ino' => 0,
'mode' => 0,
'nlink' => 0,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => 0,
'atime' => 0,
'mtime' => 0,
'ctime' => 0,
'blksize' => 0,
'blocks' => 0,
];
}
private static function createStreamContext(array $injectFaults)
{
return stream_context_create([
self::NAME => ['injectFaults' => $injectFaults],
]);
}
private function shouldFail(string $function): bool
{
return in_array($function, $this->injectFaults, true);
}
}
PK V_]= ! zipstream-php/test/Assertions.phpnu [ fail("File {$filePath} must contain {$needle}");
}
protected function assertFileDoesNotContain(string $filePath, string $needle): void
{
$last = '';
$handle = fopen($filePath, 'r');
while (!feof($handle)) {
$line = fgets($handle, 1024);
if (str_contains($last . $line, $needle)) {
fclose($handle);
$this->fail("File {$filePath} must not contain {$needle}");
}
$last = $line;
}
fclose($handle);
}
}
PK V_]tJ J zipstream-php/test/Tempfile.phpnu [ getTmpFileStream();
$this->tempfile = $tempfile;
$this->tempfileStream = $tempfileStream;
}
protected function tearDown(): void
{
unlink($this->tempfile);
if (is_resource($this->tempfileStream)) {
fclose($this->tempfileStream);
}
$this->tempfile = null;
$this->tempfileStream = null;
}
protected function getTmpFileStream(): array
{
$tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
$stream = fopen($tmp, 'wb+');
return [$tmp, $stream];
}
}
PK V_]kޓ ޓ $ zipstream-php/test/ZipStreamTest.phpnu [ tempfileStream,
sendHttpHeaders: false,
);
$zip->addFile('sample.txt', 'Sample String Data');
$zip->addFile('test/sample.txt', 'More Simple Sample Data');
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
$this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
$this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
}
public function testAddFileUtf8NameComment(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$name = 'árvíztűrő tükörfúrógép.txt';
$content = 'Sample String Data';
$comment =
'Filename has every special characters ' .
'from Hungarian language in lowercase. ' .
'In uppercase: ÁÍŰŐÜÖÚÓÉ';
$zip->addFile(fileName: $name, data: $content, comment: $comment);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame([$name], $files);
$this->assertStringEqualsFile($tmpDir . '/' . $name, $content);
$zipArchive = new ZipArchive();
$zipArchive->open($this->tempfile);
$this->assertSame($comment, $zipArchive->getCommentName($name));
}
public function testAddFileUtf8NameNonUtfComment(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$name = 'á.txt';
$content = 'any';
$comment = mb_convert_encoding('á', 'ISO-8859-2', 'UTF-8');
// @see https://libzip.org/documentation/zip_file_get_comment.html
//
// mb_convert_encoding hasn't CP437.
// nearly CP850 (DOS-Latin-1)
$guessComment = mb_convert_encoding($comment, 'UTF-8', 'CP850');
$zip->addFile(fileName: $name, data: $content, comment: $comment);
$zip->finish();
$zipArch = new ZipArchive();
$zipArch->open($this->tempfile);
$this->assertSame($guessComment, $zipArch->getCommentName($name));
$this->assertSame($comment, $zipArch->getCommentName($name, ZipArchive::FL_ENC_RAW));
}
public function testAddFileWithStorageMethod(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$zip->addFile(fileName: 'sample.txt', data: 'Sample String Data', compressionMethod: CompressionMethod::STORE);
$zip->addFile(fileName: 'test/sample.txt', data: 'More Simple Sample Data');
$zip->finish();
$zipArchive = new ZipArchive();
$zipArchive->open($this->tempfile);
$sample1 = $zipArchive->statName('sample.txt');
$sample12 = $zipArchive->statName('test/sample.txt');
$this->assertSame($sample1['comp_method'], CompressionMethod::STORE->value);
$this->assertSame($sample12['comp_method'], CompressionMethod::DEFLATE->value);
$zipArchive->close();
}
public function testAddFileFromPath(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
[$tmpExample, $streamExample] = $this->getTmpFileStream();
fwrite($streamExample, 'Sample String Data');
fclose($streamExample);
$zip->addFileFromPath(fileName: 'sample.txt', path: $tmpExample);
[$tmpExample, $streamExample] = $this->getTmpFileStream();
fwrite($streamExample, 'More Simple Sample Data');
fclose($streamExample);
$zip->addFileFromPath(fileName: 'test/sample.txt', path: $tmpExample);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
$this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
$this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
unlink($tmpExample);
}
public function testAddFileFromPathFileNotFoundException(): void
{
$this->expectException(FileNotFoundException::class);
// Get ZipStream Object
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
// Trigger error by adding a file which doesn't exist
$zip->addFileFromPath(fileName: 'foobar.php', path: '/foo/bar/foobar.php');
}
public function testAddFileFromPathFileNotReadableException(): void
{
$this->expectException(FileNotReadableException::class);
// create new virtual filesystem
$root = vfsStream::setup('vfs');
// create a virtual file with no permissions
$file = vfsStream::newFile('foo.txt', 0)->at($root)->setContent('bar');
// Get ZipStream Object
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$zip->addFileFromPath('foo.txt', $file->url());
}
public function testAddFileFromPathWithStorageMethod(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
[$tmpExample, $streamExample] = $this->getTmpFileStream();
fwrite($streamExample, 'Sample String Data');
fclose($streamExample);
$zip->addFileFromPath(fileName: 'sample.txt', path: $tmpExample, compressionMethod: CompressionMethod::STORE);
[$tmpExample, $streamExample] = $this->getTmpFileStream();
fwrite($streamExample, 'More Simple Sample Data');
fclose($streamExample);
$zip->addFileFromPath('test/sample.txt', $tmpExample);
$zip->finish();
$zipArchive = new ZipArchive();
$zipArchive->open($this->tempfile);
$sample1 = $zipArchive->statName('sample.txt');
$this->assertSame(CompressionMethod::STORE->value, $sample1['comp_method']);
$sample2 = $zipArchive->statName('test/sample.txt');
$this->assertSame(CompressionMethod::DEFLATE->value, $sample2['comp_method']);
$zipArchive->close();
}
public function testAddLargeFileFromPath(): void
{
foreach ([CompressionMethod::DEFLATE, CompressionMethod::STORE] as $compressionMethod) {
foreach ([false, true] as $zeroHeader) {
foreach ([false, true] as $zip64) {
if ($zeroHeader && $compressionMethod === CompressionMethod::DEFLATE) {
continue;
}
$this->addLargeFileFileFromPath(
compressionMethod: $compressionMethod,
zeroHeader: $zeroHeader,
zip64: $zip64
);
}
}
}
}
public function testAddFileFromStream(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
// In this test we can't use temporary stream to feed data
// because zlib.deflate filter gives empty string before PHP 7
// it works fine with file stream
$streamExample = fopen(__FILE__, 'rb');
$zip->addFileFromStream('sample.txt', $streamExample);
fclose($streamExample);
$streamExample2 = fopen('php://temp', 'wb+');
fwrite($streamExample2, 'More Simple Sample Data');
rewind($streamExample2); // move the pointer back to the beginning of file.
$zip->addFileFromStream('test/sample.txt', $streamExample2); //, $fileOptions);
fclose($streamExample2);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
$this->assertStringEqualsFile(__FILE__, file_get_contents($tmpDir . '/sample.txt'));
$this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
}
public function testAddFileFromStreamUnreadableInput(): void
{
$this->expectException(StreamNotReadableException::class);
[$tmpInput] = $this->getTmpFileStream();
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$streamUnreadable = fopen($tmpInput, 'w');
$zip->addFileFromStream('sample.json', $streamUnreadable);
}
public function testAddFileFromStreamBrokenOutputWrite(): void
{
$this->expectException(ResourceActionException::class);
$outputStream = FaultInjectionResource::getResource(['stream_write']);
$zip = new ZipStream(
outputStream: $outputStream,
sendHttpHeaders: false,
);
$zip->addFile('sample.txt', 'foobar');
}
public function testAddFileFromStreamBrokenInputRewind(): void
{
$this->expectException(ResourceActionException::class);
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
defaultEnableZeroHeader: false,
);
$fileStream = FaultInjectionResource::getResource(['stream_seek']);
$zip->addFileFromStream('sample.txt', $fileStream, maxSize: 0);
}
public function testAddFileFromStreamUnseekableInputWithoutZeroHeader(): void
{
$this->expectException(StreamNotSeekableException::class);
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
defaultEnableZeroHeader: false,
);
if (file_exists('/dev/null')) {
$streamUnseekable = fopen('/dev/null', 'w+');
} elseif (file_exists('NUL')) {
$streamUnseekable = fopen('NUL', 'w+');
} else {
$this->markTestSkipped('Needs file /dev/null');
}
$zip->addFileFromStream('sample.txt', $streamUnseekable, maxSize: 2);
}
public function testAddFileFromStreamUnseekableInputWithZeroHeader(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
defaultEnableZeroHeader: true,
defaultCompressionMethod: CompressionMethod::STORE,
);
$streamUnseekable = StreamWrapper::getResource(new class ('test') extends EndlessCycleStream {
public function isSeekable(): bool
{
return false;
}
public function seek(int $offset, int $whence = SEEK_SET): void
{
throw new RuntimeException('Not seekable');
}
});
$zip->addFileFromStream('sample.txt', $streamUnseekable, maxSize: 7);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.txt'], $files);
$this->assertSame(filesize($tmpDir . '/sample.txt'), 7);
}
public function testAddFileFromStreamWithStorageMethod(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$streamExample = fopen('php://temp', 'wb+');
fwrite($streamExample, 'Sample String Data');
rewind($streamExample); // move the pointer back to the beginning of file.
$zip->addFileFromStream('sample.txt', $streamExample, compressionMethod: CompressionMethod::STORE);
fclose($streamExample);
$streamExample2 = fopen('php://temp', 'bw+');
fwrite($streamExample2, 'More Simple Sample Data');
rewind($streamExample2); // move the pointer back to the beginning of file.
$zip->addFileFromStream('test/sample.txt', $streamExample2, compressionMethod: CompressionMethod::DEFLATE);
fclose($streamExample2);
$zip->finish();
$zipArchive = new ZipArchive();
$zipArchive->open($this->tempfile);
$sample1 = $zipArchive->statName('sample.txt');
$this->assertSame(CompressionMethod::STORE->value, $sample1['comp_method']);
$sample2 = $zipArchive->statName('test/sample.txt');
$this->assertSame(CompressionMethod::DEFLATE->value, $sample2['comp_method']);
$zipArchive->close();
}
public function testAddFileFromPsr7Stream(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$body = 'Sample String Data';
$response = new Response(200, [], $body);
$zip->addFileFromPsr7Stream('sample.json', $response->getBody());
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.json'], $files);
$this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
}
/**
* @group slow
*/
public function testAddLargeFileFromPsr7Stream(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: true,
);
$zip->addFileFromPsr7Stream(
fileName: 'sample.json',
stream: new EndlessCycleStream('0'),
maxSize: 0x100000000,
compressionMethod: CompressionMethod::STORE,
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.json'], $files);
$this->assertFileIsReadable($tmpDir . '/sample.json');
$this->assertStringStartsWith('000000', file_get_contents(filename: $tmpDir . '/sample.json', length: 20));
}
public function testContinueFinishedZip(): void
{
$this->expectException(RuntimeException::class);
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$zip->finish();
$zip->addFile('sample.txt', '1234');
}
/**
* @group slow
*/
public function testManyFilesWithoutZip64(): void
{
$this->expectException(OverflowException::class);
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: false,
);
for ($i = 0; $i <= 0xFFFF; $i++) {
$zip->addFile('sample' . $i, '');
}
$zip->finish();
}
/**
* @group slow
*/
public function testManyFilesWithZip64(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: true,
);
for ($i = 0; $i <= 0xFFFF; $i++) {
$zip->addFile('sample' . $i, '');
}
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(count($files), 0x10000);
}
/**
* @group slow
*/
public function testLongZipWithout64(): void
{
$this->expectException(OverflowException::class);
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: false,
defaultCompressionMethod: CompressionMethod::STORE,
);
for ($i = 0; $i < 4; $i++) {
$zip->addFileFromPsr7Stream(
fileName: 'sample' . $i,
stream: new EndlessCycleStream('0'),
maxSize: 0xFFFFFFFF,
compressionMethod: CompressionMethod::STORE,
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
);
}
}
/**
* @group slow
*/
public function testLongZipWith64(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: true,
defaultCompressionMethod: CompressionMethod::STORE,
);
for ($i = 0; $i < 4; $i++) {
$zip->addFileFromPsr7Stream(
fileName: 'sample' . $i,
stream: new EndlessCycleStream('0'),
maxSize: 0x5FFFFFFF,
compressionMethod: CompressionMethod::STORE,
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
);
}
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample0', 'sample1', 'sample2', 'sample3'], $files);
}
/**
* @group slow
*/
public function testAddLargeFileWithoutZip64WithZeroHeader(): void
{
$this->expectException(OverflowException::class);
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: false,
defaultEnableZeroHeader: true,
);
$zip->addFileFromPsr7Stream(
fileName: 'sample.json',
stream: new EndlessCycleStream('0'),
maxSize: 0x100000000,
compressionMethod: CompressionMethod::STORE,
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
);
}
/**
* @group slow
*/
public function testAddsZip64HeaderWhenNeeded(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: true,
defaultEnableZeroHeader: false,
);
$zip->addFileFromPsr7Stream(
fileName: 'sample.json',
stream: new EndlessCycleStream('0'),
maxSize: 0x100000000,
compressionMethod: CompressionMethod::STORE,
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.json'], $files);
$this->assertFileContains($this->tempfile, PackField::pack(
new PackField(format: 'V', value: 0x06064b50)
));
}
/**
* @group slow
*/
public function testDoesNotAddZip64HeaderWhenNotNeeded(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: true,
defaultEnableZeroHeader: false,
);
$zip->addFileFromPsr7Stream(
fileName: 'sample.json',
stream: new EndlessCycleStream('0'),
maxSize: 0x10,
compressionMethod: CompressionMethod::STORE,
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.json'], $files);
$this->assertFileDoesNotContain($this->tempfile, PackField::pack(
new PackField(format: 'V', value: 0x06064b50)
));
}
/**
* @group slow
*/
public function testAddLargeFileWithoutZip64WithoutZeroHeader(): void
{
$this->expectException(OverflowException::class);
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
enableZip64: false,
defaultEnableZeroHeader: false,
);
$zip->addFileFromPsr7Stream(
fileName: 'sample.json',
stream: new EndlessCycleStream('0'),
maxSize: 0x100000000,
compressionMethod: CompressionMethod::STORE,
lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
);
}
public function testAddFileFromPsr7StreamWithOutputToPsr7Stream(): void
{
$psr7OutputStream = new ResourceStream($this->tempfileStream);
$zip = new ZipStream(
outputStream: $psr7OutputStream,
sendHttpHeaders: false,
);
$body = 'Sample String Data';
$response = new Response(200, [], $body);
$zip->addFileFromPsr7Stream(
fileName: 'sample.json',
stream: $response->getBody(),
compressionMethod: CompressionMethod::STORE,
);
$zip->finish();
$psr7OutputStream->close();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.json'], $files);
$this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
}
public function testAddFileFromPsr7StreamWithFileSizeSet(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$body = 'Sample String Data';
$fileSize = strlen($body);
// Add fake padding
$fakePadding = "\0\0\0\0\0\0";
$response = new Response(200, [], $body . $fakePadding);
$zip->addFileFromPsr7Stream(
fileName: 'sample.json',
stream: $response->getBody(),
compressionMethod: CompressionMethod::STORE,
maxSize: $fileSize
);
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.json'], $files);
$this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
}
public function testCreateArchiveHeaders(): void
{
$headers = [];
$httpHeaderCallback = function (string $header) use (&$headers) {
$headers[] = $header;
};
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: true,
outputName: 'example.zip',
httpHeaderCallback: $httpHeaderCallback,
);
$zip->addFile(
fileName: 'sample.json',
data: 'foo',
);
$zip->finish();
$this->assertContains('Content-Type: application/x-zip', $headers);
$this->assertContains("Content-Disposition: attachment; filename*=UTF-8''example.zip", $headers);
$this->assertContains('Pragma: public', $headers);
$this->assertContains('Cache-Control: public, must-revalidate', $headers);
$this->assertContains('Content-Transfer-Encoding: binary', $headers);
}
public function testCreateArchiveWithFlushOptionSet(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
flushOutput: true,
sendHttpHeaders: false,
);
$zip->addFile('sample.txt', 'Sample String Data');
$zip->addFile('test/sample.txt', 'More Simple Sample Data');
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.txt', 'test' . DIRECTORY_SEPARATOR . 'sample.txt'], $files);
$this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
$this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
}
public function testCreateArchiveWithOutputBufferingOffAndFlushOptionSet(): void
{
// WORKAROUND (1/2): remove phpunit's output buffer in order to run test without any buffering
ob_end_flush();
$this->assertSame(0, ob_get_level());
$zip = new ZipStream(
outputStream: $this->tempfileStream,
flushOutput: true,
sendHttpHeaders: false,
);
$zip->addFile('sample.txt', 'Sample String Data');
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$this->assertStringEqualsFile($tmpDir . '/sample.txt', 'Sample String Data');
// WORKAROUND (2/2): add back output buffering so that PHPUnit doesn't complain that it is missing
ob_start();
}
public function testAddEmptyDirectory(): void
{
$zip = new ZipStream(
outputStream: $this->tempfileStream,
sendHttpHeaders: false,
);
$zip->addDirectory('foo');
$zip->finish();
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir, includeDirectories: true);
$this->assertContains('foo', $files);
$this->assertFileExists($tmpDir . DIRECTORY_SEPARATOR . 'foo');
$this->assertDirectoryExists($tmpDir . DIRECTORY_SEPARATOR . 'foo');
}
public function testAddFileSimulate(): void
{
$create = function (OperationMode $operationMode): int {
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: $operationMode,
defaultEnableZeroHeader: true,
outputStream: $this->tempfileStream,
);
$zip->addFile('sample.txt', 'Sample String Data');
$zip->addFile('test/sample.txt', 'More Simple Sample Data');
return $zip->finish();
};
$sizeExpected = $create(OperationMode::NORMAL);
$sizeActual = $create(OperationMode::SIMULATE_LAX);
$this->assertEquals($sizeExpected, $sizeActual);
}
public function testAddFileSimulateWithMaxSize(): void
{
$create = function (OperationMode $operationMode): int {
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: $operationMode,
defaultCompressionMethod: CompressionMethod::STORE,
defaultEnableZeroHeader: true,
outputStream: $this->tempfileStream,
);
$zip->addFile('sample.txt', 'Sample String Data', maxSize: 0);
return $zip->finish();
};
$sizeExpected = $create(OperationMode::NORMAL);
$sizeActual = $create(OperationMode::SIMULATE_LAX);
$this->assertEquals($sizeExpected, $sizeActual);
}
public function testAddFileSimulateWithFstat(): void
{
$create = function (OperationMode $operationMode): int {
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: $operationMode,
defaultCompressionMethod: CompressionMethod::STORE,
defaultEnableZeroHeader: true,
outputStream: $this->tempfileStream,
);
$zip->addFile('sample.txt', 'Sample String Data');
$zip->addFile('test/sample.txt', 'More Simple Sample Data');
return $zip->finish();
};
$sizeExpected = $create(OperationMode::NORMAL);
$sizeActual = $create(OperationMode::SIMULATE_LAX);
$this->assertEquals($sizeExpected, $sizeActual);
}
public function testAddFileSimulateWithExactSizeZero(): void
{
$create = function (OperationMode $operationMode): int {
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: $operationMode,
defaultCompressionMethod: CompressionMethod::STORE,
defaultEnableZeroHeader: true,
outputStream: $this->tempfileStream,
);
$zip->addFile('sample.txt', 'Sample String Data', exactSize: 18);
return $zip->finish();
};
$sizeExpected = $create(OperationMode::NORMAL);
$sizeActual = $create(OperationMode::SIMULATE_LAX);
$this->assertEquals($sizeExpected, $sizeActual);
}
public function testAddFileSimulateWithExactSizeInitial(): void
{
$create = function (OperationMode $operationMode): int {
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: $operationMode,
defaultCompressionMethod: CompressionMethod::STORE,
defaultEnableZeroHeader: false,
outputStream: $this->tempfileStream,
);
$zip->addFile('sample.txt', 'Sample String Data', exactSize: 18);
return $zip->finish();
};
$sizeExpected = $create(OperationMode::NORMAL);
$sizeActual = $create(OperationMode::SIMULATE_LAX);
$this->assertEquals($sizeExpected, $sizeActual);
}
public function testAddFileSimulateWithZeroSizeInFstat(): void
{
$create = function (OperationMode $operationMode): int {
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: $operationMode,
defaultCompressionMethod: CompressionMethod::STORE,
defaultEnableZeroHeader: false,
outputStream: $this->tempfileStream,
);
$zip->addFileFromPsr7Stream('sample.txt', new class implements StreamInterface {
public $pos = 0;
public function __toString(): string
{
return 'test';
}
public function close(): void {}
public function detach() {}
public function getSize(): ?int
{
return null;
}
public function tell(): int
{
return $this->pos;
}
public function eof(): bool
{
return $this->pos >= 4;
}
public function isSeekable(): bool
{
return true;
}
public function seek(int $offset, int $whence = SEEK_SET): void
{
$this->pos = $offset;
}
public function rewind(): void
{
$this->pos = 0;
}
public function isWritable(): bool
{
return false;
}
public function write(string $string): int
{
return 0;
}
public function isReadable(): bool
{
return true;
}
public function read(int $length): string
{
$data = substr('test', $this->pos, $length);
$this->pos += strlen($data);
return $data;
}
public function getContents(): string
{
return $this->read(4);
}
public function getMetadata(?string $key = null)
{
return $key !== null ? null : [];
}
});
return $zip->finish();
};
$sizeExpected = $create(OperationMode::NORMAL);
$sizeActual = $create(OperationMode::SIMULATE_LAX);
$this->assertEquals($sizeExpected, $sizeActual);
}
public function testAddFileSimulateWithWrongExactSize(): void
{
$this->expectException(FileSizeIncorrectException::class);
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: OperationMode::SIMULATE_LAX,
);
$zip->addFile('sample.txt', 'Sample String Data', exactSize: 1000);
}
public function testAddFileSimulateStrictZero(): void
{
$this->expectException(SimulationFileUnknownException::class);
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: OperationMode::SIMULATE_STRICT,
defaultEnableZeroHeader: true
);
$zip->addFile('sample.txt', 'Sample String Data');
}
public function testAddFileSimulateStrictInitial(): void
{
$this->expectException(SimulationFileUnknownException::class);
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: OperationMode::SIMULATE_STRICT,
defaultEnableZeroHeader: false
);
$zip->addFile('sample.txt', 'Sample String Data');
}
public function testAddFileCallbackStrict(): void
{
$this->expectException(SimulationFileUnknownException::class);
$zip = new ZipStream(
sendHttpHeaders: false,
operationMode: OperationMode::SIMULATE_STRICT,
defaultEnableZeroHeader: false
);
$zip->addFileFromCallback('sample.txt', callback: function () {
return '';
});
}
public function testAddFileCallbackLax(): void
{
$zip = new ZipStream(
operationMode: OperationMode::SIMULATE_LAX,
defaultEnableZeroHeader: false,
sendHttpHeaders: false,
);
$zip->addFileFromCallback('sample.txt', callback: function () {
return 'Sample String Data';
});
$size = $zip->finish();
$this->assertEquals($size, 142);
}
public function testExecuteSimulation(): void
{
$zip = new ZipStream(
operationMode: OperationMode::SIMULATE_STRICT,
defaultCompressionMethod: CompressionMethod::STORE,
defaultEnableZeroHeader: false,
sendHttpHeaders: false,
outputStream: $this->tempfileStream,
);
$zip->addFileFromCallback(
'sample.txt',
exactSize: 18,
callback: function () {
return 'Sample String Data';
}
);
$zip->addFileFromCallback(
'.gitkeep',
exactSize: 0,
callback: function () {
return '';
}
);
$size = $zip->finish();
$this->assertEquals(filesize($this->tempfile), 0);
$zip->executeSimulation();
clearstatcache();
$this->assertEquals(filesize($this->tempfile), $size);
$tmpDir = $this->validateAndExtractZip($this->tempfile);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['.gitkeep', 'sample.txt'], $files);
}
public function testExecuteSimulationBeforeFinish(): void
{
$this->expectException(RuntimeException::class);
$zip = new ZipStream(
operationMode: OperationMode::SIMULATE_LAX,
defaultEnableZeroHeader: false,
sendHttpHeaders: false,
outputStream: $this->tempfileStream,
);
$zip->executeSimulation();
}
private function addLargeFileFileFromPath(CompressionMethod $compressionMethod, $zeroHeader, $zip64): void
{
[$tmp, $stream] = $this->getTmpFileStream();
$zip = new ZipStream(
outputStream: $stream,
sendHttpHeaders: false,
defaultEnableZeroHeader: $zeroHeader,
enableZip64: $zip64,
);
[$tmpExample, $streamExample] = $this->getTmpFileStream();
for ($i = 0; $i <= 10000; $i++) {
fwrite($streamExample, sha1((string) $i));
if ($i % 100 === 0) {
fwrite($streamExample, "\n");
}
}
fclose($streamExample);
$shaExample = sha1_file($tmpExample);
$zip->addFileFromPath('sample.txt', $tmpExample);
unlink($tmpExample);
$zip->finish();
fclose($stream);
$tmpDir = $this->validateAndExtractZip($tmp);
$files = $this->getRecursiveFileList($tmpDir);
$this->assertSame(['sample.txt'], $files);
$this->assertSame(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$compressionMethod->value}");
unlink($tmp);
}
}
PK V_]ba=z $ zipstream-php/test/PackFieldTest.phpnu [ assertSame(
bin2hex(PackField::pack(new PackField(format: 'v', value: 0x1122))),
'2211',
);
}
public function testOverflow2(): void
{
$this->expectException(RuntimeException::class);
PackField::pack(new PackField(format: 'v', value: 0xFFFFF));
}
public function testOverflow4(): void
{
$this->expectException(RuntimeException::class);
PackField::pack(new PackField(format: 'V', value: 0xFFFFFFFFF));
}
public function testUnknownOperator(): void
{
$this->assertSame(
bin2hex(PackField::pack(new PackField(format: 'a', value: 0x1122))),
'34',
);
}
}
PK V_]q q % zipstream-php/test/ResourceStream.phpnu [ isSeekable()) {
$this->seek(0);
}
return (string) stream_get_contents($this->stream);
}
public function close(): void
{
$stream = $this->detach();
if ($stream) {
fclose($stream);
}
}
public function detach()
{
$result = $this->stream;
// According to the interface, the stream is left in an unusable state;
/** @psalm-suppress PossiblyNullPropertyAssignmentValue */
$this->stream = null;
return $result;
}
public function seek(int $offset, int $whence = SEEK_SET): void
{
if (!$this->isSeekable()) {
throw new RuntimeException();
}
if (fseek($this->stream, $offset, $whence) !== 0) {
// @codeCoverageIgnoreStart
throw new RuntimeException();
// @codeCoverageIgnoreEnd
}
}
public function isSeekable(): bool
{
return (bool) $this->getMetadata('seekable');
}
public function getMetadata(?string $key = null)
{
$metadata = stream_get_meta_data($this->stream);
return $key !== null ? @$metadata[$key] : $metadata;
}
public function getSize(): ?int
{
$stats = fstat($this->stream);
return $stats['size'];
}
public function tell(): int
{
$position = ftell($this->stream);
if ($position === false) {
// @codeCoverageIgnoreStart
throw new RuntimeException();
// @codeCoverageIgnoreEnd
}
return $position;
}
public function eof(): bool
{
return feof($this->stream);
}
public function rewind(): void
{
$this->seek(0);
}
public function write(string $string): int
{
if (!$this->isWritable()) {
throw new RuntimeException();
}
if (fwrite($this->stream, $string) === false) {
// @codeCoverageIgnoreStart
throw new RuntimeException();
// @codeCoverageIgnoreEnd
}
return strlen($string);
}
public function isWritable(): bool
{
$mode = $this->getMetadata('mode');
if (!is_string($mode)) {
// @codeCoverageIgnoreStart
throw new RuntimeException('Could not get stream mode from metadata!');
// @codeCoverageIgnoreEnd
}
return preg_match('/[waxc+]/', $mode) === 1;
}
public function read(int $length): string
{
if (!$this->isReadable()) {
throw new RuntimeException();
}
$result = fread($this->stream, $length);
if ($result === false) {
// @codeCoverageIgnoreStart
throw new RuntimeException();
// @codeCoverageIgnoreEnd
}
return $result;
}
public function isReadable(): bool
{
$mode = $this->getMetadata('mode');
if (!is_string($mode)) {
// @codeCoverageIgnoreStart
throw new RuntimeException('Could not get stream mode from metadata!');
// @codeCoverageIgnoreEnd
}
return preg_match('/[r+]/', $mode) === 1;
}
public function getContents(): string
{
if (!$this->isReadable()) {
throw new RuntimeException();
}
$result = stream_get_contents($this->stream);
if ($result === false) {
// @codeCoverageIgnoreStart
throw new RuntimeException();
// @codeCoverageIgnoreEnd
}
return $result;
}
}
PK V_]E zipstream-php/.phive/phars.xmlnu [
PK V_]nt ; zipstream-php/.github/PULL_REQUEST_TEMPLATE/FAILING_TEST.mdnu [
PK V_]Txl l 2 zipstream-php/.github/PULL_REQUEST_TEMPLATE/FIX.mdnu [
PK V_]f : zipstream-php/.github/PULL_REQUEST_TEMPLATE/NEW_FEATURE.mdnu [
PK V_]a2 : zipstream-php/.github/PULL_REQUEST_TEMPLATE/IMPROVEMENT.mdnu [
PK V_]