laipower/wp-content/plugins/jetpack-protect/jetpack_vendor/automattic/jetpack-backup-helper-scrip.../src/class-throw-on-errors.php

497 lines
16 KiB
PHP

<?php // phpcs:disable Squiz.Commenting.FileComment.Missing
// phpcs:disable WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting
// phpcs:disable WordPress.PHP.IniSet.display_errors_Disallowed
// After changing this file, consider increasing the version number ("VXXX") in all the files using this namespace, in
// order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
// to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
// are installed, or in some other cases).
namespace Automattic\Jetpack\Backup\V0001;
use Exception;
use Throwable;
/**
* Wrappers for functions which throw an exception on errors and warnings instead of silently continuing operation.
*
* PHP is pretty lax with error reporting when doing I/O operations, e.g. a typical I/O helper function returns false,
* null, and/or emits a warning, but the whole PHP application continues operation. It's for the caller of the I/O
* helper function to test the returned result of the function, and notice + act upon I/O errors.
*
* We really want to know about each and every I/O error. Therefore, this class provides wrappers for some common
* (mostly) I/O operations that throw an exception on errors instead of just returning false, null, or something else.
* This wrapper class treats warnings as errors too.
*
* Given that static method names are similar to the ones used by PHP, they're prefixed with "t_" to not erroneously
* trigger various security scanners.
*/
class Throw_On_Errors {
/**
* Execute a callable, throw an exception (together with a descriptive label) on PHP warnings / errors.
*
* @param callable $callable Callable to execute.
* @param string $label Label to add to the thrown exception to clarify what was attempted.
*
* @return mixed Callable's return value, if any.
* @throws Exception On warnings thrown by the callable.
* @noinspection PhpUnusedParameterInspection
*/
private static function throw_on_warnings( $callable, $label ) {
$old_error_reporting = error_reporting( - 1 );
$old_display_errors = ini_set( 'display_errors', 'stderr' );
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
set_error_handler(
/**
* Temporary error handler.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.set-error-handler
* @see https://www.php.net/manual/en/function.set-error-handler.php
*
* @param int $errno Level of the error raised.
* @param string $errstr Error message.
* @param string|null $errfile Filename that the error was raised in.
* @param int|null $errline Line number where the error was raised.
* @param array|null $errcontext Deprecated, unused.
*
* @return mixed
* @throws Exception
*/
// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
function ( $errno, $errstr, $errfile = null, $errline = null, $errcontext = null ) {
throw new Exception( "$errstr (file: $errfile; line: $errline)" );
}
);
$result = null;
$error_message = null;
try {
$result = $callable();
} catch ( Throwable $throwable ) {
$error_message = $throwable->getMessage();
}
restore_error_handler();
ini_set( 'display_errors', $old_display_errors );
error_reporting( $old_error_reporting );
if ( $error_message !== null ) {
throw new Exception( "$label failed: $error_message" );
}
return $result;
}
/**
* Return canonicalized absolute pathname, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.realpath
* @see https://www.php.net/manual/en/function.realpath.php
*
* @param string $path Path being checked.
*
* @return string Canonicalized absolute pathname
* @throws Exception On invalid parameters, or if realpath() has returned false or thrown warnings.
*/
public static function t_realpath( $path ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $path ) {
throw new Exception( 'Filename for realpath() is unset' );
}
$label = "realpath( '$path' )";
$realpath_result = static::throw_on_warnings(
function () use ( $path ) {
return realpath( $path );
},
$label
);
if ( false === $realpath_result ) {
throw new Exception( "Unable to $label" );
}
return $realpath_result;
}
/**
* Check whether a file or directory exists, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-exists
* @see https://www.php.net/manual/en/function.file-exists.php
*
* @param string $filename Path to the file or directory.
*
* @return bool True if the file or directory specified by filename exists; false otherwise.
* @throws Exception On invalid parameters, or if file_exists() has thrown warnings.
*/
public static function t_file_exists( $filename ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for file_exists() is unset' );
}
return static::throw_on_warnings(
function () use ( $filename ) {
return file_exists( $filename );
},
"file_exists( '$filename' )"
);
}
/**
* Tell whether the filename (or a directory) is readable, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-readable
* @see https://www.php.net/manual/en/function.is-readable.php
*
* @param string $filename Filename (or directory) to check.
*
* @return bool True if the filename (or a directory) exists and is readable, false otherwise.
* @throws Exception On invalid parameters, or if is_readable() has thrown warnings.
*/
public static function t_is_readable( $filename ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for is_readable() is unset' );
}
return static::throw_on_warnings(
function () use ( $filename ) {
return is_readable( $filename );
},
"is_readable( '$filename' )"
);
}
/**
* Tell whether the filename (or a directory) is writable, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-writable
* @see https://www.php.net/manual/en/function.is-writable.php
*
* @param string $filename Filename (or directory) to check.
*
* @return bool True if the filename (or a directory) exists and is writable, false otherwise.
* @throws Exception On invalid parameters, or if is_writable() has thrown warnings.
*/
public static function t_is_writable( $filename ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for is_writable() is unset' );
}
return static::throw_on_warnings(
function () use ( $filename ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
return is_writable( $filename );
},
"is_writable( '$filename' )"
);
}
/**
* Get file size, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.filesize
* @see https://www.php.net/manual/en/function.filesize.php
*
* @param string $filename Path to the file.
*
* @return int Size of the file in bytes
* @throws Exception On invalid parameters, or if filesize() has thrown warnings.
*/
public static function t_filesize( $filename ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for filesize() is unset' );
}
$label = "filesize( '$filename' )";
$filesize_result = static::throw_on_warnings(
function () use ( $filename ) {
return filesize( $filename );
},
$label
);
if ( false === $filesize_result ) {
throw new Exception( "Unable to $label" );
}
return $filesize_result;
}
/**
* Get file modification time, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.filemtime
* @see https://www.php.net/manual/en/function.filemtime.php
*
* @param string $filename Path to the file.
*
* @return int The time the file was last modified
* @throws Exception On invalid parameters, or if filemtime() has thrown warnings.
*/
public static function t_filemtime( $filename ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for filemtime() is unset' );
}
$label = "filemtime( '$filename' )";
$filemtime_result = static::throw_on_warnings(
function () use ( $filename ) {
return filemtime( $filename );
},
$label
);
if ( false === $filemtime_result ) {
throw new Exception( "Unable to $label" );
}
return $filemtime_result;
}
/**
* Tell whether the filename is a directory (follow symlinks), throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-dir
* @see https://www.php.net/manual/en/function.is-dir.php
*
* @param string $filename Path to the file.
*
* @return bool True if the filename (or the symlink's target) exists and is a directory, false otherwise.
* @throws Exception On invalid parameters, if is_dir() has thrown warnings, or has failed.
*/
public static function t_is_dir( $filename ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for is_dir() is unset' );
}
return static::throw_on_warnings(
function () use ( $filename ) {
return is_dir( $filename );
},
"is_dir( '$filename' )"
);
}
/**
* Make a directory, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.mkdir
* @see https://www.php.net/manual/en/function.mkdir.php
*
* @param string $directory Directory path.
* @param int $permissions Permissions of the newly created directory.
* @param bool $recursive If true, then any parent directories to the directory specified will also be created,
* with the same permissions.
*
* @return void
* @throws Exception On invalid parameters, if mkdir() has thrown warnings, or has failed.
*/
public static function t_mkdir( $directory, $permissions = 0777, $recursive = false ) {
// PHP 5.x won't complain about permissions being null, so let's do it ourselves.
if ( $permissions === null ) {
throw new Exception( 'Permissions for mkdir() are unset' );
}
$label = "mkdir( '$directory', 0" . decoct( $permissions ) . ', ' . ( $recursive ? 'true' : 'false' ) . ' )';
$mkdir_result = static::throw_on_warnings(
function () use ( $directory, $permissions, $recursive ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir
return mkdir( $directory, $permissions, $recursive );
},
$label
);
if ( false === $mkdir_result ) {
throw new Exception( "Unable to $label" );
}
}
/**
* List files and directories inside the specified path, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.scandir
* @see https://www.php.net/manual/en/function.scandir.php
*
* @param string $directory Directory that will be scanned.
*
* @return string An array of filenames.
* @throws Exception If scandir() has thrown warnings, or has failed.
*/
public static function t_scandir( $directory ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $directory ) {
throw new Exception( 'Directory for scandir() is unset' );
}
$label = "scandir( '$directory' )";
$scandir_result = static::throw_on_warnings(
function () use ( $directory ) {
return scandir( $directory );
},
$label
);
if ( false === $scandir_result ) {
throw new Exception( "Unable to $label" );
}
return $scandir_result;
}
/**
* Remove a directory, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.rmdir
* @see https://www.php.net/manual/en/function.rmdir.php
*
* @param string $directory Directory path.
*
* @return void
* @throws Exception On invalid parameters, if rmdir() has thrown warnings, or has failed.
*/
public static function t_rmdir( $directory ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $directory ) {
throw new Exception( 'Directory for mkdir() is unset' );
}
$label = "rmdir( '$directory' )";
$rmdir_result = static::throw_on_warnings(
function () use ( $directory ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir
return rmdir( $directory );
},
$label
);
if ( false === $rmdir_result ) {
throw new Exception( "Unable to $label" );
}
}
/**
* Delete a file, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.unlink
* @see https://www.php.net/manual/en/function.unlink.php
*
* @param string $filename Path to the file.
*
* @return void
* @throws Exception If unlink() has thrown warnings, or has failed.
*/
public static function t_unlink( $filename ) {
$label = "unlink( '$filename' )";
$unlink_result = static::throw_on_warnings(
function () use ( $filename ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
return unlink( $filename );
},
$label
);
if ( false === $unlink_result ) {
throw new Exception( "Unable to $label" );
}
}
/**
* Write data to a file, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-put-contents
* @see https://www.php.net/manual/en/function.file-put-contents.php
*
* @param string $filename Path to the file where to write the data.
* @param string $data The data to write.
*
* @return void
* @throws Exception If file_put_contents() has thrown warnings, has failed, or if it didn't write all the bytes.
*/
public static function t_file_put_contents( $filename, $data ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for file_put_contents() is unset' );
}
if ( $data === null ) {
throw new Exception( 'Data to write is null' );
}
$data_length = strlen( $data );
$label = "file_put_contents( '$filename', $data_length bytes of data )";
$number_of_bytes_written = static::throw_on_warnings(
function () use ( $filename, $data ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
return file_put_contents( $filename, $data );
},
$label
);
if ( false === $number_of_bytes_written ) {
throw new Exception( "Unable to $label" );
}
if ( $number_of_bytes_written !== $data_length ) {
throw new Exception(
"$label was expected to write $data_length bytes, but wrote $number_of_bytes_written bytes"
);
}
}
/**
* Read entire file into a string, throw on warnings / errors.
*
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-get-contents
* @see https://www.php.net/manual/en/function.file-get-contents.php
*
* @param string $filename Name of the file to read.
*
* @return string The read data.
* @throws Exception If file_get_contents() has thrown warnings, or has failed.
*/
public static function t_file_get_contents( $filename ) {
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
if ( ! $filename ) {
throw new Exception( 'Filename for file_get_contents() is unset' );
}
$label = "file_get_contents( '$filename' )";
$file_get_contents_result = static::throw_on_warnings(
function () use ( $filename ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
return file_get_contents( $filename );
},
$label
);
if ( false === $file_get_contents_result ) {
throw new Exception( "Unable to $label" );
}
return $file_get_contents_result;
}
}