Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.39% covered (warning)
69.39%
34 / 49
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ExecWithFallback
69.39% covered (warning)
69.39%
34 / 49
20.00% covered (danger)
20.00%
1 / 5
47.91
0.00% covered (danger)
0.00%
0 / 1
 anyAvailable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 functionEnabled
81.25% covered (warning)
81.25%
13 / 16
0.00% covered (danger)
0.00%
0 / 1
7.32
 exec
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
8.04
 execNoMercy
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 runExec
69.23% covered (warning)
69.23%
9 / 13
0.00% covered (danger)
0.00%
0 / 1
8.43
1<?php
2namespace 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 */
10class 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}