<?php namespace PhpZip\Model\Data; use PhpZip\Model\ZipData; use PhpZip\Model\ZipEntry; use PhpZip\ZipFile; /** * The class contains a streaming resource with new content added to the ZIP archive. */ class ZipNewData implements ZipData { /** * 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. * * @param ZipEntry $zipEntry * @param string|resource $data */ public function __construct(ZipEntry $zipEntry, $data) { $this->zipEntry = $zipEntry; if (\is_string($data)) { $zipEntry->setUncompressedSize(\strlen($data)); if (!($handle = fopen('php://temp', 'w+b'))) { // @codeCoverageIgnoreStart throw new \RuntimeException('A temporary resource cannot be opened for writing.'); // @codeCoverageIgnoreEnd } fwrite($handle, $data); rewind($handle); $this->stream = $handle; } elseif (\is_resource($data)) { $this->stream = $data; } $resourceId = (int) $this->stream; self::$guardClonedStream[$resourceId] = isset(self::$guardClonedStream[$resourceId]) ? self::$guardClonedStream[$resourceId] + 1 : 0; } /** * @return resource returns stream data */ public function getDataAsStream() { if (!\is_resource($this->stream)) { throw new \LogicException(sprintf('Resource has been closed (entry=%s).', $this->zipEntry->getName())); } return $this->stream; } /** * @return string returns data as string */ public function getDataAsString() { $stream = $this->getDataAsStream(); $pos = ftell($stream); try { rewind($stream); return stream_get_contents($stream); } finally { fseek($stream, $pos); } } /** * @param resource $outStream */ public function copyDataToStream($outStream) { $stream = $this->getDataAsStream(); rewind($stream); 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); } } }