Skip to content

Instantly share code, notes, and snippets.

@BYK
Last active April 12, 2016 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BYK/f85c0845d9ad75fc508927ae39210803 to your computer and use it in GitHub Desktop.
Save BYK/f85c0845d9ad75fc508927ae39210803 to your computer and use it in GitHub Desktop.
Process call argument escaping on Windows with PHP
<?php
/**
* Escapes a single argument to be glued together and passed into
* CreateProcess on Windows through `proc_open`.
*
* Adapted from https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
*
* @param string The argument to be escaped
* @result string Escaped argument that can be used in a CreateProcess call
*/
function escapeWindowsArgv($value) {
// Don't quote unless we actually need to do so hopefully
// avoid problems if programs won't parse quotes properly
if ($value && !preg_match('/["[:space:]]/', $value)) {
return $value;
}
$result = '"';
$len = strlen($value);
for ($i = 0; $i < $len; $i++) {
$numBackslashes = 0;
while ($i < $len && $value[$i] == '\\') {
$i++;
$numBackslashes++;
}
if ($i == $len) {
// Escape all backslashes, but let the terminating
// double quotation mark we add below be interpreted
// as a metacharacter.
$result .= str_repeat('\\', $numBackslashes * 2);
break;
} elseif ($value[$i] == '"') {
// Escape all backslashes and the following double quotation mark.
$result .= str_repeat('\\', $numBackslashes * 2 + 1);
$result .= $value[$i];
} else {
// Backslashes aren't special here.
$result .= str_repeat('\\', $numBackslashes);
$result .= $value[$i];
}
}
$result .= '"';
return $result;
}
/**
* Escapes all CMD metacharacters with a `^`.
*
* Adapted from https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
*
* @param string The argument to be escaped
* @result string Escaped argument that can be used in CMD.exe
*/
function escapeWindowsCMD($value) {
// Make sure this is CreateProcess-safe first
$value = escapeWindowsArgv($value);
// Now prefix all CMD meta characters with a `^` to escape them
return preg_replace('/[()%!^"<>&|]/', '^$0', $value);
}
/* Some strings that are tested:
$edgeCases = array(
'\0', // null byte
'\\',
'%',
'%%',
' ', // space
'', // empty string
'-',
'/flag',
'\\\^\%\\\'\ \\',
'%PATH%',
'%XYZ%',
'%%HOMEDIR%',
'\n', // newline
'\r', // newline
'a b',
'"a b"',
'"%%$HOMEDIR%^^"',
'\'a b\'',
'^%HO ^"M\'EDIR^%^%\'',
'"\'a\0\r\nb%PATH%%`\'"\'`\'`\'',
);
*/
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment