PluginProbe ʕ •ᴥ•ʔ
Backup Migration / 1.4.7
Backup Migration v1.4.7
2.1.6 2.1.5.2 trunk 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.6.1 1.4.7 1.4.8 1.4.9 1.4.9.1 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.5.1
backup-backup / includes / cli / php_cli_finder.php
backup-backup / includes / cli Last commit date
php_cli_finder.php 1 year ago version_check.php 1 year ago
php_cli_finder.php
303 lines
1 <?php
2
3 // Namespace
4 namespace BMI\Plugin\PHPCLI;
5
6 // Use
7 use BMI\Plugin\Backup_Migration_Plugin as BMP;
8
9 // Exit on direct access
10 if (!defined('ABSPATH')) exit;
11
12 /**
13 * Scans for PHP CLI executable and makes sure exec is working
14 */
15 class Checker {
16
17 /**
18 * Points if the exec is disabled or not
19 */
20 public $ini_disabled = true;
21
22 /**
23 * __construct - Unused construct function
24 *
25 * @return {self}
26 */
27 function __construct() {}
28
29 /**
30 * findPHP - Scans the system to find executable PHP
31 *
32 * @return {bool/array} false on fail, array on success
33 * $final_executable = [
34 * 'version' => $php_cli_version,
35 * 'brand' => $php_brand,
36 * 'memory' => $php_cli_memory . '/' . $php_cli_memory_modified,
37 * 'max_exec' => $php_cli_max_exec . '/' . $php_cli_max_exec_modified,
38 * 'executable' => $executable_path_to_file
39 * ];
40 */
41 public function findPHP() {
42
43 // Return false if exec is disabled
44 if ($this->isExecEnabled() === false) return false;
45
46 // Check if user defined own PHP CLI
47 $user_defined = false;
48 if (defined('BMI_CLI_EXECUTABLE')) {
49 if (file_exists(BMI_CLI_EXECUTABLE)) {
50 $user_defined = true;
51 }
52 }
53
54 if ($user_defined === false) {
55
56 // Makes variable for system paths list
57 $system_paths = null;
58
59 // Exec command which displays all paths per line
60 @exec('(sed "s/:/\n/g" <<< $PATH) 2>&1', $system_paths);
61
62 // Concat output
63 $system_paths = implode("\n", $system_paths);
64
65 // Check if the output is not empty, if empty abort
66 // if (empty($system_paths) || !$system_paths) return false;
67
68 // Make variable for executables that contains php keyword
69 $executables = ['php', '/usr/bin/php', '/usr/bin/php8.0', '/usr/bin/php7.4'];
70
71 // Make variable for "for" loop
72 $system_paths = explode("\n", $system_paths);
73
74 if (in_array('/usr/bin', $system_paths)) $system_paths[] = '/usr/bin';
75 if (in_array('/bin', $system_paths)) $system_paths[] = '/bin';
76 if (in_array('/etc/alternatives', $system_paths)) $system_paths[] = '/etc/alternatives';
77 if (in_array('/usr/local/bin', $system_paths)) $system_paths[] = '/usr/local/bin';
78
79 // Loop all paths and check for PHP executables
80 for ($i = 0; $i < sizeof($system_paths); ++$i) {
81
82 // Variable for scan output
83 $executables_scan = null;
84
85 // Trim the path just in case
86 $path = trim($system_paths[$i]);
87
88 // If path is empty ignore and continue
89 if (empty($path)) continue;
90
91 // Exec command which will display PHP (in name) executables
92 @exec('(for i in $(ls ' . $path . ' | grep "php"); do [ -x ' . $path . '/$i ] && echo ' . $path . '/$i || echo ""; done;) 2>&1', $executables_scan);
93
94 // Implode the output
95 $executables_scan = implode("\n", $executables_scan);
96
97 // Merge the array with other results
98 $executables = array_merge($executables, explode("\n", $executables_scan));
99
100 }
101
102 }
103
104 // Make variable for real PHP executables
105 $php_executables = [];
106
107 // If used defined own PHP CLI use it
108 if ($user_defined === true) $executables = [BMI_CLI_EXECUTABLE];
109
110 // Filter the array to exclude empty values and remove duplicates
111 $executables = array_filter(array_unique($executables));
112
113 // Make variable for final executable
114 $final_executable = false;
115
116 // Loop and test the executables
117 foreach ($executables as $exe) {
118
119 // If path+name does not contain php ignore and continue
120 if (strpos($exe, 'php') === false) continue;
121
122 // Make variable for CLI version in shell
123 $shell_version = null;
124
125 // Make variable for regex check
126 $output = null;
127
128 // Exec the command to check shell displayed version
129 @exec($exe . ' --version 2>&1', $shell_version);
130
131 // Implode the output
132 $shell_version = implode("\n", $shell_version);
133
134 // Test the output with regex to find PHP version and brand
135 preg_match('/PHP\ (.*)\ (.*)\ \(built: (.*)\)/i', $shell_version, $output);
136
137 // Check if the output is not empty and contains at least 4 results
138 if ($output && !empty($output) && !empty($output[1]) && sizeof($output) >= 4) {
139
140 // Remove additional characters from the version leave only numbers with dots
141 $php = preg_replace("/[^0-9.]/", "", $output[1]);
142
143 // Save the brand in the $brand variable
144 $brand = $output[2];
145
146 // Make variable for file version test
147 $file_version = null;
148
149 // Make variable for shell inline PHP version test
150 $inline_version = null;
151
152 // Exec the shell inline test for version
153 @exec($exe . ' -r "echo phpversion();" 2>&1', $inline_version);
154
155 // Implode the version
156 $inline_version = implode("\n", $inline_version);
157
158 // Check if the version match required minimum
159 if (version_compare($inline_version, '7.0', '>=')) {
160
161 // Path to CLI check file
162 $path_to_cli = BMI_INCLUDES . '/cli/version_check.php';
163
164 // Exec the file check if it can run files
165 @exec($exe . ' -f ' . $path_to_cli . ' 2>&1', $file_version);
166
167 // Check if the output is correct
168 if ($file_version && is_array($file_version) && !empty($file_version) && sizeof($file_version) >= 5) {
169
170 // The results from file saved to named variables
171 $php_cli_version = $file_version[0];
172 $php_cli_memory = $file_version[1];
173 $php_cli_max_exec = $file_version[2];
174 $php_cli_memory_modified = $file_version[3];
175 $php_cli_max_exec_modified = $file_version[4];
176
177 // Check if the version match the inline one (it's the same php.ini)
178 if (trim($php_cli_version) == trim($inline_version)) {
179
180 // If it match use this PHP CLI module
181 $final_executable = [
182 'brand' => $brand,
183 'version' => $php_cli_version,
184 'memory' => $php_cli_memory . '/' . $php_cli_memory_modified,
185 'max_exec' => $php_cli_max_exec . '/' . $php_cli_max_exec_modified,
186 'executable' => $exe
187 ];
188
189 break;
190
191 }
192
193 }
194
195 }
196
197 }
198
199 }
200
201 // Return the final result
202 return $final_executable;
203
204 }
205
206 /**
207 * isExecAvailable - Check if exec is not blocked by php.ini
208 *
209 * @return {bool} true on success / false on fail
210 */
211 public function isExecAvailable() {
212
213 // Get disabled functions
214 $disabled_functions = @ini_get('disable_functions');
215
216 // Turn disabled functions to array
217 $disabled_functions = explode(',', $disabled_functions);
218
219 // Check if everything is allowed
220 if (empty($disabled_functions)) {
221
222 // Check if function is callable and not disabled (PHP 8 check)
223 if (function_exists('shell_exec') && is_callable('shell_exec')) return true;
224 else return false;
225
226 }
227
228 // Few checks
229 if (!is_array($disabled_functions)) return false;
230 elseif (in_array('shell_exec', $disabled_functions)) return false;
231 elseif (in_array('exec', $disabled_functions)) return false;
232 elseif (in_array('system', $disabled_functions)) return false;
233 else {
234
235 // Check if function is callable and not disabled (PHP 8 check)
236 if (function_exists('shell_exec') && is_callable('shell_exec')) return true;
237 else return false;
238
239 }
240
241 }
242
243 /**
244 * isExecEnabled - Checks if exec function is not disabled or blocked
245 *
246 * @return {bool} true on success / false on fail
247 */
248 public function isExecEnabled() {
249
250 // Check if the function is not disabled in php.ini
251 if ($this->isExecAvailable()) {
252
253 // Mark as enabled in php_ini
254 $this->ini_disabled = false;
255
256 // Try to run simple shell
257 try {
258
259 // Output variable
260 $output = null;
261
262 // Execute the command
263 @exec('echo "It works!" 2>&1', $output);
264
265 // Implode the output just in case
266 if ($output) {
267 $output = implode("\n", $output);
268 } else return false;
269
270 // Check if the output is as expected
271 if ($output === 'It works!') return true;
272 else return false;
273
274 // Catch errors in older PHP
275 } catch (\Error $e) {
276
277 return false;
278
279 // Catch exceptions if any
280 } catch (\Exception $e) {
281
282 return false;
283
284 // Catch throwable exception if any
285 } catch (\Throwable $e) {
286
287 return false;
288
289 }
290
291 // If the function is blocked do not even try and return false
292 } else {
293
294 // Mark as disabled in php_ini
295 $this->ini_disabled = true;
296 return false;
297
298 }
299
300 }
301
302 }
303