Ne-Lexa /
php-zip
| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace PhpZip\IO; |
||||
| 4 | |||||
| 5 | use PhpZip\Constants\DosCodePage; |
||||
| 6 | use PhpZip\Constants\ZipCompressionMethod; |
||||
| 7 | use PhpZip\Constants\ZipConstants; |
||||
| 8 | use PhpZip\Constants\ZipEncryptionMethod; |
||||
| 9 | use PhpZip\Constants\ZipPlatform; |
||||
| 10 | use PhpZip\Constants\ZipVersion; |
||||
| 11 | use PhpZip\Exception\ZipException; |
||||
| 12 | use PhpZip\Exception\ZipUnsupportMethodException; |
||||
| 13 | use PhpZip\IO\Filter\Cipher\Pkware\PKEncryptionStreamFilter; |
||||
| 14 | use PhpZip\IO\Filter\Cipher\WinZipAes\WinZipAesEncryptionStreamFilter; |
||||
| 15 | use PhpZip\Model\Data\ZipSourceFileData; |
||||
| 16 | use PhpZip\Model\Extra\Fields\ApkAlignmentExtraField; |
||||
| 17 | use PhpZip\Model\Extra\Fields\WinZipAesExtraField; |
||||
| 18 | use PhpZip\Model\Extra\Fields\Zip64ExtraField; |
||||
| 19 | use PhpZip\Model\ZipContainer; |
||||
| 20 | use PhpZip\Model\ZipEntry; |
||||
| 21 | use PhpZip\Util\PackUtil; |
||||
| 22 | use PhpZip\Util\StringUtil; |
||||
| 23 | |||||
| 24 | /** |
||||
| 25 | * Class ZipWriter. |
||||
| 26 | */ |
||||
| 27 | class ZipWriter |
||||
| 28 | { |
||||
| 29 | /** @var int Chunk read size */ |
||||
| 30 | const CHUNK_SIZE = 8192; |
||||
| 31 | |||||
| 32 | /** @var ZipContainer */ |
||||
| 33 | protected $zipContainer; |
||||
| 34 | |||||
| 35 | /** |
||||
| 36 | * ZipWriter constructor. |
||||
| 37 | * |
||||
| 38 | * @param ZipContainer $container |
||||
| 39 | */ |
||||
| 40 | 136 | public function __construct(ZipContainer $container) |
|||
| 41 | { |
||||
| 42 | // we clone the container so that the changes made to |
||||
| 43 | // it do not affect the data in the ZipFile class |
||||
| 44 | 136 | $this->zipContainer = clone $container; |
|||
| 45 | 136 | } |
|||
| 46 | |||||
| 47 | /** |
||||
| 48 | * @param resource $outStream |
||||
| 49 | * |
||||
| 50 | * @throws ZipException |
||||
| 51 | */ |
||||
| 52 | 136 | public function write($outStream) |
|||
| 53 | { |
||||
| 54 | 136 | if (!\is_resource($outStream)) { |
|||
| 55 | throw new \InvalidArgumentException('$outStream must be resource'); |
||||
| 56 | } |
||||
| 57 | 136 | $this->beforeWrite(); |
|||
| 58 | 136 | $this->writeLocalBlock($outStream); |
|||
| 59 | 136 | $cdOffset = ftell($outStream); |
|||
| 60 | 136 | $this->writeCentralDirectoryBlock($outStream); |
|||
| 61 | 136 | $cdSize = ftell($outStream) - $cdOffset; |
|||
| 62 | 136 | $this->writeEndOfCentralDirectoryBlock($outStream, $cdOffset, $cdSize); |
|||
| 63 | 136 | } |
|||
| 64 | |||||
| 65 | 136 | protected function beforeWrite() |
|||
| 66 | { |
||||
| 67 | 136 | } |
|||
| 68 | |||||
| 69 | /** |
||||
| 70 | * @param resource $outStream |
||||
| 71 | * |
||||
| 72 | * @throws ZipException |
||||
| 73 | */ |
||||
| 74 | 136 | protected function writeLocalBlock($outStream) |
|||
| 75 | { |
||||
| 76 | 136 | $zipEntries = $this->zipContainer->getEntries(); |
|||
| 77 | |||||
| 78 | 136 | foreach ($zipEntries as $zipEntry) { |
|||
| 79 | 134 | $this->writeLocalHeader($outStream, $zipEntry); |
|||
| 80 | 134 | $this->writeData($outStream, $zipEntry); |
|||
| 81 | |||||
| 82 | 134 | if ($zipEntry->isDataDescriptorEnabled()) { |
|||
| 83 | 4 | $this->writeDataDescriptor($outStream, $zipEntry); |
|||
| 84 | } |
||||
| 85 | } |
||||
| 86 | 136 | } |
|||
| 87 | |||||
| 88 | /** |
||||
| 89 | * @param resource $outStream |
||||
| 90 | * @param ZipEntry $entry |
||||
| 91 | * |
||||
| 92 | * @throws ZipException |
||||
| 93 | */ |
||||
| 94 | 134 | protected function writeLocalHeader($outStream, ZipEntry $entry) |
|||
| 95 | { |
||||
| 96 | // todo in 4.0 version move zipalign functional to ApkWriter class |
||||
| 97 | 134 | if ($this->zipContainer->isZipAlign()) { |
|||
| 98 | 4 | $this->zipAlign($outStream, $entry); |
|||
| 99 | } |
||||
| 100 | |||||
| 101 | 134 | $relativeOffset = ftell($outStream); |
|||
| 102 | 134 | $entry->setLocalHeaderOffset($relativeOffset); |
|||
| 103 | |||||
| 104 | 134 | if ($entry->isEncrypted() && $entry->getEncryptionMethod() === ZipEncryptionMethod::PKWARE) { |
|||
| 105 | 4 | $entry->enableDataDescriptor(true); |
|||
| 106 | } |
||||
| 107 | |||||
| 108 | 134 | $dd = $entry->isDataDescriptorRequired() || |
|||
| 109 | 134 | $entry->isDataDescriptorEnabled(); |
|||
| 110 | |||||
| 111 | 134 | $compressedSize = $entry->getCompressedSize(); |
|||
| 112 | 134 | $uncompressedSize = $entry->getUncompressedSize(); |
|||
| 113 | |||||
| 114 | 134 | $entry->getLocalExtraFields()->remove(Zip64ExtraField::HEADER_ID); |
|||
| 115 | |||||
| 116 | 134 | if ($compressedSize > ZipConstants::ZIP64_MAGIC || $uncompressedSize > ZipConstants::ZIP64_MAGIC) { |
|||
| 117 | $entry->getLocalExtraFields()->add( |
||||
| 118 | new Zip64ExtraField($uncompressedSize, $compressedSize) |
||||
| 119 | ); |
||||
| 120 | |||||
| 121 | $compressedSize = ZipConstants::ZIP64_MAGIC; |
||||
| 122 | $uncompressedSize = ZipConstants::ZIP64_MAGIC; |
||||
| 123 | } |
||||
| 124 | |||||
| 125 | 134 | $compressionMethod = $entry->getCompressionMethod(); |
|||
| 126 | 134 | $crc = $entry->getCrc(); |
|||
| 127 | |||||
| 128 | 134 | if ($entry->isEncrypted() && ZipEncryptionMethod::isWinZipAesMethod($entry->getEncryptionMethod())) { |
|||
| 129 | /** @var WinZipAesExtraField|null $winZipAesExtra */ |
||||
| 130 | 9 | $winZipAesExtra = $entry->getLocalExtraField(WinZipAesExtraField::HEADER_ID); |
|||
| 131 | |||||
| 132 | 9 | if ($winZipAesExtra === null) { |
|||
| 133 | 8 | $winZipAesExtra = WinZipAesExtraField::create($entry); |
|||
| 134 | } |
||||
| 135 | |||||
| 136 | 9 | if ($winZipAesExtra->isV2()) { |
|||
| 137 | 7 | $crc = 0; |
|||
| 138 | } |
||||
| 139 | 9 | $compressionMethod = ZipCompressionMethod::WINZIP_AES; |
|||
| 140 | } |
||||
| 141 | |||||
| 142 | 134 | $extra = $this->getExtraFieldsContents($entry, true); |
|||
| 143 | 134 | $name = $entry->getName(); |
|||
| 144 | 134 | $dosCharset = $entry->getCharset(); |
|||
| 145 | |||||
| 146 | 134 | if ($dosCharset !== null && !$entry->isUtf8Flag()) { |
|||
| 147 | $name = DosCodePage::fromUTF8($name, $dosCharset); |
||||
| 148 | } |
||||
| 149 | |||||
| 150 | 134 | $nameLength = \strlen($name); |
|||
| 151 | 134 | $extraLength = \strlen($extra); |
|||
| 152 | |||||
| 153 | 134 | $size = $nameLength + $extraLength; |
|||
| 154 | |||||
| 155 | 134 | if ($size > 0xffff) { |
|||
| 156 | throw new ZipException( |
||||
| 157 | sprintf( |
||||
| 158 | '%s (the total size of %s bytes for the name, extra fields and comment exceeds the maximum size of %d bytes)', |
||||
| 159 | $entry->getName(), |
||||
| 160 | $size, |
||||
| 161 | 0xffff |
||||
| 162 | ) |
||||
| 163 | ); |
||||
| 164 | } |
||||
| 165 | |||||
| 166 | 134 | $extractedBy = ($entry->getExtractedOS() << 8) | $entry->getExtractVersion(); |
|||
| 167 | |||||
| 168 | 134 | fwrite( |
|||
| 169 | 134 | $outStream, |
|||
| 170 | pack( |
||||
| 171 | 134 | 'VvvvVVVVvv', |
|||
| 172 | // local file header signature 4 bytes (0x04034b50) |
||||
| 173 | 134 | ZipConstants::LOCAL_FILE_HEADER, |
|||
| 174 | // version needed to extract 2 bytes |
||||
| 175 | $extractedBy, |
||||
| 176 | // general purpose bit flag 2 bytes |
||||
| 177 | 134 | $entry->getGeneralPurposeBitFlags(), |
|||
| 178 | // compression method 2 bytes |
||||
| 179 | $compressionMethod, |
||||
| 180 | // last mod file time 2 bytes |
||||
| 181 | // last mod file date 2 bytes |
||||
| 182 | 134 | $entry->getDosTime(), |
|||
| 183 | // crc-32 4 bytes |
||||
| 184 | 134 | $dd ? 0 : $crc, |
|||
| 185 | // compressed size 4 bytes |
||||
| 186 | 134 | $dd ? 0 : $compressedSize, |
|||
| 187 | // uncompressed size 4 bytes |
||||
| 188 | 134 | $dd ? 0 : $uncompressedSize, |
|||
| 189 | // file name length 2 bytes |
||||
| 190 | $nameLength, |
||||
| 191 | // extra field length 2 bytes |
||||
| 192 | $extraLength |
||||
| 193 | ) |
||||
| 194 | ); |
||||
| 195 | |||||
| 196 | 134 | if ($nameLength > 0) { |
|||
| 197 | 134 | fwrite($outStream, $name); |
|||
| 198 | } |
||||
| 199 | |||||
| 200 | 134 | if ($extraLength > 0) { |
|||
| 201 | 15 | fwrite($outStream, $extra); |
|||
| 202 | } |
||||
| 203 | 134 | } |
|||
| 204 | |||||
| 205 | /** |
||||
| 206 | * @param resource $outStream |
||||
| 207 | * @param ZipEntry $entry |
||||
| 208 | * |
||||
| 209 | * @throws ZipException |
||||
| 210 | */ |
||||
| 211 | 4 | private function zipAlign($outStream, ZipEntry $entry) |
|||
| 212 | { |
||||
| 213 | 4 | if (!$entry->isDirectory() && $entry->getCompressionMethod() === ZipCompressionMethod::STORED) { |
|||
| 214 | 4 | $entry->removeExtraField(ApkAlignmentExtraField::HEADER_ID); |
|||
| 215 | |||||
| 216 | 4 | $extra = $this->getExtraFieldsContents($entry, true); |
|||
| 217 | 4 | $extraLength = \strlen($extra); |
|||
| 218 | 4 | $name = $entry->getName(); |
|||
| 219 | |||||
| 220 | 4 | $dosCharset = $entry->getCharset(); |
|||
| 221 | |||||
| 222 | 4 | if ($dosCharset !== null && !$entry->isUtf8Flag()) { |
|||
| 223 | $name = DosCodePage::fromUTF8($name, $dosCharset); |
||||
| 224 | } |
||||
| 225 | 4 | $nameLength = \strlen($name); |
|||
| 226 | |||||
| 227 | 4 | $multiple = ApkAlignmentExtraField::ALIGNMENT_BYTES; |
|||
| 228 | |||||
| 229 | 4 | if (StringUtil::endsWith($name, '.so')) { |
|||
| 230 | $multiple = ApkAlignmentExtraField::COMMON_PAGE_ALIGNMENT_BYTES; |
||||
| 231 | } |
||||
| 232 | |||||
| 233 | 4 | $offset = ftell($outStream); |
|||
| 234 | |||||
| 235 | $dataMinStartOffset = |
||||
| 236 | $offset + |
||||
| 237 | 4 | ZipConstants::LFH_FILENAME_POS + |
|||
| 238 | 4 | $extraLength + |
|||
| 239 | 4 | $nameLength; |
|||
| 240 | |||||
| 241 | $padding = |
||||
| 242 | 4 | ($multiple - ($dataMinStartOffset % $multiple)) |
|||
| 243 | 4 | % $multiple; |
|||
| 244 | |||||
| 245 | 4 | if ($padding > 0) { |
|||
| 246 | 4 | $dataMinStartOffset += ApkAlignmentExtraField::MIN_SIZE; |
|||
| 247 | $padding = |
||||
| 248 | 4 | ($multiple - ($dataMinStartOffset % $multiple)) |
|||
| 249 | 4 | % $multiple; |
|||
| 250 | |||||
| 251 | 4 | $entry->getLocalExtraFields()->add( |
|||
| 252 | 4 | new ApkAlignmentExtraField($multiple, $padding) |
|||
| 253 | ); |
||||
| 254 | } |
||||
| 255 | } |
||||
| 256 | 4 | } |
|||
| 257 | |||||
| 258 | /** |
||||
| 259 | * Merges the local file data fields of the given ZipExtraFields. |
||||
| 260 | * |
||||
| 261 | * @param ZipEntry $entry |
||||
| 262 | * @param bool $local |
||||
| 263 | * |
||||
| 264 | * @throws ZipException |
||||
| 265 | * |
||||
| 266 | * @return string |
||||
| 267 | */ |
||||
| 268 | 134 | protected function getExtraFieldsContents(ZipEntry $entry, $local) |
|||
| 269 | { |
||||
| 270 | 134 | $local = (bool) $local; |
|||
| 271 | 134 | $collection = $local ? |
|||
| 272 | 134 | $entry->getLocalExtraFields() : |
|||
| 273 | 134 | $entry->getCdExtraFields(); |
|||
| 274 | 134 | $extraData = ''; |
|||
| 275 | |||||
| 276 | 134 | foreach ($collection as $extraField) { |
|||
| 277 | 15 | if ($local) { |
|||
| 278 | 15 | $data = $extraField->packLocalFileData(); |
|||
| 279 | } else { |
||||
| 280 | 12 | $data = $extraField->packCentralDirData(); |
|||
| 281 | } |
||||
| 282 | 15 | $extraData .= pack( |
|||
| 283 | 15 | 'vv', |
|||
| 284 | 15 | $extraField->getHeaderId(), |
|||
| 285 | 15 | \strlen($data) |
|||
| 286 | ); |
||||
| 287 | 15 | $extraData .= $data; |
|||
| 288 | } |
||||
| 289 | |||||
| 290 | 134 | $size = \strlen($extraData); |
|||
| 291 | |||||
| 292 | 134 | if ($size > 0xffff) { |
|||
| 293 | throw new ZipException( |
||||
| 294 | sprintf( |
||||
| 295 | 'Size extra out of range: %d. Extra data: %s', |
||||
| 296 | $size, |
||||
| 297 | $extraData |
||||
| 298 | ) |
||||
| 299 | ); |
||||
| 300 | } |
||||
| 301 | |||||
| 302 | 134 | return $extraData; |
|||
| 303 | } |
||||
| 304 | |||||
| 305 | /** |
||||
| 306 | * @param resource $outStream |
||||
| 307 | * @param ZipEntry $entry |
||||
| 308 | * |
||||
| 309 | * @throws ZipException |
||||
| 310 | */ |
||||
| 311 | 134 | protected function writeData($outStream, ZipEntry $entry) |
|||
| 312 | { |
||||
| 313 | 134 | $zipData = $entry->getData(); |
|||
| 314 | |||||
| 315 | 134 | if ($zipData === null) { |
|||
| 316 | 23 | if ($entry->isDirectory()) { |
|||
| 317 | 23 | return; |
|||
| 318 | } |
||||
| 319 | |||||
| 320 | throw new ZipException(sprintf('No zip data for entry "%s"', $entry->getName())); |
||||
| 321 | } |
||||
| 322 | |||||
| 323 | // data write variants: |
||||
| 324 | // -------------------- |
||||
| 325 | // * data of source zip file -> copy compressed data |
||||
| 326 | // * store - simple write |
||||
| 327 | // * store and encryption - apply encryption filter and simple write |
||||
| 328 | // * deflate or bzip2 - apply compression filter and simple write |
||||
| 329 | // * (deflate or bzip2) and encryption - create temp stream and apply |
||||
| 330 | // compression filter to it, then apply encryption filter to root |
||||
| 331 | // stream and write temp stream data. |
||||
| 332 | // (PHP cannot apply the filter for encryption after the compression |
||||
| 333 | // filter, so a temporary stream is created for the compressed data) |
||||
| 334 | |||||
| 335 | 134 | if ($zipData instanceof ZipSourceFileData && !$zipData->hasRecompressData($entry)) { |
|||
| 336 | // data of source zip file -> copy compressed data |
||||
| 337 | 28 | $zipData->copyCompressedDataToStream($outStream); |
|||
| 338 | |||||
| 339 | 28 | return; |
|||
| 340 | } |
||||
| 341 | |||||
| 342 | 133 | $entryStream = $zipData->getDataAsStream(); |
|||
| 343 | |||||
| 344 | 133 | if (stream_get_meta_data($entryStream)['seekable']) { |
|||
| 345 | 132 | rewind($entryStream); |
|||
| 346 | } |
||||
| 347 | |||||
| 348 | 133 | $uncompressedSize = $entry->getUncompressedSize(); |
|||
| 349 | |||||
| 350 | 133 | $posBeforeWrite = ftell($outStream); |
|||
| 351 | 133 | $compressionMethod = $entry->getCompressionMethod(); |
|||
| 352 | |||||
| 353 | 133 | if ($entry->isEncrypted()) { |
|||
| 354 | 10 | if ($compressionMethod === ZipCompressionMethod::STORED) { |
|||
| 355 | 7 | $contextFilter = $this->appendEncryptionFilter($outStream, $entry, $uncompressedSize); |
|||
|
0 ignored issues
–
show
|
|||||
| 356 | 7 | $checksum = $this->writeAndCountChecksum($entryStream, $outStream, $uncompressedSize); |
|||
| 357 | } else { |
||||
| 358 | 3 | $compressStream = fopen('php://temp', 'w+b'); |
|||
| 359 | 3 | $contextFilter = $this->appendCompressionFilter($compressStream, $entry); |
|||
|
0 ignored issues
–
show
It seems like
$compressStream can also be of type false; however, parameter $outStream of PhpZip\IO\ZipWriter::appendCompressionFilter() does only seem to accept resource, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 360 | 3 | $checksum = $this->writeAndCountChecksum($entryStream, $compressStream, $uncompressedSize); |
|||
|
0 ignored issues
–
show
It seems like
$compressStream can also be of type false; however, parameter $outStream of PhpZip\IO\ZipWriter::writeAndCountChecksum() does only seem to accept resource, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 361 | |||||
| 362 | 3 | if ($contextFilter !== null) { |
|||
| 363 | 3 | stream_filter_remove($contextFilter); |
|||
| 364 | 3 | $contextFilter = null; |
|||
|
0 ignored issues
–
show
|
|||||
| 365 | } |
||||
| 366 | |||||
| 367 | 3 | rewind($compressStream); |
|||
|
0 ignored issues
–
show
It seems like
$compressStream can also be of type false; however, parameter $handle of rewind() does only seem to accept resource, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 368 | |||||
| 369 | 3 | $compressedSize = fstat($compressStream)['size']; |
|||
|
0 ignored issues
–
show
It seems like
$compressStream can also be of type false; however, parameter $handle of fstat() does only seem to accept resource, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 370 | 3 | $contextFilter = $this->appendEncryptionFilter($outStream, $entry, $compressedSize); |
|||
|
0 ignored issues
–
show
Are you sure the assignment to
$contextFilter is correct as $this->appendEncryptionF...entry, $compressedSize) targeting PhpZip\IO\ZipWriter::appendEncryptionFilter() seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. Loading history...
|
|||||
| 371 | |||||
| 372 | 10 | stream_copy_to_stream($compressStream, $outStream); |
|||
|
0 ignored issues
–
show
It seems like
$compressStream can also be of type false; however, parameter $source of stream_copy_to_stream() does only seem to accept resource, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 373 | } |
||||
| 374 | } else { |
||||
| 375 | 126 | $contextFilter = $this->appendCompressionFilter($outStream, $entry); |
|||
| 376 | 126 | $checksum = $this->writeAndCountChecksum($entryStream, $outStream, $uncompressedSize); |
|||
| 377 | } |
||||
| 378 | |||||
| 379 | 133 | if ($contextFilter !== null) { |
|||
| 380 | 56 | stream_filter_remove($contextFilter); |
|||
| 381 | 56 | $contextFilter = null; |
|||
| 382 | } |
||||
| 383 | |||||
| 384 | // my hack {@see https://bugs.php.net/bug.php?id=49874} |
||||
| 385 | 133 | fseek($outStream, 0, \SEEK_END); |
|||
| 386 | 133 | $compressedSize = ftell($outStream) - $posBeforeWrite; |
|||
| 387 | |||||
| 388 | 133 | $entry->setCompressedSize($compressedSize); |
|||
| 389 | 133 | $entry->setCrc($checksum); |
|||
| 390 | |||||
| 391 | 133 | if (!$entry->isDataDescriptorEnabled()) { |
|||
| 392 | 132 | if ($uncompressedSize > ZipConstants::ZIP64_MAGIC || $compressedSize > ZipConstants::ZIP64_MAGIC) { |
|||
| 393 | /** @var Zip64ExtraField|null $zip64ExtraLocal */ |
||||
| 394 | $zip64ExtraLocal = $entry->getLocalExtraField(Zip64ExtraField::HEADER_ID); |
||||
| 395 | |||||
| 396 | // if there is a zip64 extra record, then update it; |
||||
| 397 | // if not, write data to data descriptor |
||||
| 398 | if ($zip64ExtraLocal !== null) { |
||||
| 399 | $zip64ExtraLocal->setCompressedSize($compressedSize); |
||||
| 400 | $zip64ExtraLocal->setUncompressedSize($uncompressedSize); |
||||
| 401 | |||||
| 402 | $posExtra = $entry->getLocalHeaderOffset() + ZipConstants::LFH_FILENAME_POS + \strlen($entry->getName()); |
||||
| 403 | fseek($outStream, $posExtra); |
||||
| 404 | fwrite($outStream, $this->getExtraFieldsContents($entry, true)); |
||||
| 405 | } else { |
||||
| 406 | $posGPBF = $entry->getLocalHeaderOffset() + 6; |
||||
| 407 | $entry->enableDataDescriptor(true); |
||||
| 408 | fseek($outStream, $posGPBF); |
||||
| 409 | fwrite( |
||||
| 410 | $outStream, |
||||
| 411 | pack( |
||||
| 412 | 'v', |
||||
| 413 | // general purpose bit flag 2 bytes |
||||
| 414 | $entry->getGeneralPurposeBitFlags() |
||||
| 415 | ) |
||||
| 416 | ); |
||||
| 417 | } |
||||
| 418 | |||||
| 419 | $compressedSize = ZipConstants::ZIP64_MAGIC; |
||||
| 420 | $uncompressedSize = ZipConstants::ZIP64_MAGIC; |
||||
| 421 | } |
||||
| 422 | |||||
| 423 | 132 | $posChecksum = $entry->getLocalHeaderOffset() + 14; |
|||
| 424 | |||||
| 425 | /** @var WinZipAesExtraField|null $winZipAesExtra */ |
||||
| 426 | 132 | $winZipAesExtra = $entry->getLocalExtraField(WinZipAesExtraField::HEADER_ID); |
|||
| 427 | |||||
| 428 | 132 | if ($winZipAesExtra !== null && $winZipAesExtra->isV2()) { |
|||
| 429 | 7 | $checksum = 0; |
|||
| 430 | } |
||||
| 431 | |||||
| 432 | 132 | fseek($outStream, $posChecksum); |
|||
| 433 | 132 | fwrite( |
|||
| 434 | 132 | $outStream, |
|||
| 435 | pack( |
||||
| 436 | 132 | 'VVV', |
|||
| 437 | // crc-32 4 bytes |
||||
| 438 | $checksum, |
||||
| 439 | // compressed size 4 bytes |
||||
| 440 | $compressedSize, |
||||
| 441 | // uncompressed size 4 bytes |
||||
| 442 | $uncompressedSize |
||||
| 443 | ) |
||||
| 444 | ); |
||||
| 445 | 132 | fseek($outStream, 0, \SEEK_END); |
|||
| 446 | } |
||||
| 447 | 133 | } |
|||
| 448 | |||||
| 449 | /** |
||||
| 450 | * @param resource $inStream |
||||
| 451 | * @param resource $outStream |
||||
| 452 | * @param int $size |
||||
| 453 | * |
||||
| 454 | * @return int |
||||
| 455 | */ |
||||
| 456 | 133 | private function writeAndCountChecksum($inStream, $outStream, $size) |
|||
| 457 | { |
||||
| 458 | 133 | $contextHash = hash_init('crc32b'); |
|||
| 459 | 133 | $offset = 0; |
|||
| 460 | |||||
| 461 | 133 | while ($offset < $size) { |
|||
| 462 | 128 | $read = min(self::CHUNK_SIZE, $size - $offset); |
|||
| 463 | 128 | $buffer = fread($inStream, $read); |
|||
| 464 | 128 | fwrite($outStream, $buffer); |
|||
| 465 | 128 | hash_update($contextHash, $buffer); |
|||
| 466 | 128 | $offset += $read; |
|||
| 467 | } |
||||
| 468 | |||||
| 469 | 133 | return (int) hexdec(hash_final($contextHash)); |
|||
| 470 | } |
||||
| 471 | |||||
| 472 | /** |
||||
| 473 | * @param resource $outStream |
||||
| 474 | * @param ZipEntry $entry |
||||
| 475 | * |
||||
| 476 | * @throws ZipUnsupportMethodException |
||||
| 477 | * |
||||
| 478 | * @return resource|null |
||||
| 479 | */ |
||||
| 480 | 128 | protected function appendCompressionFilter($outStream, ZipEntry $entry) |
|||
| 481 | { |
||||
| 482 | 128 | $contextCompress = null; |
|||
| 483 | 128 | switch ($entry->getCompressionMethod()) { |
|||
| 484 | case ZipCompressionMethod::DEFLATED: |
||||
| 485 | 49 | if (!($contextCompress = stream_filter_append( |
|||
| 486 | 49 | $outStream, |
|||
| 487 | 49 | 'zlib.deflate', |
|||
| 488 | 49 | \STREAM_FILTER_WRITE, |
|||
| 489 | 49 | ['level' => $entry->getCompressionLevel()] |
|||
| 490 | ))) { |
||||
| 491 | throw new \RuntimeException('Could not append filter "zlib.deflate" to out stream'); |
||||
| 492 | } |
||||
| 493 | 49 | break; |
|||
| 494 | |||||
| 495 | case ZipCompressionMethod::BZIP2: |
||||
| 496 | 4 | if (!($contextCompress = stream_filter_append( |
|||
| 497 | 4 | $outStream, |
|||
| 498 | 4 | 'bzip2.compress', |
|||
| 499 | 4 | \STREAM_FILTER_WRITE, |
|||
| 500 | 4 | ['blocks' => $entry->getCompressionLevel(), 'work' => 0] |
|||
| 501 | ))) { |
||||
| 502 | throw new \RuntimeException('Could not append filter "bzip2.compress" to out stream'); |
||||
| 503 | } |
||||
| 504 | 4 | break; |
|||
| 505 | |||||
| 506 | case ZipCompressionMethod::STORED: |
||||
| 507 | // file without compression, do nothing |
||||
| 508 | 107 | break; |
|||
| 509 | |||||
| 510 | default: |
||||
| 511 | throw new ZipUnsupportMethodException( |
||||
| 512 | sprintf( |
||||
| 513 | '%s (compression method %d (%s) is not supported)', |
||||
| 514 | $entry->getName(), |
||||
| 515 | $entry->getCompressionMethod(), |
||||
| 516 | ZipCompressionMethod::getCompressionMethodName($entry->getCompressionMethod()) |
||||
| 517 | ) |
||||
| 518 | ); |
||||
| 519 | } |
||||
| 520 | |||||
| 521 | 128 | return $contextCompress; |
|||
| 522 | } |
||||
| 523 | |||||
| 524 | /** |
||||
| 525 | * @param resource $outStream |
||||
| 526 | * @param ZipEntry $entry |
||||
| 527 | * @param int $size |
||||
| 528 | * |
||||
| 529 | * @return resource|null |
||||
| 530 | */ |
||||
| 531 | 10 | protected function appendEncryptionFilter($outStream, ZipEntry $entry, $size) |
|||
| 532 | { |
||||
| 533 | 10 | $encContextFilter = null; |
|||
| 534 | |||||
| 535 | 10 | if ($entry->isEncrypted()) { |
|||
| 536 | 10 | if ($entry->getEncryptionMethod() === ZipEncryptionMethod::PKWARE) { |
|||
| 537 | 4 | PKEncryptionStreamFilter::register(); |
|||
| 538 | 4 | $cipherFilterName = PKEncryptionStreamFilter::FILTER_NAME; |
|||
| 539 | } else { |
||||
| 540 | 9 | WinZipAesEncryptionStreamFilter::register(); |
|||
| 541 | 9 | $cipherFilterName = WinZipAesEncryptionStreamFilter::FILTER_NAME; |
|||
| 542 | } |
||||
| 543 | 10 | $encContextFilter = stream_filter_append( |
|||
| 544 | 10 | $outStream, |
|||
| 545 | $cipherFilterName, |
||||
| 546 | 10 | \STREAM_FILTER_WRITE, |
|||
| 547 | [ |
||||
| 548 | 10 | 'entry' => $entry, |
|||
| 549 | 10 | 'size' => $size, |
|||
| 550 | ] |
||||
| 551 | ); |
||||
| 552 | |||||
| 553 | 10 | if (!$encContextFilter) { |
|||
|
0 ignored issues
–
show
|
|||||
| 554 | throw new \RuntimeException('Not apply filter ' . $cipherFilterName); |
||||
| 555 | } |
||||
| 556 | } |
||||
| 557 | |||||
| 558 | 10 | return $encContextFilter; |
|||
| 559 | } |
||||
| 560 | |||||
| 561 | /** |
||||
| 562 | * @param resource $outStream |
||||
| 563 | * @param ZipEntry $entry |
||||
| 564 | */ |
||||
| 565 | 4 | protected function writeDataDescriptor($outStream, ZipEntry $entry) |
|||
| 566 | { |
||||
| 567 | 4 | $crc = $entry->getCrc(); |
|||
| 568 | |||||
| 569 | /** @var WinZipAesExtraField|null $winZipAesExtra */ |
||||
| 570 | 4 | $winZipAesExtra = $entry->getLocalExtraField(WinZipAesExtraField::HEADER_ID); |
|||
| 571 | |||||
| 572 | 4 | if ($winZipAesExtra !== null && $winZipAesExtra->isV2()) { |
|||
| 573 | $crc = 0; |
||||
| 574 | } |
||||
| 575 | |||||
| 576 | 4 | fwrite( |
|||
| 577 | 4 | $outStream, |
|||
| 578 | pack( |
||||
| 579 | 4 | 'VV', |
|||
| 580 | // data descriptor signature 4 bytes (0x08074b50) |
||||
| 581 | 4 | ZipConstants::DATA_DESCRIPTOR, |
|||
| 582 | // crc-32 4 bytes |
||||
| 583 | $crc |
||||
| 584 | ) |
||||
| 585 | ); |
||||
| 586 | |||||
| 587 | if ( |
||||
| 588 | 4 | $entry->isZip64ExtensionsRequired() || |
|||
| 589 | 4 | $entry->getLocalExtraFields()->has(Zip64ExtraField::HEADER_ID) |
|||
| 590 | ) { |
||||
| 591 | $dd = |
||||
| 592 | // compressed size 8 bytes |
||||
| 593 | PackUtil::packLongLE($entry->getCompressedSize()) . |
||||
| 594 | // uncompressed size 8 bytes |
||||
| 595 | PackUtil::packLongLE($entry->getUncompressedSize()); |
||||
| 596 | } else { |
||||
| 597 | 4 | $dd = pack( |
|||
| 598 | 4 | 'VV', |
|||
| 599 | // compressed size 4 bytes |
||||
| 600 | 4 | $entry->getCompressedSize(), |
|||
| 601 | // uncompressed size 4 bytes |
||||
| 602 | 4 | $entry->getUncompressedSize() |
|||
| 603 | ); |
||||
| 604 | } |
||||
| 605 | |||||
| 606 | 4 | fwrite($outStream, $dd); |
|||
| 607 | 4 | } |
|||
| 608 | |||||
| 609 | /** |
||||
| 610 | * @param resource $outStream |
||||
| 611 | * |
||||
| 612 | * @throws ZipException |
||||
| 613 | */ |
||||
| 614 | 136 | protected function writeCentralDirectoryBlock($outStream) |
|||
| 615 | { |
||||
| 616 | 136 | foreach ($this->zipContainer->getEntries() as $outputEntry) { |
|||
| 617 | 134 | $this->writeCentralDirectoryHeader($outStream, $outputEntry); |
|||
| 618 | } |
||||
| 619 | 136 | } |
|||
| 620 | |||||
| 621 | /** |
||||
| 622 | * Writes a Central File Header record. |
||||
| 623 | * |
||||
| 624 | * @param resource $outStream |
||||
| 625 | * @param ZipEntry $entry |
||||
| 626 | * |
||||
| 627 | * @throws ZipException |
||||
| 628 | */ |
||||
| 629 | 134 | protected function writeCentralDirectoryHeader($outStream, ZipEntry $entry) |
|||
| 630 | { |
||||
| 631 | 134 | $compressedSize = $entry->getCompressedSize(); |
|||
| 632 | 134 | $uncompressedSize = $entry->getUncompressedSize(); |
|||
| 633 | 134 | $localHeaderOffset = $entry->getLocalHeaderOffset(); |
|||
| 634 | |||||
| 635 | 134 | $entry->getCdExtraFields()->remove(Zip64ExtraField::HEADER_ID); |
|||
| 636 | |||||
| 637 | if ( |
||||
| 638 | 134 | $localHeaderOffset > ZipConstants::ZIP64_MAGIC || |
|||
| 639 | 134 | $compressedSize > ZipConstants::ZIP64_MAGIC || |
|||
| 640 | 134 | $uncompressedSize > ZipConstants::ZIP64_MAGIC |
|||
| 641 | ) { |
||||
| 642 | $zip64ExtraField = new Zip64ExtraField(); |
||||
| 643 | |||||
| 644 | if ($uncompressedSize >= ZipConstants::ZIP64_MAGIC) { |
||||
| 645 | $zip64ExtraField->setUncompressedSize($uncompressedSize); |
||||
| 646 | $uncompressedSize = ZipConstants::ZIP64_MAGIC; |
||||
| 647 | } |
||||
| 648 | |||||
| 649 | if ($compressedSize >= ZipConstants::ZIP64_MAGIC) { |
||||
| 650 | $zip64ExtraField->setCompressedSize($compressedSize); |
||||
| 651 | $compressedSize = ZipConstants::ZIP64_MAGIC; |
||||
| 652 | } |
||||
| 653 | |||||
| 654 | if ($localHeaderOffset >= ZipConstants::ZIP64_MAGIC) { |
||||
| 655 | $zip64ExtraField->setLocalHeaderOffset($localHeaderOffset); |
||||
| 656 | $localHeaderOffset = ZipConstants::ZIP64_MAGIC; |
||||
| 657 | } |
||||
| 658 | |||||
| 659 | $entry->getCdExtraFields()->add($zip64ExtraField); |
||||
| 660 | } |
||||
| 661 | |||||
| 662 | 134 | $extra = $this->getExtraFieldsContents($entry, false); |
|||
| 663 | 134 | $extraLength = \strlen($extra); |
|||
| 664 | |||||
| 665 | 134 | $name = $entry->getName(); |
|||
| 666 | 134 | $comment = $entry->getComment(); |
|||
| 667 | |||||
| 668 | 134 | $dosCharset = $entry->getCharset(); |
|||
| 669 | |||||
| 670 | 134 | if ($dosCharset !== null && !$entry->isUtf8Flag()) { |
|||
| 671 | $name = DosCodePage::fromUTF8($name, $dosCharset); |
||||
| 672 | |||||
| 673 | if ($comment) { |
||||
| 674 | $comment = DosCodePage::fromUTF8($comment, $dosCharset); |
||||
| 675 | } |
||||
| 676 | } |
||||
| 677 | |||||
| 678 | 134 | $commentLength = \strlen($comment); |
|||
| 679 | |||||
| 680 | 134 | $compressionMethod = $entry->getCompressionMethod(); |
|||
| 681 | 134 | $crc = $entry->getCrc(); |
|||
| 682 | |||||
| 683 | /** @var WinZipAesExtraField|null $winZipAesExtra */ |
||||
| 684 | 134 | $winZipAesExtra = $entry->getLocalExtraField(WinZipAesExtraField::HEADER_ID); |
|||
| 685 | |||||
| 686 | 134 | if ($winZipAesExtra !== null) { |
|||
| 687 | 9 | if ($winZipAesExtra->isV2()) { |
|||
| 688 | 7 | $crc = 0; |
|||
| 689 | } |
||||
| 690 | 9 | $compressionMethod = ZipCompressionMethod::WINZIP_AES; |
|||
| 691 | } |
||||
| 692 | |||||
| 693 | 134 | fwrite( |
|||
| 694 | 134 | $outStream, |
|||
| 695 | pack( |
||||
| 696 | 134 | 'VvvvvVVVVvvvvvVV', |
|||
| 697 | // central file header signature 4 bytes (0x02014b50) |
||||
| 698 | 134 | ZipConstants::CENTRAL_FILE_HEADER, |
|||
| 699 | // version made by 2 bytes |
||||
| 700 | 134 | ($entry->getCreatedOS() << 8) | $entry->getSoftwareVersion(), |
|||
| 701 | // version needed to extract 2 bytes |
||||
| 702 | 134 | ($entry->getExtractedOS() << 8) | $entry->getExtractVersion(), |
|||
| 703 | // general purpose bit flag 2 bytes |
||||
| 704 | 134 | $entry->getGeneralPurposeBitFlags(), |
|||
| 705 | // compression method 2 bytes |
||||
| 706 | $compressionMethod, |
||||
| 707 | // last mod file datetime 4 bytes |
||||
| 708 | 134 | $entry->getDosTime(), |
|||
| 709 | // crc-32 4 bytes |
||||
| 710 | $crc, |
||||
| 711 | // compressed size 4 bytes |
||||
| 712 | $compressedSize, |
||||
| 713 | // uncompressed size 4 bytes |
||||
| 714 | $uncompressedSize, |
||||
| 715 | // file name length 2 bytes |
||||
| 716 | 134 | \strlen($name), |
|||
| 717 | // extra field length 2 bytes |
||||
| 718 | $extraLength, |
||||
| 719 | // file comment length 2 bytes |
||||
| 720 | $commentLength, |
||||
| 721 | // disk number start 2 bytes |
||||
| 722 | 134 | 0, |
|||
| 723 | // internal file attributes 2 bytes |
||||
| 724 | 134 | $entry->getInternalAttributes(), |
|||
| 725 | // external file attributes 4 bytes |
||||
| 726 | 134 | $entry->getExternalAttributes(), |
|||
| 727 | // relative offset of local header 4 bytes |
||||
| 728 | $localHeaderOffset |
||||
| 729 | ) |
||||
| 730 | ); |
||||
| 731 | |||||
| 732 | // file name (variable size) |
||||
| 733 | 134 | fwrite($outStream, $name); |
|||
| 734 | |||||
| 735 | 134 | if ($extraLength > 0) { |
|||
| 736 | // extra field (variable size) |
||||
| 737 | 12 | fwrite($outStream, $extra); |
|||
| 738 | } |
||||
| 739 | |||||
| 740 | 134 | if ($commentLength > 0) { |
|||
| 741 | // file comment (variable size) |
||||
| 742 | 3 | fwrite($outStream, $comment); |
|||
| 743 | } |
||||
| 744 | 134 | } |
|||
| 745 | |||||
| 746 | /** |
||||
| 747 | * @param resource $outStream |
||||
| 748 | * @param int $centralDirectoryOffset |
||||
| 749 | * @param int $centralDirectorySize |
||||
| 750 | */ |
||||
| 751 | 136 | protected function writeEndOfCentralDirectoryBlock( |
|||
| 752 | $outStream, |
||||
| 753 | $centralDirectoryOffset, |
||||
| 754 | $centralDirectorySize |
||||
| 755 | ) { |
||||
| 756 | 136 | $cdEntriesCount = \count($this->zipContainer); |
|||
| 757 | |||||
| 758 | 136 | $cdEntriesZip64 = $cdEntriesCount > 0xffff; |
|||
| 759 | 136 | $cdSizeZip64 = $centralDirectorySize > ZipConstants::ZIP64_MAGIC; |
|||
| 760 | 136 | $cdOffsetZip64 = $centralDirectoryOffset > ZipConstants::ZIP64_MAGIC; |
|||
| 761 | |||||
| 762 | 136 | $zip64Required = $cdEntriesZip64 |
|||
| 763 | 135 | || $cdSizeZip64 |
|||
| 764 | 136 | || $cdOffsetZip64; |
|||
| 765 | |||||
| 766 | 136 | if ($zip64Required) { |
|||
| 767 | 1 | $zip64EndOfCentralDirectoryOffset = ftell($outStream); |
|||
| 768 | |||||
| 769 | // find max software version, version needed to extract and most common platform |
||||
| 770 | 1 | list($softwareVersion, $versionNeededToExtract) = array_reduce( |
|||
| 771 | 1 | $this->zipContainer->getEntries(), |
|||
| 772 | 1 | static function (array $carry, ZipEntry $entry) { |
|||
| 773 | 1 | $carry[0] = max($carry[0], $entry->getSoftwareVersion() & 0xFF); |
|||
| 774 | 1 | $carry[1] = max($carry[1], $entry->getExtractVersion() & 0xFF); |
|||
| 775 | |||||
| 776 | 1 | return $carry; |
|||
| 777 | 1 | }, |
|||
| 778 | [ZipVersion::v10_DEFAULT_MIN, ZipVersion::v45_ZIP64_EXT] |
||||
| 779 | ); |
||||
| 780 | |||||
| 781 | $createdOS = $extractedOS = ZipPlatform::OS_DOS; |
||||
| 782 | $versionMadeBy = ($createdOS << 8) | max($softwareVersion, ZipVersion::v45_ZIP64_EXT); |
||||
| 783 | $versionExtractedBy = ($extractedOS << 8) | max($versionNeededToExtract, ZipVersion::v45_ZIP64_EXT); |
||||
| 784 | |||||
| 785 | // write zip64 end of central directory signature |
||||
| 786 | fwrite( |
||||
| 787 | $outStream, |
||||
| 788 | pack( |
||||
| 789 | 'V', |
||||
| 790 | // signature 4 bytes (0x06064b50) |
||||
| 791 | ZipConstants::ZIP64_END_CD |
||||
| 792 | ) |
||||
| 793 | ); |
||||
| 794 | // size of zip64 end of central |
||||
| 795 | // directory record 8 bytes |
||||
| 796 | fwrite($outStream, PackUtil::packLongLE(ZipConstants::ZIP64_END_OF_CD_LEN - 12)); |
||||
| 797 | fwrite( |
||||
| 798 | $outStream, |
||||
| 799 | pack( |
||||
| 800 | 'vvVV', |
||||
| 801 | // version made by 2 bytes |
||||
| 802 | $versionMadeBy & 0xFFFF, |
||||
| 803 | // version needed to extract 2 bytes |
||||
| 804 | $versionExtractedBy & 0xFFFF, |
||||
| 805 | // number of this disk 4 bytes |
||||
| 806 | 0, |
||||
| 807 | // number of the disk with the |
||||
| 808 | // start of the central directory 4 bytes |
||||
| 809 | 0 |
||||
| 810 | ) |
||||
| 811 | ); |
||||
| 812 | |||||
| 813 | fwrite( |
||||
| 814 | $outStream, |
||||
| 815 | // total number of entries in the |
||||
| 816 | // central directory on this disk 8 bytes |
||||
| 817 | PackUtil::packLongLE($cdEntriesCount) . |
||||
| 818 | // total number of entries in the |
||||
| 819 | // central directory 8 bytes |
||||
| 820 | PackUtil::packLongLE($cdEntriesCount) . |
||||
| 821 | // size of the central directory 8 bytes |
||||
| 822 | PackUtil::packLongLE($centralDirectorySize) . |
||||
| 823 | // offset of start of central |
||||
| 824 | // directory with respect to |
||||
| 825 | // the starting disk number 8 bytes |
||||
| 826 | PackUtil::packLongLE($centralDirectoryOffset) |
||||
| 827 | ); |
||||
| 828 | |||||
| 829 | // write zip64 end of central directory locator |
||||
| 830 | fwrite( |
||||
| 831 | $outStream, |
||||
| 832 | pack( |
||||
| 833 | 'VV', |
||||
| 834 | // zip64 end of central dir locator |
||||
| 835 | // signature 4 bytes (0x07064b50) |
||||
| 836 | ZipConstants::ZIP64_END_CD_LOC, |
||||
| 837 | // number of the disk with the |
||||
| 838 | // start of the zip64 end of |
||||
| 839 | // central directory 4 bytes |
||||
| 840 | 0 |
||||
| 841 | ) . |
||||
| 842 | // relative offset of the zip64 |
||||
| 843 | // end of central directory record 8 bytes |
||||
| 844 | PackUtil::packLongLE($zip64EndOfCentralDirectoryOffset) . |
||||
| 845 | // total number of disks 4 bytes |
||||
| 846 | pack('V', 1) |
||||
| 847 | ); |
||||
| 848 | } |
||||
| 849 | |||||
| 850 | $comment = $this->zipContainer->getArchiveComment(); |
||||
| 851 | $commentLength = $comment !== null ? \strlen($comment) : 0; |
||||
| 852 | |||||
| 853 | fwrite( |
||||
| 854 | $outStream, |
||||
| 855 | pack( |
||||
| 856 | 'VvvvvVVv', |
||||
| 857 | // end of central dir signature 4 bytes (0x06054b50) |
||||
| 858 | ZipConstants::END_CD, |
||||
| 859 | // number of this disk 2 bytes |
||||
| 860 | 0, |
||||
| 861 | // number of the disk with the |
||||
| 862 | // start of the central directory 2 bytes |
||||
| 863 | 0, |
||||
| 864 | // total number of entries in the |
||||
| 865 | // central directory on this disk 2 bytes |
||||
| 866 | $cdEntriesZip64 ? 0xffff : $cdEntriesCount, |
||||
| 867 | // total number of entries in |
||||
| 868 | // the central directory 2 bytes |
||||
| 869 | $cdEntriesZip64 ? 0xffff : $cdEntriesCount, |
||||
| 870 | // size of the central directory 4 bytes |
||||
| 871 | $cdSizeZip64 ? ZipConstants::ZIP64_MAGIC : $centralDirectorySize, |
||||
| 872 | // offset of start of central |
||||
| 873 | // directory with respect to |
||||
| 874 | // the starting disk number 4 bytes |
||||
| 875 | $cdOffsetZip64 ? ZipConstants::ZIP64_MAGIC : $centralDirectoryOffset, |
||||
| 876 | // .ZIP file comment length 2 bytes |
||||
| 877 | $commentLength |
||||
| 878 | ) |
||||
| 879 | ); |
||||
| 880 | |||||
| 881 | if ($comment !== null && $commentLength > 0) { |
||||
| 882 | // .ZIP file comment (variable size) |
||||
| 883 | fwrite($outStream, $comment); |
||||
| 884 | } |
||||
| 885 | } |
||||
| 886 | } |
||||
| 887 |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.