From 9e95551f5c3c8e650759841d69b72eb72f791e4d Mon Sep 17 00:00:00 2001 From: azjezz Date: Sat, 7 May 2022 18:53:55 +0100 Subject: [PATCH] feat(shell): introduce stream_unpack function Signed-off-by: azjezz --- CHANGELOG.md | 2 ++ docs/component/shell.md | 3 +- src/Psl/Internal/Loader.php | 1 + src/Psl/Shell/stream_unpack.php | 56 +++++++++++++++++++++++++++++++++ src/Psl/Shell/unpack.php | 27 ++-------------- 5 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 src/Psl/Shell/stream_unpack.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0014bd92..711b7387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,3 +71,5 @@ * **BC** - `$escape_arguments` argument of `Shell\execute` function has been removed. * introduced a new `Psl\Shell\ErrorOutputBehavior` enum * added a new `$error_output_behavior` argument to `Shell\execute` function, which can be used to return the command error output content, as well as the standard output content. +* introduced a new `Psl\Shell\unpack` function to unpack packed result of `Shell\execute` ( see `Psl\Shell\ErrorOutputBehavior::Packed` ). +* introduced a new `Psl\Shell\stream_unpack` function to unpack packed result of `Shell\execute` chunk by chunk, maintaing order ( see `Psl\Shell\ErrorOutputBehavior::Packed` ). diff --git a/docs/component/shell.md b/docs/component/shell.md index 7a18a086..52d91a93 100644 --- a/docs/component/shell.md +++ b/docs/component/shell.md @@ -13,7 +13,8 @@ #### `Functions` - [execute](./../../src/Psl/Shell/execute.php#L41) -- [unpack](./../../src/Psl/Shell/unpack.php#L20) +- [stream_unpack](./../../src/Psl/Shell/stream_unpack.php#L30) +- [unpack](./../../src/Psl/Shell/unpack.php#L16) #### `Enums` diff --git a/src/Psl/Internal/Loader.php b/src/Psl/Internal/Loader.php index e0af18b8..e31e992d 100644 --- a/src/Psl/Internal/Loader.php +++ b/src/Psl/Internal/Loader.php @@ -379,6 +379,7 @@ final class Loader 'Psl\Encoding\Hex\decode', 'Psl\Shell\execute', 'Psl\Shell\unpack', + 'Psl\Shell\stream_unpack', 'Psl\Shell\Internal\escape_argument', 'Psl\Html\encode', 'Psl\Html\encode_special_characters', diff --git a/src/Psl/Shell/stream_unpack.php b/src/Psl/Shell/stream_unpack.php new file mode 100644 index 00000000..6701ffcb --- /dev/null +++ b/src/Psl/Shell/stream_unpack.php @@ -0,0 +1,56 @@ + Generator where the key is either 1 ( representing the standard output ), + * or 2 ( representing the standard error output ), and the value is the output chunk. + * + * Example: + * + * Shell\stream_unpack( + * Shell\execute('php', ['-r', 'fwrite(STDOUT, "a"); fwrite(STDERR, "b"); fwrite(STDOUT, "c");'], null, [], ErrorOutputBehavior::Packed), + * ); + * => Generator(1 => "a", 2 => "b", 1 => "c") + */ +function stream_unpack(string $content): Generator +{ + while ($content !== '') { + if (Str\Byte\length($content) < 5) { + throw new Exception\InvalidArgumentException('$content contains an invalid header value.'); + } + + $headers = byte_unpack('C1type/N1size', Str\Byte\slice($content, 0, 5)); + /** @var int<0, max> $type */ + $type = (int) $headers['type']; + /** @var int<0, max> $size */ + $size = (int) $headers['size']; + + if ($size > (Str\Byte\length($content) - 5)) { + throw new Exception\InvalidArgumentException('$content contains an invalid header value.'); + } + + $chunk = Str\Byte\slice($content, 5, $size); + $content = Str\Byte\slice($content, $size + 5); + + if ($type === 1 || $type === 2) { + yield $type => $chunk; + } else { + throw new Exception\InvalidArgumentException('$content contains an invalid header value.'); + } + } +} diff --git a/src/Psl/Shell/unpack.php b/src/Psl/Shell/unpack.php index a304c9f9..e2f7928c 100644 --- a/src/Psl/Shell/unpack.php +++ b/src/Psl/Shell/unpack.php @@ -4,10 +4,6 @@ namespace Psl\Shell; -use Psl\Str; - -use function unpack as byte_unpack; - /** * Unpack the result of `Shell\execute()` when using `ErrorOutputBehavior::Packed` error output behavior. * @@ -20,30 +16,11 @@ function unpack(string $content): array { $result = ['', '']; - while ($content !== '') { - if (Str\Byte\length($content) < 5) { - throw new Exception\InvalidArgumentException('$content contains an invalid header value.'); - } - - $headers = byte_unpack('C1type/N1size', Str\Byte\slice($content, 0, 5)); - /** @var int<0, max> $type */ - $type = (int) $headers['type']; - /** @var int<0, max> $size */ - $size = (int) $headers['size']; - - if ($size > (Str\Byte\length($content) - 5)) { - throw new Exception\InvalidArgumentException('$content contains an invalid header value.'); - } - - $chunk = Str\Byte\slice($content, 5, $size); - $content = Str\Byte\slice($content, $size + 5); - + foreach (stream_unpack($content) as $type => $chunk) { if ($type === 1) { $result[0] .= $chunk; - } elseif ($type === 2) { - $result[1] .= $chunk; } else { - throw new Exception\InvalidArgumentException('$content contains an invalid header value.'); + $result[1] .= $chunk; } }