Files
2026-04-28 15:13:50 +02:00

330 lines
8.7 KiB
PHP

<?php
/**
* WordPress FileSystem compatibility class.
*
* @since 4.2
*/
namespace WPO\IPS\Compatibility;
defined( 'ABSPATH' ) or exit;
if ( ! class_exists( '\\WPO\\IPS\\Compatibility\\FileSystem' ) ) :
class FileSystem {
/**
* Default filesystem method.
*/
public const FILESYSTEM_DEFAULT = 'php';
/**
* Available filesystem methods.
*/
public const FILESYSTEM_METHODS = array( 'php', 'wp' );
/**
* Filesystem method enabled.
* @var string
*/
public string $system_enabled = self::FILESYSTEM_DEFAULT;
/**
* Suppress errors.
* @var bool
*/
public bool $suppress_errors = false;
/**
* WP_Filesystem instance.
* @var \WP_Filesystem_Direct|null
*/
public ?\WP_Filesystem_Direct $wp_filesystem = null;
/**
* Singleton instance.
* @var self|null
*/
protected static ?self $_instance = null;
/**
* Singleton instance.
* @return self
*/
public static function instance() {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Constructor
*/
public function __construct() {
$this->suppress_errors = apply_filters( 'wpo_wcpdf_file_system_suppress_errors', true );
$debug_settings = get_option( 'wpo_wcpdf_settings_debug', array() );
if ( ! is_array( $debug_settings ) ) {
$debug_settings = array();
}
$debug_settings['file_system_method'] = apply_filters( // Allow overriding the filesystem method via filter
'wpo_wcpdf_filesystem_method',
$debug_settings['file_system_method'] ?? self::FILESYSTEM_DEFAULT
);
$this->system_enabled = in_array( $debug_settings['file_system_method'], self::FILESYSTEM_METHODS, true )
? $debug_settings['file_system_method']
: self::FILESYSTEM_DEFAULT;
if ( $this->is_wp_filesystem() ) {
$this->initialize_wp_filesystem();
} elseif ( ! defined( 'FS_CHMOD_FILE' ) ) {
define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
}
}
/**
* Check if WP_Filesystem is enabled.
* @return bool
*/
public function is_wp_filesystem(): bool {
return ( 'wp' === $this->system_enabled );
}
/**
* Check if PHP file functions are being used.
* @return bool
*/
public function is_php_filesystem(): bool {
return ( 'php' === $this->system_enabled );
}
/**
* Initialize WP_Filesystem
* @return void
*/
public function initialize_wp_filesystem(): void {
// Force the use of the direct filesystem method if not already defined
if ( ! defined( 'FS_METHOD' ) ) {
define( 'FS_METHOD', 'direct' );
}
require_once ABSPATH . 'wp-admin/includes/file.php';
global $wp_filesystem;
if ( ! WP_Filesystem() || ! $wp_filesystem || ! is_a( $wp_filesystem, '\WP_Filesystem_Direct' ) ) {
wcpdf_log_error( 'WP_Filesystem initialization failed or not using direct method. Falling back to PHP methods.', 'warning' );
$this->system_enabled = $this->change_setting_value( 'php' );
return;
}
$this->wp_filesystem = $wp_filesystem;
}
/**
* Change the filesystem setting value
* @param string $method
* @return string
*/
protected function change_setting_value( string $method ): string {
$debug_settings = get_option( 'wpo_wcpdf_settings_debug', array() );
$debug_settings['file_system_method'] = in_array( $method, self::FILESYSTEM_METHODS, true ) ? $method : self::FILESYSTEM_DEFAULT;
update_option( 'wpo_wcpdf_settings_debug', $debug_settings );
return $debug_settings['file_system_method'];
}
/**
* Get file contents
* @param string $filename
* @return string|bool
*/
public function get_contents( string $filename ) {
if ( empty( $filename ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->get_contents( $filename ) :
( $this->suppress_errors ? @file_get_contents( $filename ) : file_get_contents( $filename ) );
}
/**
* Check if file is readable
* @param string $filename
* @return bool
*/
public function is_readable( string $filename ): bool {
if ( empty( $filename ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->is_readable( $filename ) :
( $this->suppress_errors ? @is_readable( $filename ) : is_readable( $filename ) );
}
/**
* Write file contents
* @param string $filename
* @param string $contents
* @param int|false $mode
* @return int|bool
*/
public function put_contents( string $filename, string $contents, $mode = false ) {
if ( empty( $filename ) || empty( $contents ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->put_contents( $filename, $contents, $mode ) :
( $this->suppress_errors ? @file_put_contents( $filename, $contents ) : file_put_contents( $filename, $contents ) );
}
/**
* Output a file directly to the response.
*
* @param string $filename
* @return bool
*/
public function output_file( string $filename ): bool {
if ( empty( $filename ) ) {
return false;
}
// WP_Filesystem_Direct doesn't provide a streaming API, so fall back to get_contents().
if ( $this->is_wp_filesystem() ) {
$contents = $this->wp_filesystem->get_contents( $filename );
if ( false === $contents ) {
return false;
}
echo $contents; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return true;
}
$handle = $this->suppress_errors ? @fopen( $filename, 'rb' ) : fopen( $filename, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
if ( false === $handle ) {
return false;
}
while ( ! feof( $handle ) ) {
$buffer = fread( $handle, 8192 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread
if ( false === $buffer ) {
fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
return false;
}
echo $buffer; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
return true;
}
/**
* Check if file exists
* @param string $filename
* @return bool
*/
public function exists( string $filename ): bool {
if ( empty( $filename ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->exists( $filename ) :
( $this->suppress_errors ? @file_exists( $filename ) : file_exists( $filename ) );
}
/**
* Check if directory exists
* @param string $filename
* @return bool
*/
public function is_dir( string $filename ): bool {
if ( empty( $filename ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->is_dir( $filename ) :
( $this->suppress_errors ? @is_dir( $filename ) : is_dir( $filename ) );
}
/**
* Create a directory
* @param string $path
* @return bool
*/
public function mkdir( string $path ): bool {
if ( empty( $path ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->mkdir( $path ) :
( $this->suppress_errors ? @mkdir( $path ) : mkdir( $path ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir
}
/**
* Check if file is writable
* @param string $filename
* @return bool
*/
public function is_writable( string $filename ): bool {
if ( empty( $filename ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->is_writable( $filename ) :
( 'Windows' === PHP_OS_FAMILY ?
wp_is_writable( $filename ) :
( $this->suppress_errors ? @is_writable( $filename ) : is_writable( $filename ) ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
);
}
/**
* Delete a file
* @param string $filename
* @param bool $recursive
* @return bool
*/
public function delete( string $filename, bool $recursive = false ): bool {
if ( empty( $filename ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->delete( $filename, $recursive ) :
( $this->suppress_errors ? @unlink( $filename ) : unlink( $filename ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
}
/**
* Get directory listing
* @param string $path
* @return array|bool
*/
public function dirlist( string $path ) {
if ( empty( $path ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->dirlist( $path ) :
( $this->suppress_errors ? @scandir( $path ) : scandir( $path ) );
}
/**
* Get file modification time
* @param string $filename
* @return int|bool
*/
public function mtime( string $filename ) {
if ( empty( $filename ) ) {
return false;
}
return $this->is_wp_filesystem() ?
$this->wp_filesystem->mtime( $filename ) :
( $this->suppress_errors ? @filemtime( $filename ) : filemtime( $filename ) );
}
}
endif; // Class exists check