Skip to content

Commit bd8ad3f

Browse files
committed
feature symfony#32126 [Process] Allow writing portable "prepared" command lines (Simperfit)
This PR was merged into the 4.4 branch. Discussion ---------- [Process] Allow writing portable "prepared" command lines | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | symfony#23778 <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | symfony/symfony-docs#11802 <!-- required for new features --> Hey here, it's me, again ! I've talked with @nicolas-grekas and he gave me a new way of writing this feature that will not be a problem for people using things like {{ toto }} since we are using the linux style of using envvar, the replaced arguments is only replaced when using windows. This should not break anything and work as expected! see symfony#24763 This makes `"$FOO"` work seamlessly on Linux and on Windows to reference an env var (that can be provided when calling e.g. the "run" method) Commits ------- 3f8354f [Process] Allow writing portable "prepared" command lines
2 parents f15722d + 3f8354f commit bd8ad3f

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-5
lines changed

src/Symfony/Component/Process/Process.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,20 +291,23 @@ public function start(callable $callback = null, array $env = [])
291291
$this->hasCallback = null !== $callback;
292292
$descriptors = $this->getDescriptors();
293293

294+
if ($this->env) {
295+
$env += $this->env;
296+
}
297+
298+
$env += $this->getDefaultEnv();
299+
294300
if (\is_array($commandline = $this->commandline)) {
295301
$commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline));
296302

297303
if ('\\' !== \DIRECTORY_SEPARATOR) {
298304
// exec is mandatory to deal with sending a signal to the process
299305
$commandline = 'exec '.$commandline;
300306
}
307+
} else {
308+
$commandline = $this->replacePlaceholders($commandline, $env);
301309
}
302310

303-
if ($this->env) {
304-
$env += $this->env;
305-
}
306-
$env += $this->getDefaultEnv();
307-
308311
$options = ['suppress_errors' => true];
309312

310313
if ('\\' === \DIRECTORY_SEPARATOR) {
@@ -1632,6 +1635,17 @@ private function escapeArgument(?string $argument): string
16321635
return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"';
16331636
}
16341637

1638+
private function replacePlaceholders(string $commandline, array $env)
1639+
{
1640+
return preg_replace_callback('/"\$([_a-zA-Z]++[_a-zA-Z0-9]*+)"/', function ($matches) use ($commandline, $env) {
1641+
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
1642+
throw new InvalidArgumentException(sprintf('Command line is missing a value for key %s: %s.', $matches[0], $commandline));
1643+
}
1644+
1645+
return '\\' === \DIRECTORY_SEPARATOR ? $this->escapeArgument($env[$matches[1]]) : $matches[0];
1646+
}, $commandline);
1647+
}
1648+
16351649
private function getDefaultEnv()
16361650
{
16371651
$env = [];

src/Symfony/Component/Process/Tests/ProcessTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,50 @@ public function provideEscapeArgument()
15001500
yield [1.1];
15011501
}
15021502

1503+
public function testPreparedCommand()
1504+
{
1505+
$p = Process::fromShellCommandline('echo "$abc"DEF');
1506+
$p->run(null, ['abc' => 'ABC']);
1507+
1508+
$this->assertSame('ABCDEF', rtrim($p->getOutput()));
1509+
}
1510+
1511+
public function testPreparedCommandMulti()
1512+
{
1513+
$p = Process::fromShellCommandline('echo "$abc""$def"');
1514+
$p->run(null, ['abc' => 'ABC', 'def' => 'DEF']);
1515+
1516+
$this->assertSame('ABCDEF', rtrim($p->getOutput()));
1517+
}
1518+
1519+
public function testPreparedCommandWithQuoteInIt()
1520+
{
1521+
$p = Process::fromShellCommandline('php -r "$code" "$def"');
1522+
$p->run(null, ['code' => 'echo $argv[1];', 'def' => '"DEF"']);
1523+
1524+
$this->assertSame('"DEF"', rtrim($p->getOutput()));
1525+
}
1526+
1527+
/**
1528+
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
1529+
* @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc".
1530+
*/
1531+
public function testPreparedCommandWithMissingValue()
1532+
{
1533+
$p = Process::fromShellCommandline('echo "$abc"');
1534+
$p->run(null, ['bcd' => 'BCD']);
1535+
}
1536+
1537+
/**
1538+
* @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
1539+
* @expectedExceptionMessage Command line is missing a value for key "$abc": echo "$abc".
1540+
*/
1541+
public function testPreparedCommandWithNoValues()
1542+
{
1543+
$p = Process::fromShellCommandline('echo "$abc"');
1544+
$p->run(null, []);
1545+
}
1546+
15031547
public function testEnvArgument()
15041548
{
15051549
$env = ['FOO' => 'Foo', 'BAR' => 'Bar'];

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy