2017-05-08 08:51:52 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Amp\ByteStream;
|
|
|
|
|
|
|
|
use Amp\Promise;
|
|
|
|
|
2017-05-25 17:51:25 +02:00
|
|
|
/**
|
|
|
|
* Allows compression of output streams using Zlib.
|
|
|
|
*/
|
2017-05-22 14:35:13 +02:00
|
|
|
final class ZlibOutputStream implements OutputStream {
|
2017-05-08 08:51:52 +02:00
|
|
|
private $destination;
|
2017-05-14 14:40:20 +02:00
|
|
|
private $encoding;
|
2017-05-22 14:35:13 +02:00
|
|
|
private $options;
|
2017-05-08 08:51:52 +02:00
|
|
|
private $resource;
|
|
|
|
|
2017-05-22 14:35:13 +02:00
|
|
|
/**
|
2017-05-25 17:51:25 +02:00
|
|
|
* @param OutputStream $destination Output stream to write the compressed data to.
|
|
|
|
* @param int $encoding Compression encoding to use, see `deflate_init()`.
|
|
|
|
* @param array $options Compression options to use, see `deflate_init()`.
|
2017-05-22 14:35:13 +02:00
|
|
|
*
|
2017-05-25 17:51:25 +02:00
|
|
|
* @throws StreamException If an invalid encoding or invalid options have been passed.
|
2017-05-22 14:35:13 +02:00
|
|
|
*
|
|
|
|
* @see http://php.net/manual/en/function.deflate-init.php
|
|
|
|
*/
|
|
|
|
public function __construct(OutputStream $destination, int $encoding, array $options = []) {
|
2017-05-08 08:51:52 +02:00
|
|
|
$this->destination = $destination;
|
2017-05-14 14:40:20 +02:00
|
|
|
$this->encoding = $encoding;
|
2017-05-22 14:35:13 +02:00
|
|
|
$this->options = $options;
|
2017-05-22 14:44:36 +02:00
|
|
|
$this->resource = @\deflate_init($encoding, $options);
|
2017-05-08 08:51:52 +02:00
|
|
|
|
|
|
|
if ($this->resource === false) {
|
|
|
|
throw new StreamException("Failed initializing deflate context");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-25 17:51:25 +02:00
|
|
|
/** @inheritdoc */
|
2017-05-08 08:51:52 +02:00
|
|
|
public function write(string $data): Promise {
|
|
|
|
if ($this->resource === null) {
|
|
|
|
throw new ClosedException("The stream has already been closed");
|
|
|
|
}
|
|
|
|
|
|
|
|
$compressed = \deflate_add($this->resource, $data, \ZLIB_SYNC_FLUSH);
|
|
|
|
|
|
|
|
if ($compressed === false) {
|
|
|
|
throw new StreamException("Failed adding data to deflate context");
|
|
|
|
}
|
|
|
|
|
|
|
|
$promise = $this->destination->write($compressed);
|
|
|
|
$promise->onResolve(function ($error) {
|
|
|
|
if ($error) {
|
|
|
|
$this->close();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return $promise;
|
|
|
|
}
|
|
|
|
|
2017-05-25 17:51:25 +02:00
|
|
|
/** @inheritdoc */
|
2017-05-08 08:51:52 +02:00
|
|
|
public function end(string $finalData = ""): Promise {
|
|
|
|
if ($this->resource === null) {
|
|
|
|
throw new ClosedException("The stream has already been closed");
|
|
|
|
}
|
|
|
|
|
|
|
|
$compressed = \deflate_add($this->resource, $finalData, \ZLIB_FINISH);
|
|
|
|
|
|
|
|
if ($compressed === false) {
|
|
|
|
throw new StreamException("Failed adding data to deflate context");
|
|
|
|
}
|
|
|
|
|
|
|
|
$promise = $this->destination->write($compressed);
|
|
|
|
$promise->onResolve(function ($error) {
|
|
|
|
if ($error) {
|
|
|
|
$this->close();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return $promise;
|
|
|
|
}
|
|
|
|
|
2017-05-25 17:51:25 +02:00
|
|
|
/** @internal */
|
|
|
|
private function close() {
|
2017-05-08 08:51:52 +02:00
|
|
|
$this->resource = null;
|
2017-05-08 09:30:11 +02:00
|
|
|
$this->destination = null;
|
2017-05-08 08:51:52 +02:00
|
|
|
}
|
2017-05-14 14:40:20 +02:00
|
|
|
|
2017-05-25 17:51:25 +02:00
|
|
|
/**
|
|
|
|
* Gets the used compression encoding.
|
|
|
|
*
|
|
|
|
* @return int Encoding specified on construction time.
|
|
|
|
*/
|
2017-05-14 14:40:20 +02:00
|
|
|
public function getEncoding(): int {
|
|
|
|
return $this->encoding;
|
|
|
|
}
|
2017-05-22 14:35:13 +02:00
|
|
|
|
2017-05-25 17:51:25 +02:00
|
|
|
/**
|
|
|
|
* Gets the used compression options.
|
|
|
|
*
|
|
|
|
* @return array Options array passed on construction time.
|
|
|
|
*/
|
2017-05-22 14:35:13 +02:00
|
|
|
public function getOptions(): array {
|
|
|
|
return $this->options;
|
|
|
|
}
|
2017-05-08 08:51:52 +02:00
|
|
|
}
|