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