-
Notifications
You must be signed in to change notification settings - Fork 308
Changed taskExec to use symfony process placeholders #1038
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.x
Are you sure you want to change the base?
Changes from all commits
f06df01
0da2288
4fbd183
19fe043
3a87e4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,9 +10,9 @@ | |
| use Symfony\Component\Process\Exception\InvalidArgumentException; | ||
|
|
||
| /** | ||
| * ProcessUtils is a bunch of utility methods. We want to allow Robo 1.x | ||
| * to work with Symfony 4.x while remaining backwards compatibility. This | ||
| * requires us to replace some deprecated functionality removed in Symfony. | ||
| * ProcessUtils is a bunch of utility methods. | ||
| * These methods are usually private, and are needed to execute and escape | ||
| * some functions for display purposes | ||
| */ | ||
| class ProcessUtils | ||
| { | ||
|
|
@@ -24,58 +24,47 @@ private function __construct() | |
| } | ||
|
|
||
| /** | ||
| * Escapes a string to be used as a shell argument. | ||
| * | ||
| * This method is a copy of a method that was deprecated by Symfony 3.3 and | ||
| * removed in Symfony 4; it will be removed once there is an actual | ||
| * replacement for escapeArgument. | ||
| * | ||
| * @param string $argument | ||
| * The argument that will be escaped. | ||
| * Symfony Process has a private method to replace Placeholders in command lines, | ||
| * which we use to rebuild a Command Description. | ||
| * | ||
| * @param string $commandline | ||
| * @param array $env | ||
| * @return string | ||
| * The escaped argument. | ||
| */ | ||
| public static function escapeArgument($argument) | ||
| public static function replacePlaceholders(string $commandline, array $env) | ||
| { | ||
| //Fix for PHP bug #43784 escapeshellarg removes % from given string | ||
| //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows | ||
| //@see https://bugs.php.net/bug.php?id=43784 | ||
| //@see https://bugs.php.net/bug.php?id=49446 | ||
| if ('\\' === DIRECTORY_SEPARATOR) { | ||
| if ('' === $argument) { | ||
| return escapeshellarg($argument); | ||
| } | ||
|
|
||
| $escapedArgument = ''; | ||
| $quote = false; | ||
| foreach (preg_split('/(")/', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { | ||
| if ('"' === $part) { | ||
| $escapedArgument .= '\\"'; | ||
| } elseif (self::isSurroundedBy($part, '%')) { | ||
| // Avoid environment variable expansion | ||
| $escapedArgument .= '^%"' . substr($part, 1, -1) . '"^%'; | ||
| } else { | ||
| // escape trailing backslash | ||
| if ('\\' === substr($part, -1)) { | ||
| $part .= '\\'; | ||
| } | ||
| $quote = true; | ||
| $escapedArgument .= $part; | ||
| } | ||
| return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) { | ||
| if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) { | ||
| throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": ', $matches[1]).$commandline); | ||
| } | ||
| if ($quote) { | ||
| $escapedArgument = '"' . $escapedArgument . '"'; | ||
| } | ||
|
|
||
| return $escapedArgument; | ||
| } | ||
|
|
||
| return "'" . str_replace("'", "'\\''", $argument) . "'"; | ||
| return self::escapeArgument($env[$matches[1]]); | ||
| }, $commandline); | ||
| } | ||
|
|
||
| private static function isSurroundedBy($arg, $char) | ||
| /** | ||
| * Used by Symfony Process to form the final command line, with escaped parameters. | ||
| * Robo uses placeholders to handle the process arguments without escaping the whole string. | ||
| * | ||
| * @param string|null $argument | ||
| * @return string | ||
| */ | ||
| public static function escapeArgument(?string $argument): string | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since escapeArgument is only used for display purposes in taskExec, maybe we should retain the existing implementation. The new version might have some benefits for Windows, but since it will only be used by code that calls
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm undecided about this; if we don't update the implementation, then the printed string won't exactly match the string that Symfony is actually exec'ing, which could be odd. Then again, you could also get into that situation if you ended up with an older version of Symfony that has a different implementation of escape. I'm not sure if our Composer version constraints on our dependencies allows that, though.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Symfony Process is set at 4.4.11 min, and escapeArgument has been changed to this implementation since at least 4.1.7. |
||
| { | ||
| return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; | ||
| if ('' === $argument || null === $argument) { | ||
| return '""'; | ||
| } | ||
| if ('\\' !== \DIRECTORY_SEPARATOR) { | ||
| return "'".str_replace("'", "'\\''", $argument)."'"; | ||
| } | ||
| if (false !== strpos($argument, "\0")) { | ||
| $argument = str_replace("\0", '?', $argument); | ||
| } | ||
| if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) { | ||
| return $argument; | ||
| } | ||
| $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument); | ||
|
|
||
| return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"'; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
| use Robo\Result; | ||
| use Robo\Common\CommandReceiver; | ||
| use Robo\Common\ExecOneCommand; | ||
| use Robo\Common\ProcessUtils; | ||
|
|
||
| /** | ||
| * Executes shell script. Closes it when running in background mode. | ||
|
|
@@ -98,8 +99,9 @@ public function background($arg = true) | |
| */ | ||
| protected function getCommandDescription() | ||
| { | ||
| return $this->getCommand(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now I am wondering if this PR is in fact breaking, since you have changed the contract on Would it work out to make
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I've just tested outside of taskExec, and it does break on other commands : most command include a custom In any case, there's more than So to sum things up :
Sounds good?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that sounds good. |
||
| return ProcessUtils::replacePlaceholders($this->getCommand(), $this->argumentsEnv); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
|
|
@@ -132,7 +134,7 @@ public function run() | |
| { | ||
| $this->hideProgressIndicator(); | ||
| // TODO: Symfony 4 requires that we supply the working directory. | ||
| $result_data = $this->execute(Process::fromShellCommandline($this->getCommand(), getcwd())); | ||
| $result_data = $this->execute(Process::fromShellCommandline($this->getCommand(), getcwd(), $this->argumentsEnv)); | ||
|
Alenore marked this conversation as resolved.
|
||
| $result = new Result( | ||
| $this, | ||
| $result_data->getExitCode(), | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.