fix: env handling and inheritance
This commit is contained in:
113
src/Command.php
113
src/Command.php
@@ -10,15 +10,14 @@ use ValueError;
|
|||||||
/**
|
/**
|
||||||
* A process builder, providing fine-grained control over how a new process
|
* A process builder, providing fine-grained control over how a new process
|
||||||
* should be spawned.
|
* should be spawned.
|
||||||
*
|
|
||||||
* TODO: Wonky env handling.
|
|
||||||
*/
|
*/
|
||||||
final class Command implements Stringable
|
final class Command implements Stringable
|
||||||
{
|
{
|
||||||
public readonly string $program;
|
public readonly string $program;
|
||||||
|
|
||||||
private array $args;
|
private array $args;
|
||||||
private ?array $envs = null;
|
private ?array $environment = null;
|
||||||
|
private bool $environmentInherit = true;
|
||||||
private ?string $cwd = null;
|
private ?string $cwd = null;
|
||||||
|
|
||||||
private ?Stdio $stdin = null;
|
private ?Stdio $stdin = null;
|
||||||
@@ -26,7 +25,17 @@ final class Command implements Stringable
|
|||||||
private ?Stdio $stderr = null;
|
private ?Stdio $stderr = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Constructs a new Command for launching the program at path program, with
|
||||||
|
* the following default configuration:
|
||||||
*
|
*
|
||||||
|
* - No arguments to the program
|
||||||
|
* - Inherit the current process's environment
|
||||||
|
* - Inherit the current process's working directory
|
||||||
|
* - Inherit stdin/stdout/stderr for spawn or status, but create pipes for
|
||||||
|
* output
|
||||||
|
*
|
||||||
|
* Builder methods are provided to change these defaults and otherwise
|
||||||
|
* configure the process.
|
||||||
*/
|
*/
|
||||||
public function __construct(string $program)
|
public function __construct(string $program)
|
||||||
{
|
{
|
||||||
@@ -40,6 +49,22 @@ final class Command implements Stringable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an argument to pass to the program.
|
* Adds an argument to pass to the program.
|
||||||
|
*
|
||||||
|
* Only one argument can be passed per use. So instead of:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* $command->arg('-C /path/to/repo');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* usage would be:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* $command
|
||||||
|
* ->arg('-C')
|
||||||
|
* ->arg('/path/to/repo');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* To pass multiple arguments see {@see Command::args()}.
|
||||||
*/
|
*/
|
||||||
public function arg(string|Stringable $arg): static
|
public function arg(string|Stringable $arg): static
|
||||||
{
|
{
|
||||||
@@ -50,6 +75,8 @@ final class Command implements Stringable
|
|||||||
/**
|
/**
|
||||||
* Adds multiple arguments to pass to the program.
|
* Adds multiple arguments to pass to the program.
|
||||||
*
|
*
|
||||||
|
* To pass a single argument see {@see Command::arg()}.
|
||||||
|
*
|
||||||
* @param iterable<string|Stringable> $args
|
* @param iterable<string|Stringable> $args
|
||||||
*/
|
*/
|
||||||
public function args(iterable $args): static
|
public function args(iterable $args): static
|
||||||
@@ -63,22 +90,45 @@ final class Command implements Stringable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts or updates an explicit environment variable mapping.
|
* Inserts or updates an explicit environment variable mapping.
|
||||||
|
*
|
||||||
|
* This method allows you to add an environment variable mapping to the
|
||||||
|
* spawned process or overwrite a previously set value. You can use
|
||||||
|
* {@see Command::envs()} to set multiple environment variables
|
||||||
|
* simultaneously.
|
||||||
|
*
|
||||||
|
* Child processes will inherit environment variables from their parent
|
||||||
|
* process by default. Environment variables explicitly set using
|
||||||
|
* {@see Command::env()} take precedence over inherited variables. You can
|
||||||
|
* disable environment variable inheritance entirely using
|
||||||
|
* {@see Command::envClear()} or for a single key using
|
||||||
|
* {@see Command::envRemove()}.
|
||||||
*/
|
*/
|
||||||
public function env(string $key, string|Stringable $val): static
|
public function env(string $key, string|Stringable $val): static
|
||||||
{
|
{
|
||||||
$this->envs[$key] = $val;
|
$this->environment[$key] = $val;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts or updates multiple environment variable mappings.
|
* Inserts or updates multiple environment variable mappings.
|
||||||
*
|
*
|
||||||
|
* This method allows you to add multiple environment variable mappings to
|
||||||
|
* the spawned process or overwrite previously set values. You can use
|
||||||
|
* {@see Command::env()} to set a single environment variable.
|
||||||
|
*
|
||||||
|
* Child processes will inherit environment variables from their parent
|
||||||
|
* process by default. Environment variables explicitly set using
|
||||||
|
* {@see Command::envs()} take precedence over inherited variables. You can
|
||||||
|
* disable environment variable inheritance entirely using
|
||||||
|
* {@see Command::envClear()} or for a single key using
|
||||||
|
* {@see Command::envRemove()}.
|
||||||
|
*
|
||||||
* @param iterable<string, string|Stringable> $vars
|
* @param iterable<string, string|Stringable> $vars
|
||||||
*/
|
*/
|
||||||
public function envs(iterable $vars): static
|
public function envs(iterable $vars): static
|
||||||
{
|
{
|
||||||
foreach ($vars as $key => $val) {
|
foreach ($vars as $key => $val) {
|
||||||
$this->envs[$key] = (string) $val;
|
$this->environment[$key] = (string) $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@@ -87,27 +137,42 @@ final class Command implements Stringable
|
|||||||
/**
|
/**
|
||||||
* Removes an explicitly set environment variable and prevents inheriting it
|
* Removes an explicitly set environment variable and prevents inheriting it
|
||||||
* from a parent process.
|
* from a parent process.
|
||||||
|
*
|
||||||
|
* This method will remove the explicit value of an environment variable set
|
||||||
|
* via {@see Command::env()} or {@see Command::envs()}. In addition, it will
|
||||||
|
* prevent the spawned child process from inheriting that environment
|
||||||
|
* variable from its parent process.
|
||||||
|
*
|
||||||
|
* After calling {@see Command::envRemove()}, the value associated with its
|
||||||
|
* key from {@see Command::getEnvs()} will be `NULL`.
|
||||||
|
*
|
||||||
|
* To clear all explicitly set environment variables and disable all
|
||||||
|
* environment variable inheritance, you can use {@see Command::envClear()}.
|
||||||
*/
|
*/
|
||||||
public function envRemove(string $key): static
|
public function envRemove(string $key): static
|
||||||
{
|
{
|
||||||
if (is_null($this->envs)) {
|
$this->environment[$key] = null;
|
||||||
$this->envs = getenv();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->envs[$key])) {
|
|
||||||
unset($this->envs[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all explicitly set environment variables and prevents inheriting
|
* Clears all explicitly set environment variables and prevents inheriting
|
||||||
* any parent process environment variables.
|
* any parent process environment variables.
|
||||||
|
*
|
||||||
|
* This method will remove all explicitly added environment variables set
|
||||||
|
* via {@see Command::env()} or {@see Command::envs()}. In addition, it will
|
||||||
|
* prevent the spawned child process from inheriting any environment
|
||||||
|
* variable from its parent process.
|
||||||
|
*
|
||||||
|
* After calling {@see Command::envClear()}, the array from
|
||||||
|
* {@see Command::getEnvs()} will be empty.
|
||||||
|
*
|
||||||
|
* You can use {@see Command::envRemove()} to clear a single mapping.
|
||||||
*/
|
*/
|
||||||
public function envClear(): static
|
public function envClear(): static
|
||||||
{
|
{
|
||||||
$this->envs = [];
|
$this->environmentInherit = false;
|
||||||
|
$this->environment = [];
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,10 +331,17 @@ final class Command implements Stringable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the environment variables set for the child process.
|
* Returns the environment variables set for the child process.
|
||||||
|
*
|
||||||
|
* Environment variables explicitly set using {@see Command::env(),
|
||||||
|
* {@see Command::envs()}, and {@see Command::envRemove} can be retrieved
|
||||||
|
* with this method.
|
||||||
|
*
|
||||||
|
* Note that this output does not include environment variables inherited
|
||||||
|
* from the parent process.
|
||||||
*/
|
*/
|
||||||
public function getEnvs(): array
|
public function getEnvs(): array
|
||||||
{
|
{
|
||||||
return $this->envs;
|
return $this->environment ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -310,7 +382,16 @@ final class Command implements Stringable
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
$proc = proc_open($command, $descriptorSpec, $pipes, $this->cwd, $this->envs);
|
$environment = $this->environment;
|
||||||
|
if (is_array($environment) && $this->environmentInherit) {
|
||||||
|
foreach (getenv() as $key => $val) {
|
||||||
|
if (!array_key_exists($key, $environment)) {
|
||||||
|
$environment[$key] = $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$proc = proc_open($command, $descriptorSpec, $pipes, $this->cwd, $environment);
|
||||||
if ($proc === false) {
|
if ($proc === false) {
|
||||||
throw new CommandException(sprintf(
|
throw new CommandException(sprintf(
|
||||||
'Program "%s" failed to start',
|
'Program "%s" failed to start',
|
||||||
|
|||||||
Reference in New Issue
Block a user