feature #19053 [Process] Allow inheriting env vars instead of replacing them (nicolas-grekas)
This PR was merged into the 3.2-dev branch.
Discussion
----------
[Process] Allow inheriting env vars instead of replacing them
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #18796
| License | MIT
| Doc PR | -
Commits
-------
524508c
[Process] Allow merging env vars instead of replacing them
This commit is contained in:
commit
161d629c3f
@ -73,6 +73,7 @@ class Process implements \IteratorAggregate
|
||||
private $incrementalErrorOutputOffset = 0;
|
||||
private $tty;
|
||||
private $pty;
|
||||
private $inheritEnv = false;
|
||||
|
||||
private $useFileHandles = false;
|
||||
/** @var PipesInterface */
|
||||
@ -267,9 +268,22 @@ class Process implements \IteratorAggregate
|
||||
$descriptors = $this->getDescriptors();
|
||||
|
||||
$commandline = $this->commandline;
|
||||
$envline = '';
|
||||
|
||||
if (null !== $this->env && $this->inheritEnv) {
|
||||
if ('\\' === DIRECTORY_SEPARATOR && !empty($this->options['bypass_shell']) && !$this->enhanceWindowsCompatibility) {
|
||||
throw new LogicException('The "bypass_shell" option must be false to inherit environment variables while enhanced Windows compatibility is off');
|
||||
}
|
||||
$env = '\\' === DIRECTORY_SEPARATOR ? '(SET %s)&&' : 'export %s;';
|
||||
foreach ($this->env as $k => $v) {
|
||||
$envline .= sprintf($env, ProcessUtils::escapeArgument("$k=$v"));
|
||||
}
|
||||
$env = null;
|
||||
} else {
|
||||
$env = $this->env;
|
||||
}
|
||||
if ('\\' === DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) {
|
||||
$commandline = 'cmd /V:ON /E:ON /D /C "('.$commandline.')';
|
||||
$commandline = 'cmd /V:ON /E:ON /D /C "('.$envline.$commandline.')';
|
||||
foreach ($this->processPipes->getFiles() as $offset => $filename) {
|
||||
$commandline .= ' '.$offset.'>'.ProcessUtils::escapeArgument($filename);
|
||||
}
|
||||
@ -283,15 +297,17 @@ class Process implements \IteratorAggregate
|
||||
$descriptors[3] = array('pipe', 'w');
|
||||
|
||||
// See https://unix.stackexchange.com/questions/71205/background-process-pipe-input
|
||||
$commandline = '{ ('.$this->commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
|
||||
$commandline = $envline.'{ ('.$this->commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
|
||||
$commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code';
|
||||
|
||||
// Workaround for the bug, when PTS functionality is enabled.
|
||||
// @see : https://bugs.php.net/69442
|
||||
$ptsWorkaround = fopen(__FILE__, 'r');
|
||||
} elseif ('' !== $envline) {
|
||||
$commandline = $envline.$commandline;
|
||||
}
|
||||
|
||||
$this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options);
|
||||
$this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $env, $this->options);
|
||||
|
||||
if (!is_resource($this->process)) {
|
||||
throw new RuntimeException('Unable to launch a new process.');
|
||||
@ -1197,6 +1213,30 @@ class Process implements \IteratorAggregate
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether environment variables will be inherited or not.
|
||||
*
|
||||
* @param bool $inheritEnv
|
||||
*
|
||||
* @return self The current Process instance
|
||||
*/
|
||||
public function inheritEnvironmentVariables($inheritEnv = true)
|
||||
{
|
||||
$this->inheritEnv = (bool) $inheritEnv;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether environment variables will be inherited or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function areEnvironmentVariablesInherited()
|
||||
{
|
||||
return $this->inheritEnv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a check between the timeout definition and the time the process started.
|
||||
*
|
||||
|
@ -267,14 +267,11 @@ class ProcessBuilder
|
||||
$arguments = array_merge($this->prefix, $this->arguments);
|
||||
$script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $arguments));
|
||||
|
||||
$process = new Process($script, $this->cwd, $this->env, $this->input, $this->timeout, $options);
|
||||
|
||||
if ($this->inheritEnv) {
|
||||
$env = array_replace($_ENV, $_SERVER, $this->env);
|
||||
} else {
|
||||
$env = $this->env;
|
||||
$process->inheritEnvironmentVariables();
|
||||
}
|
||||
|
||||
$process = new Process($script, $this->cwd, $env, $this->input, $this->timeout, $options);
|
||||
|
||||
if ($this->outputDisabled) {
|
||||
$process->disableOutput();
|
||||
}
|
||||
|
@ -17,17 +17,11 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testInheritEnvironmentVars()
|
||||
{
|
||||
$_ENV['MY_VAR_1'] = 'foo';
|
||||
|
||||
$proc = ProcessBuilder::create()
|
||||
->add('foo')
|
||||
->getProcess();
|
||||
|
||||
unset($_ENV['MY_VAR_1']);
|
||||
|
||||
$env = $proc->getEnv();
|
||||
$this->assertArrayHasKey('MY_VAR_1', $env);
|
||||
$this->assertEquals('foo', $env['MY_VAR_1']);
|
||||
$this->assertTrue($proc->areEnvironmentVariablesInherited());
|
||||
}
|
||||
|
||||
public function testAddEnvironmentVariables()
|
||||
@ -46,22 +40,7 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
|
||||
;
|
||||
|
||||
$this->assertSame($env, $proc->getEnv());
|
||||
}
|
||||
|
||||
public function testProcessShouldInheritAndOverrideEnvironmentVars()
|
||||
{
|
||||
$_ENV['MY_VAR_1'] = 'foo';
|
||||
|
||||
$proc = ProcessBuilder::create()
|
||||
->setEnv('MY_VAR_1', 'bar')
|
||||
->add('foo')
|
||||
->getProcess();
|
||||
|
||||
unset($_ENV['MY_VAR_1']);
|
||||
|
||||
$env = $proc->getEnv();
|
||||
$this->assertArrayHasKey('MY_VAR_1', $env);
|
||||
$this->assertEquals('bar', $env['MY_VAR_1']);
|
||||
$this->assertFalse($proc->areEnvironmentVariablesInherited());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1357,6 +1357,40 @@ class ProcessTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame('456', $p2->getOutput());
|
||||
}
|
||||
|
||||
public function testInheritEnvEnabled()
|
||||
{
|
||||
$process = $this->getProcess(self::$phpBin.' -r '.escapeshellarg('echo serialize($_SERVER);'), null, array('BAR' => 'BAZ'));
|
||||
|
||||
putenv('FOO=BAR');
|
||||
|
||||
$this->assertSame($process, $process->inheritEnvironmentVariables(1));
|
||||
$this->assertTrue($process->areEnvironmentVariablesInherited());
|
||||
|
||||
$process->run();
|
||||
|
||||
$expected = array('BAR' => 'BAZ', 'FOO' => 'BAR');
|
||||
$env = array_intersect_key(unserialize($process->getOutput()), $expected);
|
||||
|
||||
$this->assertSame($expected, $env);
|
||||
}
|
||||
|
||||
public function testInheritEnvDisabled()
|
||||
{
|
||||
$process = $this->getProcess(self::$phpBin.' -r '.escapeshellarg('echo serialize($_SERVER);'), null, array('BAR' => 'BAZ'));
|
||||
|
||||
putenv('FOO=BAR');
|
||||
|
||||
$this->assertFalse($process->areEnvironmentVariablesInherited());
|
||||
|
||||
$process->run();
|
||||
|
||||
$expected = array('BAR' => 'BAZ', 'FOO' => 'BAR');
|
||||
$env = array_intersect_key(unserialize($process->getOutput()), $expected);
|
||||
unset($expected['FOO']);
|
||||
|
||||
$this->assertSame($expected, $env);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $commandline
|
||||
* @param null|string $cwd
|
||||
|
Reference in New Issue
Block a user