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 $incrementalErrorOutputOffset = 0;
|
||||||
private $tty;
|
private $tty;
|
||||||
private $pty;
|
private $pty;
|
||||||
|
private $inheritEnv = false;
|
||||||
|
|
||||||
private $useFileHandles = false;
|
private $useFileHandles = false;
|
||||||
/** @var PipesInterface */
|
/** @var PipesInterface */
|
||||||
@ -267,9 +268,22 @@ class Process implements \IteratorAggregate
|
|||||||
$descriptors = $this->getDescriptors();
|
$descriptors = $this->getDescriptors();
|
||||||
|
|
||||||
$commandline = $this->commandline;
|
$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) {
|
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) {
|
foreach ($this->processPipes->getFiles() as $offset => $filename) {
|
||||||
$commandline .= ' '.$offset.'>'.ProcessUtils::escapeArgument($filename);
|
$commandline .= ' '.$offset.'>'.ProcessUtils::escapeArgument($filename);
|
||||||
}
|
}
|
||||||
@ -283,15 +297,17 @@ class Process implements \IteratorAggregate
|
|||||||
$descriptors[3] = array('pipe', 'w');
|
$descriptors[3] = array('pipe', 'w');
|
||||||
|
|
||||||
// See https://unix.stackexchange.com/questions/71205/background-process-pipe-input
|
// 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';
|
$commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code';
|
||||||
|
|
||||||
// Workaround for the bug, when PTS functionality is enabled.
|
// Workaround for the bug, when PTS functionality is enabled.
|
||||||
// @see : https://bugs.php.net/69442
|
// @see : https://bugs.php.net/69442
|
||||||
$ptsWorkaround = fopen(__FILE__, 'r');
|
$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)) {
|
if (!is_resource($this->process)) {
|
||||||
throw new RuntimeException('Unable to launch a new process.');
|
throw new RuntimeException('Unable to launch a new process.');
|
||||||
@ -1197,6 +1213,30 @@ class Process implements \IteratorAggregate
|
|||||||
return $this;
|
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.
|
* 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);
|
$arguments = array_merge($this->prefix, $this->arguments);
|
||||||
$script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $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) {
|
if ($this->inheritEnv) {
|
||||||
$env = array_replace($_ENV, $_SERVER, $this->env);
|
$process->inheritEnvironmentVariables();
|
||||||
} else {
|
|
||||||
$env = $this->env;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$process = new Process($script, $this->cwd, $env, $this->input, $this->timeout, $options);
|
|
||||||
|
|
||||||
if ($this->outputDisabled) {
|
if ($this->outputDisabled) {
|
||||||
$process->disableOutput();
|
$process->disableOutput();
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,11 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
public function testInheritEnvironmentVars()
|
public function testInheritEnvironmentVars()
|
||||||
{
|
{
|
||||||
$_ENV['MY_VAR_1'] = 'foo';
|
|
||||||
|
|
||||||
$proc = ProcessBuilder::create()
|
$proc = ProcessBuilder::create()
|
||||||
->add('foo')
|
->add('foo')
|
||||||
->getProcess();
|
->getProcess();
|
||||||
|
|
||||||
unset($_ENV['MY_VAR_1']);
|
$this->assertTrue($proc->areEnvironmentVariablesInherited());
|
||||||
|
|
||||||
$env = $proc->getEnv();
|
|
||||||
$this->assertArrayHasKey('MY_VAR_1', $env);
|
|
||||||
$this->assertEquals('foo', $env['MY_VAR_1']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddEnvironmentVariables()
|
public function testAddEnvironmentVariables()
|
||||||
@ -46,22 +40,7 @@ class ProcessBuilderTest extends \PHPUnit_Framework_TestCase
|
|||||||
;
|
;
|
||||||
|
|
||||||
$this->assertSame($env, $proc->getEnv());
|
$this->assertSame($env, $proc->getEnv());
|
||||||
}
|
$this->assertFalse($proc->areEnvironmentVariablesInherited());
|
||||||
|
|
||||||
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']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1357,6 +1357,40 @@ class ProcessTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertSame('456', $p2->getOutput());
|
$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 string $commandline
|
||||||
* @param null|string $cwd
|
* @param null|string $cwd
|
||||||
|
Reference in New Issue
Block a user