Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
69.39% |
34 / 49 |
|
20.00% |
1 / 5 |
CRAP | |
0.00% |
0 / 1 |
ExecWithFallback | |
69.39% |
34 / 49 |
|
20.00% |
1 / 5 |
47.91 | |
0.00% |
0 / 1 |
anyAvailable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
functionEnabled | |
81.25% |
13 / 16 |
|
0.00% |
0 / 1 |
7.32 | |||
exec | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
8.04 | |||
execNoMercy | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
runExec | |
69.23% |
9 / 13 |
|
0.00% |
0 / 1 |
8.43 |
1 | <?php |
2 | namespace ExecWithFallback; |
3 | |
4 | /** |
5 | * Execute command with exec(), open_proc() or whatever available |
6 | * |
7 | * @package ExecWithFallback |
8 | * @author Bjørn Rosell <it@rosell.dk> |
9 | */ |
10 | class ExecWithFallback |
11 | { |
12 | |
13 | protected static $methods = ['exec', 'passthru', 'popen', 'proc_open', 'shell_exec']; |
14 | |
15 | /** |
16 | * Check if any of the methods are available on the system. |
17 | * |
18 | * @param boolean $needResultCode Whether the code using this library is going to supply $result_code to the exec |
19 | * call. This matters because shell_exec is only available when not. |
20 | */ |
21 | public static function anyAvailable($needResultCode = true) |
22 | { |
23 | return Availability::anyAvailable($needResultCode); |
24 | } |
25 | |
26 | /** |
27 | * Check if a function is enabled (function_exists as well as ini is tested) |
28 | * |
29 | * @param string $functionName The name of the function |
30 | * |
31 | * @return boolean If the function is enabled |
32 | */ |
33 | public static function functionEnabled($functionName) |
34 | { |
35 | if (!function_exists($functionName)) { |
36 | return false; |
37 | } |
38 | if (function_exists('ini_get')) { |
39 | if (ini_get('safe_mode')) { |
40 | return false; |
41 | } |
42 | $d = ini_get('disable_functions'); |
43 | if ($d === false) { |
44 | $d = ''; |
45 | } |
46 | $d2 = ini_get('suhosin.executor.func.blacklist'); |
47 | if ($d2 === false) { |
48 | $d2 = ''; |
49 | } |
50 | $d .= $d2 . ','; |
51 | |
52 | $d = preg_replace('/,\s*/', ',', $d); |
53 | if (strpos(',' . $d . ',', ',' . $functionName . ',') !== false) { |
54 | return false; |
55 | } |
56 | } |
57 | return is_callable($functionName); |
58 | } |
59 | |
60 | |
61 | /** |
62 | * Execute. - A substitute for exec() |
63 | * |
64 | * Same signature and results as exec(): https://www.php.net/manual/en/function.exec.php |
65 | * In case neither exec(), nor emulations are available, it throws an Exception. |
66 | * This is more gentle than real exec(), which on some systems throws a FATAL when exec() is disabled |
67 | * If you want the more acurate substitute, which might halt execution, use execNoMercy() instead. |
68 | * |
69 | * @param string $command The command to execute |
70 | * @param string &$output (optional) |
71 | * @param int &$result_code (optional) |
72 | * |
73 | * @return string | false The last line of output or false in case of failure |
74 | * @throws \Exception If no methods are available |
75 | */ |
76 | public static function exec($command, &$output = null, &$result_code = null) |
77 | { |
78 | foreach (self::$methods as $method) { |
79 | if (self::functionEnabled($method)) { |
80 | if (func_num_args() >= 3) { |
81 | if ($method == 'shell_exec') { |
82 | continue; |
83 | } |
84 | $result = self::runExec($method, $command, $output, $result_code); |
85 | } else { |
86 | $result = self::runExec($method, $command, $output); |
87 | } |
88 | if ($result !== false) { |
89 | return $result; |
90 | } |
91 | } |
92 | } |
93 | if (isset($result) && ($result === false)) { |
94 | return false; |
95 | } |
96 | throw new \Exception('exec() is not available'); |
97 | } |
98 | |
99 | /** |
100 | * Execute. - A substitute for exec(), with exact same errors thrown if exec() is missing. |
101 | * |
102 | * Danger: On some systems, this results in a fatal (non-catchable) error. |
103 | */ |
104 | public static function execNoMercy($command, &$output = null, &$result_code = null) |
105 | { |
106 | if (func_num_args() == 3) { |
107 | try { |
108 | return self::exec($command, $output, $result_code); |
109 | } catch (\Exception $e) { |
110 | // MIGHT THROW FATAL! |
111 | return exec($command, $output, $result_code); |
112 | } |
113 | } else { |
114 | try { |
115 | return self::exec($command, $output); |
116 | } catch (\Exception $e) { |
117 | // MIGHT THROW FATAL! |
118 | return exec($command, $output); |
119 | } |
120 | } |
121 | } |
122 | |
123 | public static function runExec($method, $command, &$output = null, &$result_code = null) |
124 | { |
125 | switch ($method) { |
126 | case 'exec': |
127 | return exec($command, $output, $result_code); |
128 | case 'passthru': |
129 | return Passthru::exec($command, $output, $result_code); |
130 | case 'popen': |
131 | return POpen::exec($command, $output, $result_code); |
132 | case 'proc_open': |
133 | return ProcOpen::exec($command, $output, $result_code); |
134 | case 'shell_exec': |
135 | if (func_num_args() == 4) { |
136 | return ShellExec::exec($command, $output, $result_code); |
137 | } else { |
138 | return ShellExec::exec($command, $output); |
139 | } |
140 | } |
141 | return false; |
142 | } |
143 | } |