PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-backup-helper-script-manager / src / class-throw-on-errors.php

class-throw-on-errors.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.5, at jetpack_vendor/automattic/jetpack-backup-helper-script-manager/src/class-throw-on-errors.php

499 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:disable Squiz.Commenting.FileComment.Missing
2
3 // phpcs:disable WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting
4 // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting
5 // phpcs:disable WordPress.PHP.IniSet.display_errors_Disallowed
6
7 // After changing this file, consider increasing the version number ("VXXX") in all the files using this namespace, in
8 // order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
9 // to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
10 // are installed, or in some other cases).
11 namespace Automattic\Jetpack\Backup\V0005;
12
13 use Exception;
14 use Throwable;
15
16 /**
17 * Wrappers for functions which throw an exception on errors and warnings instead of silently continuing operation.
18 *
19 * PHP is pretty lax with error reporting when doing I/O operations, e.g. a typical I/O helper function returns false,
20 * null, and/or emits a warning, but the whole PHP application continues operation. It's for the caller of the I/O
21 * helper function to test the returned result of the function, and notice + act upon I/O errors.
22 *
23 * We really want to know about each and every I/O error. Therefore, this class provides wrappers for some common
24 * (mostly) I/O operations that throw an exception on errors instead of just returning false, null, or something else.
25 * This wrapper class treats warnings as errors too.
26 *
27 * Given that static method names are similar to the ones used by PHP, they're prefixed with "t_" to not erroneously
28 * trigger various security scanners.
29 */
30 class Throw_On_Errors {
31
32 /**
33 * Execute a callable, throw an exception (together with a descriptive label) on PHP warnings / errors.
34 *
35 * @param callable $callable Callable to execute.
36 * @param string $label Label to add to the thrown exception to clarify what was attempted.
37 *
38 * @return mixed Callable's return value, if any.
39 * @throws Exception On warnings thrown by the callable.
40 * @noinspection PhpUnusedParameterInspection
41 */
42 private static function throw_on_warnings( $callable, $label ) {
43 $old_error_reporting = error_reporting( - 1 );
44 $old_display_errors = ini_set( 'display_errors', 'stderr' );
45
46 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
47 set_error_handler(
48 /**
49 * Temporary error handler.
50 *
51 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.set-error-handler
52 * @see https://www.php.net/manual/en/function.set-error-handler.php
53 *
54 * @param int $errno Level of the error raised.
55 * @param string $errstr Error message.
56 * @param string|null $errfile Filename that the error was raised in.
57 * @param int|null $errline Line number where the error was raised.
58 * @param array|null $errcontext Deprecated, unused.
59 *
60 * @return never
61 * @throws Exception
62 */
63 // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
64 function ( $errno, $errstr, $errfile = null, $errline = null, $errcontext = null ) {
65 throw new Exception( "$errstr (file: $errfile; line: $errline)" );
66 }
67 );
68
69 $result = null;
70 $error_message = null;
71 try {
72 $result = $callable();
73 } catch ( Throwable $throwable ) {
74 $error_message = $throwable->getMessage();
75 }
76
77 restore_error_handler();
78 ini_set( 'display_errors', $old_display_errors );
79 error_reporting( $old_error_reporting );
80
81 if ( $error_message !== null ) {
82 throw new Exception( "$label failed: $error_message" );
83 }
84
85 return $result;
86 }
87
88 /**
89 * Return canonicalized absolute pathname, throw on warnings / errors.
90 *
91 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.realpath
92 * @see https://www.php.net/manual/en/function.realpath.php
93 *
94 * @param string $path Path being checked.
95 *
96 * @return string Canonicalized absolute pathname
97 * @throws Exception On invalid parameters, or if realpath() has returned false or thrown warnings.
98 */
99 public static function t_realpath( $path ) {
100 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
101 if ( ! $path ) {
102 throw new Exception( 'Filename for realpath() is unset' );
103 }
104
105 $label = "realpath( '$path' )";
106
107 $realpath_result = static::throw_on_warnings(
108 function () use ( $path ) {
109 return realpath( $path );
110 },
111 $label
112 );
113
114 if ( false === $realpath_result ) {
115 throw new Exception( "Unable to $label" );
116 }
117
118 return $realpath_result;
119 }
120
121 /**
122 * Check whether a file or directory exists, throw on warnings / errors.
123 *
124 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-exists
125 * @see https://www.php.net/manual/en/function.file-exists.php
126 *
127 * @param string $filename Path to the file or directory.
128 *
129 * @return bool True if the file or directory specified by filename exists; false otherwise.
130 * @throws Exception On invalid parameters, or if file_exists() has thrown warnings.
131 */
132 public static function t_file_exists( $filename ) {
133 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
134 if ( ! $filename ) {
135 throw new Exception( 'Filename for file_exists() is unset' );
136 }
137
138 return static::throw_on_warnings(
139 function () use ( $filename ) {
140 return file_exists( $filename );
141 },
142 "file_exists( '$filename' )"
143 );
144 }
145
146 /**
147 * Tell whether the filename (or a directory) is readable, throw on warnings / errors.
148 *
149 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-readable
150 * @see https://www.php.net/manual/en/function.is-readable.php
151 *
152 * @param string $filename Filename (or directory) to check.
153 *
154 * @return bool True if the filename (or a directory) exists and is readable, false otherwise.
155 * @throws Exception On invalid parameters, or if is_readable() has thrown warnings.
156 */
157 public static function t_is_readable( $filename ) {
158 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
159 if ( ! $filename ) {
160 throw new Exception( 'Filename for is_readable() is unset' );
161 }
162
163 return static::throw_on_warnings(
164 function () use ( $filename ) {
165 return is_readable( $filename );
166 },
167 "is_readable( '$filename' )"
168 );
169 }
170
171 /**
172 * Tell whether the filename (or a directory) is writable, throw on warnings / errors.
173 *
174 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-writable
175 * @see https://www.php.net/manual/en/function.is-writable.php
176 *
177 * @param string $filename Filename (or directory) to check.
178 *
179 * @return bool True if the filename (or a directory) exists and is writable, false otherwise.
180 * @throws Exception On invalid parameters, or if is_writable() has thrown warnings.
181 */
182 public static function t_is_writable( $filename ) {
183 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
184 if ( ! $filename ) {
185 throw new Exception( 'Filename for is_writable() is unset' );
186 }
187
188 return static::throw_on_warnings(
189 function () use ( $filename ) {
190 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
191 return is_writable( $filename );
192 },
193 "is_writable( '$filename' )"
194 );
195 }
196
197 /**
198 * Get file size, throw on warnings / errors.
199 *
200 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.filesize
201 * @see https://www.php.net/manual/en/function.filesize.php
202 *
203 * @param string $filename Path to the file.
204 *
205 * @return int Size of the file in bytes
206 * @throws Exception On invalid parameters, or if filesize() has thrown warnings.
207 */
208 public static function t_filesize( $filename ) {
209 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
210 if ( ! $filename ) {
211 throw new Exception( 'Filename for filesize() is unset' );
212 }
213
214 $label = "filesize( '$filename' )";
215
216 $filesize_result = static::throw_on_warnings(
217 function () use ( $filename ) {
218 return filesize( $filename );
219 },
220 $label
221 );
222
223 if ( false === $filesize_result ) {
224 throw new Exception( "Unable to $label" );
225 }
226
227 return $filesize_result;
228 }
229
230 /**
231 * Get file modification time, throw on warnings / errors.
232 *
233 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.filemtime
234 * @see https://www.php.net/manual/en/function.filemtime.php
235 *
236 * @param string $filename Path to the file.
237 *
238 * @return int The time the file was last modified
239 * @throws Exception On invalid parameters, or if filemtime() has thrown warnings.
240 */
241 public static function t_filemtime( $filename ) {
242 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
243 if ( ! $filename ) {
244 throw new Exception( 'Filename for filemtime() is unset' );
245 }
246
247 $label = "filemtime( '$filename' )";
248
249 $filemtime_result = static::throw_on_warnings(
250 function () use ( $filename ) {
251 return filemtime( $filename );
252 },
253 $label
254 );
255
256 if ( false === $filemtime_result ) {
257 throw new Exception( "Unable to $label" );
258 }
259
260 return $filemtime_result;
261 }
262
263 /**
264 * Tell whether the filename is a directory (follow symlinks), throw on warnings / errors.
265 *
266 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-dir
267 * @see https://www.php.net/manual/en/function.is-dir.php
268 *
269 * @param string $filename Path to the file.
270 *
271 * @return bool True if the filename (or the symlink's target) exists and is a directory, false otherwise.
272 * @throws Exception On invalid parameters, if is_dir() has thrown warnings, or has failed.
273 */
274 public static function t_is_dir( $filename ) {
275 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
276 if ( ! $filename ) {
277 throw new Exception( 'Filename for is_dir() is unset' );
278 }
279
280 return static::throw_on_warnings(
281 function () use ( $filename ) {
282 return is_dir( $filename );
283 },
284 "is_dir( '$filename' )"
285 );
286 }
287
288 /**
289 * Make a directory, throw on warnings / errors.
290 *
291 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.mkdir
292 * @see https://www.php.net/manual/en/function.mkdir.php
293 *
294 * @param string $directory Directory path.
295 * @param int $permissions Permissions of the newly created directory.
296 * @param bool $recursive If true, then any parent directories to the directory specified will also be created,
297 * with the same permissions.
298 *
299 * @return void
300 * @throws Exception On invalid parameters, if mkdir() has thrown warnings, or has failed.
301 */
302 public static function t_mkdir( $directory, $permissions = 0777, $recursive = false ) {
303 // PHP 5.x won't complain about permissions being null, so let's do it ourselves.
304 if ( $permissions === null ) {
305 throw new Exception( 'Permissions for mkdir() are unset' );
306 }
307
308 $label = "mkdir( '$directory', 0" . decoct( $permissions ) . ', ' . ( $recursive ? 'true' : 'false' ) . ' )';
309
310 $mkdir_result = static::throw_on_warnings(
311 function () use ( $directory, $permissions, $recursive ) {
312 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir
313 return mkdir( $directory, $permissions, $recursive );
314 },
315 $label
316 );
317
318 if ( false === $mkdir_result ) {
319 throw new Exception( "Unable to $label" );
320 }
321 }
322
323 /**
324 * List files and directories inside the specified path, throw on warnings / errors.
325 *
326 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.scandir
327 * @see https://www.php.net/manual/en/function.scandir.php
328 *
329 * @param string $directory Directory that will be scanned.
330 *
331 * @return string[] An array of filenames.
332 * @throws Exception If scandir() has thrown warnings, or has failed.
333 */
334 public static function t_scandir( $directory ) {
335
336 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
337 if ( ! $directory ) {
338 throw new Exception( 'Directory for scandir() is unset' );
339 }
340
341 $label = "scandir( '$directory' )";
342
343 $scandir_result = static::throw_on_warnings(
344 function () use ( $directory ) {
345 return scandir( $directory );
346 },
347 $label
348 );
349
350 if ( false === $scandir_result ) {
351 throw new Exception( "Unable to $label" );
352 }
353
354 return $scandir_result;
355 }
356
357 /**
358 * Remove a directory, throw on warnings / errors.
359 *
360 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.rmdir
361 * @see https://www.php.net/manual/en/function.rmdir.php
362 *
363 * @param string $directory Directory path.
364 *
365 * @return void
366 * @throws Exception On invalid parameters, if rmdir() has thrown warnings, or has failed.
367 */
368 public static function t_rmdir( $directory ) {
369 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
370 if ( ! $directory ) {
371 throw new Exception( 'Directory for mkdir() is unset' );
372 }
373
374 $label = "rmdir( '$directory' )";
375
376 $rmdir_result = static::throw_on_warnings(
377 function () use ( $directory ) {
378 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir
379 return rmdir( $directory );
380 },
381 $label
382 );
383
384 if ( false === $rmdir_result ) {
385 throw new Exception( "Unable to $label" );
386 }
387 }
388
389 /**
390 * Delete a file, throw on warnings / errors.
391 *
392 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.unlink
393 * @see https://www.php.net/manual/en/function.unlink.php
394 *
395 * @param string $filename Path to the file.
396 *
397 * @return void
398 * @throws Exception If unlink() has thrown warnings, or has failed.
399 */
400 public static function t_unlink( $filename ) {
401
402 $label = "unlink( '$filename' )";
403
404 $unlink_result = static::throw_on_warnings(
405 function () use ( $filename ) {
406 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
407 return unlink( $filename );
408 },
409 $label
410 );
411
412 if ( false === $unlink_result ) {
413 throw new Exception( "Unable to $label" );
414 }
415 }
416
417 /**
418 * Write data to a file, throw on warnings / errors.
419 *
420 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-put-contents
421 * @see https://www.php.net/manual/en/function.file-put-contents.php
422 *
423 * @param string $filename Path to the file where to write the data.
424 * @param string $data The data to write.
425 *
426 * @return void
427 * @throws Exception If file_put_contents() has thrown warnings, has failed, or if it didn't write all the bytes.
428 */
429 public static function t_file_put_contents( $filename, $data ) {
430
431 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
432 if ( ! $filename ) {
433 throw new Exception( 'Filename for f_p_c() is unset' );
434 }
435 if ( $data === null ) {
436 throw new Exception( 'Data to write is null' );
437 }
438
439 $data_length = strlen( $data );
440
441 // Weird label is intentional, otherwise security scanners find this label suspicious.
442 $label = "f_p_c( '$filename', $data_length bytes of data )";
443
444 $number_of_bytes_written = static::throw_on_warnings(
445 function () use ( $filename, $data ) {
446 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
447 return file_put_contents( $filename, $data );
448 },
449 $label
450 );
451
452 if ( false === $number_of_bytes_written ) {
453 throw new Exception( "Unable to $label" );
454 }
455
456 if ( $number_of_bytes_written !== $data_length ) {
457 throw new Exception(
458 "$label was expected to write $data_length bytes, but wrote $number_of_bytes_written bytes"
459 );
460 }
461 }
462
463 /**
464 * Read entire file into a string, throw on warnings / errors.
465 *
466 * @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-get-contents
467 * @see https://www.php.net/manual/en/function.file-get-contents.php
468 *
469 * @param string $filename Name of the file to read.
470 *
471 * @return string The read data.
472 * @throws Exception If file_get_contents() has thrown warnings, or has failed.
473 */
474 public static function t_file_get_contents( $filename ) {
475
476 // PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
477 if ( ! $filename ) {
478 throw new Exception( 'Filename for f_g_c() is unset' );
479 }
480
481 // Weird label is intentional, otherwise security scanners find this label suspicious.
482 $label = "f_g_c( '$filename' )";
483
484 $fgc_result = static::throw_on_warnings(
485 function () use ( $filename ) {
486 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
487 return file_get_contents( $filename );
488 },
489 $label
490 );
491
492 if ( false === $fgc_result ) {
493 throw new Exception( "Unable to $label" );
494 }
495
496 return $fgc_result;
497 }
498 }
499