1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Process; 13 14use Symfony\Component\Process\Exception\InvalidArgumentException; 15 16/** 17 * ProcessUtils is a bunch of utility methods. 18 * 19 * This class contains static methods only and is not meant to be instantiated. 20 * 21 * @author Martin Hasoň <martin.hason@gmail.com> 22 */ 23class ProcessUtils 24{ 25 /** 26 * This class should not be instantiated. 27 */ 28 private function __construct() 29 { 30 } 31 32 /** 33 * Escapes a string to be used as a shell argument. 34 * 35 * @param string $argument The argument that will be escaped 36 * 37 * @return string The escaped argument 38 * 39 * @deprecated since version 3.3, to be removed in 4.0. Use a command line array or give env vars to the `Process::start/run()` method instead. 40 */ 41 public static function escapeArgument($argument) 42 { 43 @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use a command line array or give env vars to the Process::start/run() method instead.', \E_USER_DEPRECATED); 44 45 //Fix for PHP bug #43784 escapeshellarg removes % from given string 46 //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows 47 //@see https://bugs.php.net/43784 48 //@see https://bugs.php.net/49446 49 if ('\\' === \DIRECTORY_SEPARATOR) { 50 if ('' === $argument) { 51 return escapeshellarg($argument); 52 } 53 54 $escapedArgument = ''; 55 $quote = false; 56 foreach (preg_split('/(")/', $argument, -1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE) as $part) { 57 if ('"' === $part) { 58 $escapedArgument .= '\\"'; 59 } elseif (self::isSurroundedBy($part, '%')) { 60 // Avoid environment variable expansion 61 $escapedArgument .= '^%"'.substr($part, 1, -1).'"^%'; 62 } else { 63 // escape trailing backslash 64 if ('\\' === substr($part, -1)) { 65 $part .= '\\'; 66 } 67 $quote = true; 68 $escapedArgument .= $part; 69 } 70 } 71 if ($quote) { 72 $escapedArgument = '"'.$escapedArgument.'"'; 73 } 74 75 return $escapedArgument; 76 } 77 78 return "'".str_replace("'", "'\\''", $argument)."'"; 79 } 80 81 /** 82 * Validates and normalizes a Process input. 83 * 84 * @param string $caller The name of method call that validates the input 85 * @param mixed $input The input to validate 86 * 87 * @return mixed The validated input 88 * 89 * @throws InvalidArgumentException In case the input is not valid 90 */ 91 public static function validateInput($caller, $input) 92 { 93 if (null !== $input) { 94 if (\is_resource($input)) { 95 return $input; 96 } 97 if (\is_string($input)) { 98 return $input; 99 } 100 if (is_scalar($input)) { 101 return (string) $input; 102 } 103 if ($input instanceof Process) { 104 return $input->getIterator($input::ITER_SKIP_ERR); 105 } 106 if ($input instanceof \Iterator) { 107 return $input; 108 } 109 if ($input instanceof \Traversable) { 110 return new \IteratorIterator($input); 111 } 112 113 throw new InvalidArgumentException(sprintf('"%s" only accepts strings, Traversable objects or stream resources.', $caller)); 114 } 115 116 return $input; 117 } 118 119 private static function isSurroundedBy($arg, $char) 120 { 121 return 2 < \strlen($arg) && $char === $arg[0] && $char === $arg[\strlen($arg) - 1]; 122 } 123} 124