This commit changes the api of the stream classes, since indication of success or failure is no longer necessary.
122 lines
3.4 KiB
PHP
122 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Nih\CommandBuilder;
|
|
|
|
use InvalidArgumentException;
|
|
use Override;
|
|
use Stringable;
|
|
|
|
/**
|
|
* Describes what to do with a standard I/O stream for a child process when
|
|
* passed to the stdin, stdout, and stderr methods of {@see Command}.
|
|
*
|
|
* @psalm-suppress UnusedClass
|
|
*/
|
|
abstract class Stdio
|
|
{
|
|
/**
|
|
* The child inherits from the corresponding parent descriptor.
|
|
*/
|
|
public static function inherit(): StdioInterface
|
|
{
|
|
return new class implements StdioInterface {
|
|
#[Override]
|
|
public function getDescriptionSpec(int $fd): mixed
|
|
{
|
|
return match ($fd) {
|
|
0 => STDIN,
|
|
1 => STDOUT,
|
|
2 => STDERR,
|
|
default => ['php://fd/' . $fd, 'w+'],
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* A new pipe should be arranged to connect the parent and child processes.
|
|
*/
|
|
public static function piped(): StdioInterface
|
|
{
|
|
return new class implements StdioInterface {
|
|
#[Override]
|
|
public function getDescriptionSpec(int $fd): mixed
|
|
{
|
|
return match ($fd) {
|
|
0 => ['pipe', 'r'],
|
|
1, 2 => ['pipe', 'w'],
|
|
default => ['pipe', 'w+'],
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* This stream will be ignored. This is the equivalent of attaching the
|
|
* stream to `/dev/null`.
|
|
*/
|
|
public static function null(): StdioInterface
|
|
{
|
|
return new class implements StdioInterface {
|
|
#[Override]
|
|
public function getDescriptionSpec(int $fd): mixed
|
|
{
|
|
// TODO: Support windows (I think you just write to `nul` in any
|
|
// directory?)
|
|
return match ($fd) {
|
|
0 => ['file', '/dev/null', 'r'],
|
|
1, 2 => ['file', '/dev/null', 'w'],
|
|
default => ['file', '/dev/null', 'w+'],
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Like piped, but instead of capturing the stream into a handle, read
|
|
* and/or write from/to a file.
|
|
*/
|
|
public static function file(string|Stringable $filename, string $mode): StdioInterface
|
|
{
|
|
return new class((string) $filename, $mode) implements StdioInterface {
|
|
public function __construct(
|
|
private string $filename,
|
|
private string $mode,
|
|
) {}
|
|
|
|
#[Override]
|
|
public function getDescriptionSpec(int $fd): mixed
|
|
{
|
|
return ['file', $this->filename, $this->mode];
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Like piped, but instead of capturing the stream into a handle, read
|
|
* and/or write from/to a stream resource.
|
|
*
|
|
* @param resource $stream
|
|
*
|
|
* @throws InvalidArgumentException When stream is not a stream resource
|
|
*/
|
|
public static function stream($stream): StdioInterface
|
|
{
|
|
if (get_resource_type($stream) !== 'stream') {
|
|
throw new InvalidArgumentException('resource is not a stream');
|
|
}
|
|
|
|
return new class($stream) implements StdioInterface {
|
|
public function __construct(private mixed $stream) {}
|
|
|
|
#[Override]
|
|
public function getDescriptionSpec(int $fd): mixed
|
|
{
|
|
return $this->stream;
|
|
}
|
|
};
|
|
}
|
|
}
|