Skip to content

Commit

Permalink
Fixes #44: Compress sitemap with a single gzip member per file
Browse files Browse the repository at this point in the history
  • Loading branch information
terales authored and samdark committed Aug 17, 2017
1 parent c650baf commit 01c653c
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 5 deletions.
72 changes: 67 additions & 5 deletions Sitemap.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ class Sitemap
*/
private $writer;

/**
* @var resource for writable incremental deflate context
*/
private $deflateContext;

/**
* @var resource for php://temp stream
*/
private $tempFile;

/**
* @param string $filePath path of the file to write to
* @throws \InvalidArgumentException
Expand Down Expand Up @@ -136,7 +146,7 @@ private function finishFile()
if ($this->writer !== null) {
$this->writer->endElement();
$this->writer->endDocument();
$this->flush();
$this->flush(true);
}
}

Expand All @@ -150,14 +160,66 @@ public function write()

/**
* Flushes buffer into file
* @param bool $finishFile Pass true to close the file to write to, used only when useGzip is true
*/
private function flush()
private function flush($finishFile = false)
{
$filePath = $this->getCurrentFilePath();
if ($this->useGzip) {
$filePath = 'compress.zlib://' . $filePath;
$this->flushGzip($finishFile);
return;
}
file_put_contents($this->getCurrentFilePath(), $this->writer->flush(true), FILE_APPEND);
}

/**
* Decides how to flush buffer into compressed file
* @param bool $finishFile Pass true to close the file to write to
*/
private function flushGzip($finishFile = false) {
if (function_exists('deflate_init') && function_exists('deflate_add')) {
$this->flushWithIncrementalDeflate($finishFile);
return;
}
$this->flushWithTempFileFallback($finishFile);
}

/**
* Flushes buffer into file with incremental deflating data, available in php 7.0+
* @param bool $finishFile Pass true to write last chunk with closing headers
*/
private function flushWithIncrementalDeflate($finishFile = false) {
$flushMode = $finishFile ? ZLIB_FINISH : ZLIB_NO_FLUSH;

if (empty($this->deflateContext)) {
$this->deflateContext = deflate_init(ZLIB_ENCODING_GZIP);
}

$compressedChunk = deflate_add($this->deflateContext, $this->writer->flush(true), $flushMode);
file_put_contents($this->getCurrentFilePath(), $compressedChunk, FILE_APPEND);

if ($finishFile) {
$this->deflateContext = null;
}
}

/**
* Flushes buffer into temporary stream and compresses stream into a file on finish
* @param bool $finishFile Pass true to compress temporary stream into desired file
*/
private function flushWithTempFileFallback($finishFile = false) {
if (empty($this->tempFile) || !is_resource($this->tempFile)) {
$this->tempFile = fopen('php://temp/', 'w');
}

fwrite($this->tempFile, $this->writer->flush(true));

if ($finishFile) {
$file = fopen('compress.zlib://' . $this->getCurrentFilePath(), 'w');
rewind($this->tempFile);
stream_copy_to_stream($this->tempFile, $file);
fclose($file);
fclose($this->tempFile);
}
file_put_contents($filePath, $this->writer->flush(true), FILE_APPEND);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions tests/SitemapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ protected function assertIsValidSitemap($fileName)
$this->assertTrue($xml->schemaValidate(__DIR__ . '/sitemap.xsd'));
}

protected function assertIsOneMemberGzipFile($fileName)
{
$gzipMemberStartSequence = pack('H*', '1f8b08');
$content = file_get_contents($fileName);
$isOneMemberGzipFile = (strpos($content, $gzipMemberStartSequence, 1) === false);
$this->assertTrue($isOneMemberGzipFile, "There are more than one gzip member in $fileName");
}

public function testWritingFile()
{
$fileName = __DIR__ . '/sitemap_regular.xml';
Expand Down Expand Up @@ -129,6 +137,7 @@ public function testWritingFileGzipped()
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$this->assertEquals('application/x-gzip', $finfo->file($fileName));
$this->assertIsValidSitemap('compress.zlib://' . $fileName);
$this->assertIsOneMemberGzipFile($fileName);

unlink($fileName);
}
Expand Down Expand Up @@ -161,6 +170,7 @@ public function testMultipleFilesGzipped()
$this->assertTrue(file_exists($expectedFile), "$expectedFile does not exist!");
$this->assertEquals('application/x-gzip', $finfo->file($expectedFile));
$this->assertIsValidSitemap('compress.zlib://' . $expectedFile);
$this->assertIsOneMemberGzipFile($expectedFile);
unlink($expectedFile);
}

Expand Down

0 comments on commit 01c653c

Please sign in to comment.