Skip to content

Commit

Permalink
Merge pull request #215 from namithj/#187
Browse files Browse the repository at this point in the history
  • Loading branch information
namithj authored Jan 10, 2025
2 parents 0cfc765 + 500676e commit 53ae2ce
Show file tree
Hide file tree
Showing 11 changed files with 684 additions and 197 deletions.
2 changes: 1 addition & 1 deletion assets/js/aspire-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class ViewLog {
jQuery.ajax(parameters)
.done(function (response) {
if ((true == response.success) && ('' != response.data.content)) {
let lines = response.data.content.split(aspireupdate.line_ending);
let lines = response.data.content;
jQuery.each(lines, function (index, line) {
jQuery('<div>')
.append(
Expand Down
118 changes: 40 additions & 78 deletions includes/class-debug.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ class Debug {
*/
private static $log_file = 'debug-aspire-update.log';

/**
* The filesystem.
*
* @var Filesystem_Direct
*/
private static $filesystem;

/**
* Get the Log file path.
*
Expand All @@ -31,79 +38,41 @@ private static function get_file_path() {
/**
* Initializes the WordPress Filesystem.
*
* @return WP_Filesystem_Base|false The filesystem object or false on failure.
* @return Filesystem_Direct The filesystem object.
*/
private static function init_filesystem() {
global $wp_filesystem;

if ( ! $wp_filesystem ) {
if ( ! self::$filesystem instanceof Filesystem_Direct ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
WP_Filesystem();
self::$filesystem = new Filesystem_Direct( false );
}

return $wp_filesystem;
}

/**
* Checks the filesystem status and logs error to debug log.
*
* @param WP_Filesystem_Base $wp_filesystem The filesystem object.
*
* @return boolean true on success and false on failure.
*/
private static function verify_filesystem( $wp_filesystem ) {
if ( ! $wp_filesystem ) {
if (
defined( 'WP_DEBUG' ) &&
( true === WP_DEBUG ) &&
defined( 'WP_DEBUG_LOG' ) &&
( true === WP_DEBUG_LOG )
) {
// phpcs:disable WordPress.PHP.DevelopmentFunctions
/**
* Log error in file write fails only if debug is set to true. This is a valid use case.
*/
error_log( 'AspireUpdate - Could not open or write to the file system. Check file system permissions to debug log directory.' ); // @codeCoverageIgnore
// phpcs:enable
}
return false;
}
return true;
return self::$filesystem;
}

/**
* Get the content of the log file truncated upto N number of lines.
*
* @param integer $limit Max no of lines to return. Defaults to a 1000 lines.
*
* @return string|WP_Error The File content truncate upto the number of lines set in the limit parameter.
* @return array|WP_Error An array of lines in the file, limited to $limit, or a WP_Error object on failure.
*/
public static function read( $limit = 1000 ) {
$wp_filesystem = self::init_filesystem();
$file_path = self::get_file_path();
if ( ! self::verify_filesystem( $wp_filesystem ) || ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_readable( $file_path ) ) {

if ( ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_readable( $file_path ) ) {
return new \WP_Error( 'not_readable', __( 'Error: Unable to read the log file.', 'aspireupdate' ) );
}

$file_content = $wp_filesystem->get_contents_array( $file_path );
$content = '';
$index = 0;
foreach ( $file_content as $file_content_lines ) {
if ( ( $index < $limit ) ) {
$content .= $file_content_lines . PHP_EOL;
++$index;
}
}
if ( '' === trim( $content ) ) {
$content = esc_html__( '*****Log file is empty.*****', 'aspireupdate' );
} elseif ( $limit < count( $file_content ) ) {
$content .= PHP_EOL . sprintf(
/* translators: 1: The number of lines at which the content was truncated. */
esc_html__( '*****Log truncated at %s lines.*****', 'aspireupdate' ),
$limit
);
$file_content = $wp_filesystem->get_contents_array( $file_path, $limit, true );

if ( ( false === $file_content ) || ( 0 === count( array_filter( $file_content ) ) ) ) {
$file_content = [ esc_html__( '*****Log file is empty.*****', 'aspireupdate' ) ];
}
return $content;

return $file_content;
}

/**
Expand All @@ -114,7 +83,8 @@ public static function read( $limit = 1000 ) {
public static function clear() {
$wp_filesystem = self::init_filesystem();
$file_path = self::get_file_path();
if ( ! self::verify_filesystem( $wp_filesystem ) || ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_writable( $file_path ) ) {

if ( ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_writable( $file_path ) ) {
return new \WP_Error( 'not_accessible', __( 'Error: Unable to access the log file.', 'aspireupdate' ) );
}

Expand All @@ -133,30 +103,22 @@ public static function clear() {
* @param string $type The log level ('string', 'request', 'response').
*/
public static function log( $message, $type = 'string' ) {
$wp_filesystem = self::init_filesystem();
if ( self::verify_filesystem( $wp_filesystem ) ) {
$timestamp = gmdate( 'Y-m-d H:i:s' );
$formatted_message = sprintf(
'[%s] [%s]: %s',
$timestamp,
strtoupper( $type ),
self::format_message( $message )
) . PHP_EOL;

$file_path = self::get_file_path();

$content = '';
if ( $wp_filesystem->exists( $file_path ) ) {
if ( $wp_filesystem->is_readable( $file_path ) ) {
$content = $wp_filesystem->get_contents( $file_path );
}
}
$wp_filesystem->put_contents(
$file_path,
$formatted_message . $content,
FS_CHMOD_FILE
);
}
$wp_filesystem = self::init_filesystem();
$timestamp = gmdate( 'Y-m-d H:i:s' );
$formatted_message = sprintf(
'[%s] [%s]: %s',
$timestamp,
strtoupper( $type ),
self::format_message( $message )
) . PHP_EOL;

$file_path = self::get_file_path();
$wp_filesystem->put_contents(
$file_path,
$formatted_message,
FS_CHMOD_FILE,
'a'
);
}

/**
Expand Down
104 changes: 104 additions & 0 deletions includes/class-filesystem-direct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php
/**
* The Class for WordPress Direct Filesystem with optimized read and write routines.
*
* @package aspire-update
*/

namespace AspireUpdate;

/**
* The Class for WordPress Direct Filesystem with optimized read and write routines.
*/
class Filesystem_Direct extends \WP_Filesystem_Direct {

/**
* Reads entire file into an array with options for limiting the number of lines and direction from the the lines are counted.
*
* @param string $file Path to the file.
* @param int $number_of_lines The number of lines to read. Default is -1 (read all lines).
* @param bool $count_bottom_to_top Count the lines from the bottom up. Default is false (count from top to bottom).
*
* @return array|false File contents in an array on success, false on failure.
*/
public function get_contents_array( $file, $number_of_lines = -1, $count_bottom_to_top = false ) {
if ( ! $this->exists( $file ) ) {
return false;
}

if ( -1 === $number_of_lines ) {
return @file( $file );
}

$handle = @fopen( $file, 'r' );
if ( ! $handle ) {
return false;
}

$lines = [];
$line_count = 0;

// phpcs:disable Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
/**
* This is a valid and intentional use.
*/
while ( ( $line = fgets( $handle ) ) !== false ) {
$lines[] = trim( $line );
++$line_count;

if ( $count_bottom_to_top ) {
if ( $number_of_lines > 0 && $line_count > $number_of_lines ) {
array_shift( $lines );
}
} elseif ( $number_of_lines > 0 && $line_count >= $number_of_lines ) {
break;
}
}
// phpcs:enable

fclose( $handle );

return $lines;
}

/**
* Write contents to a file with additional modes.
*
* @param string $file The path to the file.
* @param string $contents The content to write.
* @param int|false $mode Optional. The file permissions as octal number, usually 0644.
* Default false.
* @param string $write_mode The write mode:
* 'w' - Overwrite the file (default).
* 'a' - Append to the file.
* 'x' - Create a new file and write, fail if the file exists.
* 'c' - Open the file for writing, but do not truncate.
* @return bool True on success, false on failure.
*/
public function put_contents( $file, $contents, $mode = false, $write_mode = 'w' ) {
$valid_write_modes = [ 'w', 'a', 'x', 'c' ];
if ( ! in_array( $write_mode, $valid_write_modes, true ) ) {
return false;
}

$handle = @fopen( $file, $write_mode );
if ( ! $handle ) {
return false;
}

mbstring_binary_safe_encoding();
$data_length = strlen( $contents );
$bytes_written = fwrite( $handle, $contents );
reset_mbstring_encoding();

fclose( $handle );

if ( $data_length !== $bytes_written ) {
return false;
}

$this->chmod( $file, $mode );

return true;
}
}
23 changes: 23 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@
<!-- Enforce short syntax arrays. -->
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>

<!-- Exclude custom Filesystem API implementations from certain sniffs. -->
<rule ref="WordPress.WP.AlternativeFunctions.file_system_operations_fopen">
<exclude-pattern>includes/class-filesystem-direct\.php</exclude-pattern>
</rule>

<rule ref="WordPress.WP.AlternativeFunctions.file_system_operations_fclose">
<exclude-pattern>includes/class-filesystem-direct\.php</exclude-pattern>
</rule>

<rule ref="WordPress.WP.AlternativeFunctions.file_system_operations_fwrite">
<exclude-pattern>includes/class-filesystem-direct\.php</exclude-pattern>
</rule>

<rule ref="WordPress.PHP.NoSilencedErrors.Discouraged">
<exclude-pattern>includes/class-filesystem-direct\.php</exclude-pattern>
</rule>

<!-- Exclude test classes from naming conventions. -->
<rule ref="WordPress.Files.FileName.InvalidClassFileName">
<exclude-pattern>/tests/phpunit/*</exclude-pattern>
Expand All @@ -82,4 +99,10 @@
<rule ref="WordPress.WP.AlternativeFunctions.unlink_unlink">
<exclude-pattern>/tests/phpunit/*</exclude-pattern>
</rule>
<rule ref="WordPress.WP.AlternativeFunctions.file_system_operations_mkdir">
<exclude-pattern>/tests/phpunit/*</exclude-pattern>
</rule>
<rule ref="WordPress.WP.AlternativeFunctions.file_system_operations_rmdir">
<exclude-pattern>/tests/phpunit/*</exclude-pattern>
</rule>
</ruleset>
30 changes: 15 additions & 15 deletions tests/phpunit/includes/Debug_UnitTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,26 @@ abstract class Debug_UnitTestCase extends WP_UnitTestCase {
protected static $log_file;

/**
* Previously created filesystems.
* The class for use with the Reflection API.
*
* @var array
* @var \ReflectionClass
*/
protected static $filesystems = [];
protected static $reflection;

/**
* The original value of $wp_filesystem before any tests run.
* The default filesystem.
*
* @var WP_Filesystem_Base|null|false False if not already set.
* @var \AspireUpdate\Filesystem_Direct|null
*/
protected static $default_filesystem;

/**
* Previously created filesystems.
*
* @var array
*/
protected static $filesystems = [];

/**
* Gets the log file's path, and deletes if it exists before any tests run.
* Backs up the default filesystem.
Expand All @@ -48,11 +55,8 @@ public static function set_up_before_class() {
unlink( self::$log_file );
}

if ( isset( $GLOBALS['wp_filesystem'] ) ) {
self::$default_filesystem = $GLOBALS['wp_filesystem'];
} else {
self::$default_filesystem = false;
}
self::$reflection = new ReflectionClass( '\AspireUpdate\Debug' );
self::$default_filesystem = self::$reflection->getStaticPropertyValue( 'filesystem' );
}

/**
Expand Down Expand Up @@ -83,11 +87,7 @@ public function tear_down() {
unlink( self::$log_file );
}

if ( false === self::$default_filesystem ) {
unset( $GLOBALS['wp_filesystem'] );
} else {
$GLOBALS['wp_filesystem'] = self::$default_filesystem;
}
self::$reflection->setStaticPropertyValue( 'filesystem', self::$default_filesystem );

parent::tear_down();
}
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/includes/class-ap-fake-filesystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';

class AP_FakeFilesystem extends WP_Filesystem_Direct {
class AP_FakeFilesystem extends AspireUpdate\Filesystem_Direct {
/**
* Whether paths should exist.
*
Expand Down
Loading

0 comments on commit 53ae2ce

Please sign in to comment.