1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-07-31 20:50:13 +02:00

Fixed problem with cloning a zip container.

This commit is contained in:
Ne-Lexa
2020-01-22 12:48:15 +03:00
parent 5ec656fde4
commit 943cf3e777
10 changed files with 285 additions and 47 deletions

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,16 @@ namespace PhpZip\Tests;
use PhpZip\Exception\ZipEntryNotFoundException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\Tests\Internal\CustomZip\ZipFileCustomWriter;
use PhpZip\Tests\Internal\CustomZip\ZipFileWithBeforeSave;
use PhpZip\Tests\Internal\Epub\EpubFile;
use PhpZip\ZipFile;
/**
* Checks the ability to create own file-type class, reader, writer and container.
*
* @see http://www.epubtest.org/test-books source epub files
**.
*
* @internal
*
@@ -19,6 +23,8 @@ final class CustomZipFormatTest extends ZipTestCase
{
/**
* @throws ZipException
*
* @see http://www.epubtest.org/test-books source epub files
*/
public function testEpub()
{
@@ -57,4 +63,64 @@ final class CustomZipFormatTest extends ZipTestCase
self::assertTrue($epubFile->hasEntry('mimetype'));
$epubFile->close();
}
/**
* @throws \Exception
*/
public function testBeforeSaveInZipWriter()
{
$zipFile = new ZipFileCustomWriter();
for ($i = 0; $i < 10; $i++) {
$zipFile['file ' . $i] = 'contents file ' . $i;
}
$this->existsExtraFields($zipFile, false);
$zipFile->saveAsFile($this->outputFilename);
$this->existsExtraFields($zipFile, false);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
}
/**
* @throws \Exception
*/
public function testBeforeSaveInZipFile()
{
$zipFile = new ZipFileWithBeforeSave();
for ($i = 0; $i < 10; $i++) {
$zipFile['file ' . $i] = 'contents file ' . $i;
}
$this->existsExtraFields($zipFile, false);
$zipFile->saveAsFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
self::assertCorrectZipArchive($this->outputFilename);
$zipFile->openFile($this->outputFilename);
$this->existsExtraFields($zipFile, true);
$zipFile->close();
}
/**
* @param ZipFile $zipFile
* @param bool $exists
*/
private function existsExtraFields(ZipFile $zipFile, $exists)
{
foreach ($zipFile->getEntries() as $entry) {
$localExtras = $entry->getLocalExtraFields();
$cdExtras = $entry->getCdExtraFields();
self::assertSame(isset($localExtras[NtfsExtraField::HEADER_ID]), $exists);
self::assertSame(isset($cdExtras[NtfsExtraField::HEADER_ID]), $exists);
self::assertSame(isset($localExtras[NewUnixExtraField::HEADER_ID]), $exists);
self::assertSame(isset($cdExtras[NewUnixExtraField::HEADER_ID]), $exists);
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\IO\ZipWriter;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\Model\ZipContainer;
/**
* Class CustomZipWriter.
*/
class CustomZipWriter extends ZipWriter
{
/**
* ZipWriter constructor.
*
* @param ZipContainer $container
*/
public function __construct(ZipContainer $container)
{
// dump($container);
parent::__construct($container);
// dd($this->zipContainer);
}
protected function beforeWrite()
{
parent::beforeWrite();
$now = new \DateTimeImmutable();
$ntfsTimeExtra = NtfsExtraField::create($now, $now->modify('-1 day'), $now->modify('-10 day'));
$unixExtra = new NewUnixExtraField();
foreach ($this->zipContainer->getEntries() as $entry) {
$entry->addExtraField($ntfsTimeExtra);
$entry->addExtraField($unixExtra);
}
}
}

View File

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

View File

@@ -0,0 +1,28 @@
<?php
namespace PhpZip\Tests\Internal\CustomZip;
use PhpZip\Model\Extra\Fields\NewUnixExtraField;
use PhpZip\Model\Extra\Fields\NtfsExtraField;
use PhpZip\ZipFile;
/**
* Class ZipFileWithBeforeSave.
*/
class ZipFileWithBeforeSave extends ZipFile
{
/**
* Event before save or output.
*/
protected function onBeforeSave()
{
$now = new \DateTimeImmutable();
$ntfsTimeExtra = NtfsExtraField::create($now, $now->modify('-1 day'), $now->modify('-10 day'));
$unixExtra = new NewUnixExtraField();
foreach ($this->zipContainer->getEntries() as $entry) {
$entry->addExtraField($ntfsTimeExtra);
$entry->addExtraField($unixExtra);
}
}
}

View File

@@ -304,6 +304,55 @@ class ZipEntryTest extends TestCase
static::assertNull($zipEntry->getData());
}
/**
* @throws \Exception
*/
public function testZipNewDataGuardClone()
{
$resource = fopen('php://temp', 'r+b');
static::assertNotFalse($resource);
fwrite($resource, random_bytes(1024));
rewind($resource);
$zipEntry = new ZipEntry('entry');
$zipEntry2 = new ZipEntry('entry2');
$zipData = new ZipNewData($zipEntry, $resource);
$zipData2 = new ZipNewData($zipEntry2, $resource);
$cloneData = clone $zipData;
$cloneData2 = clone $cloneData;
static::assertSame($zipData->getDataAsStream(), $resource);
static::assertSame($zipData2->getDataAsStream(), $resource);
static::assertSame($cloneData->getDataAsStream(), $resource);
static::assertSame($cloneData2->getDataAsStream(), $resource);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
unset($cloneData);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
unset($zipData);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
unset($zipData2);
$validResource = \is_resource($resource);
static::assertTrue($validResource);
$reflectionClass = new \ReflectionClass($cloneData2);
static::assertSame(
$reflectionClass->getStaticProperties()['guardClonedStream'][(int) $resource],
0
);
unset($cloneData2);
$validResource = \is_resource($resource);
static::assertFalse($validResource);
}
/**
* @dataProvider providePlatform
*

View File

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

View File

@@ -2435,4 +2435,18 @@ class ZipFileTest extends ZipTestCase
$zipFile->close();
}
/**
* @throws ZipException
*/
public function testMultiSave()
{
$zipFile = new ZipFile();
$zipFile['file 1'] = 'contents';
for ($i = 0; $i < 10; $i++) {
$zipFile->saveAsFile($this->outputFilename);
self::assertCorrectZipArchive($this->outputFilename);
}
$zipFile->close();
}
}