CompressorInterface
- methods for determining whether to compress, compress, determine whether to unpack and decompress. The class constructor will take a threshold value in bytes, starting from which compression is enabled. This interface will allow you to implement your favorite compression algorithm yourself, for example, a lamp WinRAR.
AbstractCompressor
class so as not to duplicate it in each of the implementations.
abstract class AbstractCompressor implements CompressorInterface { const BYTE_CHARSET = 'US-ASCII'; protected $threshold; public function __construct(int $threshold) { $this->threshold = $threshold; } public function shouldCompress($data): bool { if (!\is_string($data)) { return false; } return \mb_strlen($data, self::BYTE_CHARSET) > $this->threshold; } }
mb_strlen
to overcome possible problems with mbstring.func_overload
and one-byte encoding to prevent attempts to automatically detect encoding from data.
\x1f\x8b\x08"
(from them we will understand that the string must be unpacked).
class GzipCompressor extends AbstractCompressor { public function compress(string $data): string { $compressed = @\gzencode($data); if ($compressed === false) { throw new CompressorException('Compression failed'); } return $compressed; } public function isCompressed($data): bool { if (!\is_string($data)) { return false; } return 0 === \mb_strpos($data, "\x1f" . "\x8b" . "\x08", 0, self::BYTE_CHARSET); } public function decompress(string $data): string { $decompressed = @\gzdecode($data); if ($decompressed === false) { throw new CompressorException('Decompression failed'); } return $decompressed; } }
class CompressProcessor implements ProcessorInterface { private $compressor; public function __construct(CompressorInterface $compressor) { $this->compressor = $compressor; } public function process(CommandInterface $command) { if ($command instanceof CompressibleCommandInterface) { $command->setCompressor($this->compressor); if ($command instanceof ArgumentsCompressibleCommandInterface) { $arguments = $command->compressArguments($command->getArguments()); $command->setRawArguments($arguments); } } } }
CompressibleCommandInterface
- shows that the command supports compression and describes a method for the command to get the implementation of the CompressorInterface
.
ArgumentsCompressibleCommandInterface
- the successor of the first interface, shows that the command supports the compression of arguments.
\Predis\Profile\RedisProfile::createCommand()
):
public function createCommand($commandID, array $arguments = array()) { // $command = new $commandClass(); $command->setArguments($arguments); if (isset($this->processor)) { $this->processor->process($command); } return $command; }
GzipCompressor
in our case, but it could be some other mechanism that needs to be initialized outside the predis, for example, an encryption system or a mechanism for signing data). Because of this, an interface appeared with a method for compressing arguments.
CommandInterface::parseResponse()
, which is not entirely correct.
use CompressibleCommandTrait; use CompressArgumentsHelperTrait; public function compressArguments(array $arguments): array { $this->compressArgument($arguments, 1); return $arguments; }
use CompressibleCommandTrait; public function parseResponse($data) { if (!$this->compressor->isCompressed($data)) { return $data; } return $this->compressor->decompress($data); }
composer require b1rdex/predis-compressible
use B1rdex\PredisCompressible\CompressProcessor; use B1rdex\PredisCompressible\Compressor\GzipCompressor; use B1rdex\PredisCompressible\Command\StringGet; use B1rdex\PredisCompressible\Command\StringSet; use B1rdex\PredisCompressible\Command\StringSetExpire; use B1rdex\PredisCompressible\Command\StringSetPreserve; use Predis\Client; use Predis\Configuration\OptionsInterface; use Predis\Profile\Factory; use Predis\Profile\RedisProfile; // strings with length > 2048 bytes will be compressed $compressor = new GzipCompressor(2048); $client = new Client([], [ 'profile' => function (OptionsInterface $options) use ($compressor) { $profile = Factory::getDefault(); if ($profile instanceof RedisProfile) { $processor = new CompressProcessor($compressor); $profile->setProcessor($processor); $profile->defineCommand('SET', StringSet::class); $profile->defineCommand('SETEX', StringSetExpire::class); $profile->defineCommand('SETNX', StringSetPreserve::class); $profile->defineCommand('GET', StringGet::class); } return $profile; }, ]);
Source: https://habr.com/ru/post/331474/