1
0
mirror of https://github.com/danog/parallel.git synced 2024-12-02 17:52:14 +01:00
parallel/lib/Sync/ChannelParser.php
2017-06-07 23:33:13 -05:00

84 lines
2.4 KiB
PHP

<?php
namespace Amp\Parallel\Sync;
use Amp\CallableMaker;
use Amp\Parser\Parser;
class ChannelParser extends Parser {
use CallableMaker;
const HEADER_LENGTH = 5;
/**
* @param callable(mixed $data) Callback invoked when data is parsed.
*/
public function __construct(callable $callback) {
parent::__construct(self::parser($callback, self::callableFromStaticMethod("errorHandler")));
}
/**
* @param mixed $data Data to encode to send over a channel.
*
* @return string Encoded data that can be parsed by this class.
*
* @throws \Amp\Parallel\Sync\SerializationException
*/
public function encode($data): string {
try {
$data = \serialize($data);
} catch (\Throwable $exception) {
throw new SerializationException(
"The given data cannot be sent because it is not serializable.", $exception
);
}
return \pack("CL", 0, \strlen($data)) . $data;
}
/**
* @param \SplQueue $queue
* @param callable $errorHandler
*
* @return \Generator
*
* @throws \Amp\Parallel\Sync\ChannelException
* @throws \Amp\Parallel\Sync\SerializationException
*/
private static function parser(callable $callback, callable $errorHandler): \Generator {
while (true) {
$data = \unpack("Cprefix/Llength", yield self::HEADER_LENGTH);
if ($data["prefix"] !== 0) {
throw new ChannelException("Invalid header received");
}
$data = yield $data["length"];
\set_error_handler($errorHandler);
// Attempt to unserialize the received data.
try {
$data = \unserialize($data);
} catch (\Throwable $exception) {
throw new SerializationException("Exception thrown when unserializing data", $exception);
} finally {
\restore_error_handler();
}
$callback($data);
}
}
private static function errorHandler($errno, $errstr, $errfile, $errline) {
if ($errno & \error_reporting()) {
throw new ChannelException(\sprintf(
'Received corrupted data. Errno: %d; %s in file %s on line %d',
$errno,
$errstr,
$errfile,
$errline
));
}
}
}