Fix code sniffer errors in CodeHacker and related classes.
This commit is contained in:
parent
57845ef8b8
commit
f0b0822c1c
|
@ -1,7 +1,19 @@
|
|||
<?php
|
||||
/**
|
||||
* CodeHacker class file.
|
||||
*
|
||||
* @package WooCommerce/Testing
|
||||
*/
|
||||
|
||||
//phpcs:disable Squiz.Commenting.FunctionComment.Missing, Squiz.Commenting.VariableComment.Missing
|
||||
//phpcs:disable WordPress.WP.AlternativeFunctions, WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
|
||||
namespace Automattic\WooCommerce\Testing\CodeHacking;
|
||||
|
||||
use \ReflectionObject;
|
||||
use \ReflectionFunction;
|
||||
use \ReflectionException;
|
||||
|
||||
/**
|
||||
* CodeHacker - allows to hack (alter on the fly) the content of PHP code files.
|
||||
*
|
||||
|
@ -24,19 +36,19 @@ class CodeHacker {
|
|||
|
||||
const PROTOCOL = 'file';
|
||||
|
||||
/** @var resource|null */
|
||||
public $context;
|
||||
|
||||
/** @var resource|null */
|
||||
private $handle;
|
||||
|
||||
/** @var array|null */
|
||||
private static $pathWhitelist = array();
|
||||
private static $path_white_list = array();
|
||||
|
||||
private static $hacks = array();
|
||||
|
||||
private static $enabled = false;
|
||||
|
||||
/**
|
||||
* Enable the code hacker.
|
||||
*/
|
||||
public static function enable() {
|
||||
if ( ! self::$enabled ) {
|
||||
stream_wrapper_unregister( self::PROTOCOL );
|
||||
|
@ -45,6 +57,9 @@ class CodeHacker {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the code hacker.
|
||||
*/
|
||||
public static function restore() {
|
||||
if ( self::$enabled ) {
|
||||
stream_wrapper_restore( self::PROTOCOL );
|
||||
|
@ -52,26 +67,65 @@ class CodeHacker {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all the registered hacks.
|
||||
*/
|
||||
public static function clear_hacks() {
|
||||
self::$hacks = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the code hacker is enabled.
|
||||
*
|
||||
* @return bool True if the code hacker is enabled.
|
||||
*/
|
||||
public static function is_enabled() {
|
||||
return self::$enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new hack.
|
||||
*
|
||||
* @param mixed $hack A function with signature "hack($code, $path)" or an object containing a method with that signature.
|
||||
* @throws \Exception Invalid input.
|
||||
*/
|
||||
public static function add_hack( $hack ) {
|
||||
if ( ! is_callable( $hack ) && ! is_object( $hack ) ) {
|
||||
throw new Exception( "Hacks must be either functions, or objects having a 'process(\$text, \$path)' method." );
|
||||
throw new \Exception( "Hacks must be either functions, or objects having a 'process(\$text, \$path)' method." );
|
||||
}
|
||||
|
||||
// TODO: Check that callbacks have at least two parameters (if they have more, they must be optional); and that objects have a "process" method with the same condition.
|
||||
if ( ! self::is_valid_hack_callback( $hack ) && ! self::is_valid_hack_object( $hack ) ) {
|
||||
throw new \Exception( "CodeHacker::addhack: hacks must be either a function with a 'hack(\$code,\$path)' signature, or an object containing a public method 'hack' with that signature. " );
|
||||
}
|
||||
|
||||
self::$hacks[] = $hack;
|
||||
}
|
||||
|
||||
public static function setWhitelist( array $pathWhitelist ) {
|
||||
self::$pathWhitelist = $pathWhitelist;
|
||||
private static function is_valid_hack_callback( $callback ) {
|
||||
return is_callable( $callback ) && 2 === ( new ReflectionFunction( $callback ) )->getNumberOfRequiredParameters();
|
||||
}
|
||||
|
||||
private static function is_valid_hack_object( $callback ) {
|
||||
if ( ! is_object( $callback ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ro = new ReflectionObject( ( $callback ) );
|
||||
try {
|
||||
$rm = $ro->getMethod( 'hack' );
|
||||
return $rm->isPublic() && ! $rm->isStatic() && 2 === $rm->getNumberOfRequiredParameters();
|
||||
} catch ( ReflectionException $exception ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the white list of files to hack. If note set, all the PHP files will be hacked.
|
||||
*
|
||||
* @param array $path_white_list Paths of the files to hack, can be relative paths.
|
||||
*/
|
||||
public static function set_white_list( array $path_white_list ) {
|
||||
self::$path_white_list = $path_white_list;
|
||||
}
|
||||
|
||||
|
||||
|
@ -104,8 +158,8 @@ class CodeHacker {
|
|||
}
|
||||
|
||||
|
||||
public function rename( $pathFrom, $pathTo ) {
|
||||
return $this->native( 'rename', $pathFrom, $pathTo, $this->context );
|
||||
public function rename( $path_from, $path_to ) {
|
||||
return $this->native( 'rename', $path_from, $path_to, $this->context );
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,13 +168,11 @@ class CodeHacker {
|
|||
}
|
||||
|
||||
|
||||
public function stream_cast( $castAs ) {
|
||||
public function stream_cast( $cast_as ) {
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
|
||||
public function stream_close() {
|
||||
// echo "***** CLOSE HANDLE: " . $this->handle . " \n";
|
||||
fclose( $this->handle );
|
||||
}
|
||||
|
||||
|
@ -159,11 +211,11 @@ class CodeHacker {
|
|||
}
|
||||
|
||||
|
||||
public function stream_open( $path, $mode, $options, &$openedPath ) {
|
||||
$usePath = (bool) ( $options & STREAM_USE_PATH );
|
||||
if ( $mode === 'rb' && self::pathInWhitelist( $path ) && pathinfo( $path, PATHINFO_EXTENSION ) === 'php' ) {
|
||||
$content = $this->native( 'file_get_contents', $path, $usePath, $this->context );
|
||||
if ( $content === false ) {
|
||||
public function stream_open( $path, $mode, $options, &$opened_path ) {
|
||||
$use_path = (bool) ( $options & STREAM_USE_PATH );
|
||||
if ( 'rb' === $mode && self::path_in_white_list( $path ) && 'php' === pathinfo( $path, PATHINFO_EXTENSION ) ) {
|
||||
$content = $this->native( 'file_get_contents', $path, $use_path, $this->context );
|
||||
if ( false === $content ) {
|
||||
return false;
|
||||
}
|
||||
$modified = self::hack( $content, $path );
|
||||
|
@ -175,8 +227,8 @@ class CodeHacker {
|
|||
}
|
||||
}
|
||||
$this->handle = $this->context
|
||||
? $this->native( 'fopen', $path, $mode, $usePath, $this->context )
|
||||
: $this->native( 'fopen', $path, $mode, $usePath );
|
||||
? $this->native( 'fopen', $path, $mode, $use_path, $this->context )
|
||||
: $this->native( 'fopen', $path, $mode, $use_path );
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
|
@ -205,8 +257,8 @@ class CodeHacker {
|
|||
}
|
||||
|
||||
|
||||
public function stream_truncate( $newSize ) {
|
||||
return ftruncate( $this->handle, $newSize );
|
||||
public function stream_truncate( $new_size ) {
|
||||
return ftruncate( $this->handle, $new_size );
|
||||
}
|
||||
|
||||
|
||||
|
@ -250,15 +302,19 @@ class CodeHacker {
|
|||
}
|
||||
|
||||
|
||||
private static function pathInWhitelist( $path ) {
|
||||
if ( empty( self::$pathWhitelist ) ) {
|
||||
private static function path_in_white_list( $path ) {
|
||||
if ( empty( self::$path_white_list ) ) {
|
||||
return true;
|
||||
}
|
||||
foreach ( self::$pathWhitelist as $whitelistItem ) {
|
||||
if ( substr( $path, -strlen( $whitelistItem ) ) === $whitelistItem ) {
|
||||
foreach ( self::$path_white_list as $white_list_item ) {
|
||||
if ( substr( $path, -strlen( $white_list_item ) ) === $white_list_item ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//phpcs:enable Squiz.Commenting.FunctionComment.Missing, Squiz.Commenting.VariableComment.Missing
|
||||
//phpcs:enable WordPress.WP.AlternativeFunctions, WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* CodeHackerTestHook class file.
|
||||
*
|
||||
* @package WooCommerce/Testing
|
||||
*/
|
||||
|
||||
// phpcs:disable Squiz.Commenting.FunctionComment.Missing, PHPCompatibility.FunctionDeclarations
|
||||
|
||||
namespace Automattic\WooCommerce\Testing\CodeHacking;
|
||||
|
||||
|
@ -91,14 +98,14 @@ final class CodeHackerTestHook implements BeforeTestHook, AfterTestHook {
|
|||
*
|
||||
* @param object $reflection_object The class or method reflection object whose doc comment will be parsed.
|
||||
* @return bool True if at least one valid @hack annotation was found.
|
||||
* @throws Exception
|
||||
* @throws Exception Class specified in @hack directive doesn't exist.
|
||||
*/
|
||||
private function add_hacks_from_annotations( $reflection_object ) {
|
||||
$annotations = Test::parseAnnotations( $reflection_object->getDocComment() );
|
||||
$hacks_added = false;
|
||||
|
||||
foreach ( $annotations as $id => $annotation_instances ) {
|
||||
if ( $id !== 'hack' ) {
|
||||
if ( 'hack' !== $id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -107,7 +114,7 @@ final class CodeHackerTestHook implements BeforeTestHook, AfterTestHook {
|
|||
$params = $matches[0];
|
||||
|
||||
$hack_class = array_shift( $params );
|
||||
if(false === strpos( $hack_class, '\\' )) {
|
||||
if ( false === strpos( $hack_class, '\\' ) ) {
|
||||
$hack_class = __NAMESPACE__ . '\\Hacks\\' . $hack_class;
|
||||
}
|
||||
|
||||
|
@ -132,7 +139,7 @@ final class CodeHackerTestHook implements BeforeTestHook, AfterTestHook {
|
|||
*
|
||||
* @param string $class_name Test class name.
|
||||
* @param string $method_name Test method name.
|
||||
* @throws ReflectionException
|
||||
* @throws ReflectionException Error when instatiating a ReflectionClass.
|
||||
*/
|
||||
private function execute_before_methods( $class_name, $method_name ) {
|
||||
$methods = array( 'before_all', "before_{$method_name}" );
|
||||
|
@ -154,3 +161,5 @@ final class CodeHackerTestHook implements BeforeTestHook, AfterTestHook {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:enable Squiz.Commenting.FunctionComment.Missing, PHPCompatibility.FunctionDeclarations
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* BypassFinalsHack class file.
|
||||
*
|
||||
* @package WooCommerce/Testing
|
||||
*/
|
||||
|
||||
// phpcs:disable Squiz.Commenting.FunctionComment.Missing
|
||||
|
||||
namespace Automattic\WooCommerce\Testing\CodeHacking\Hacks;
|
||||
|
||||
|
@ -21,3 +28,5 @@ final class BypassFinalsHack extends CodeHack {
|
|||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:enable Squiz.Commenting.FunctionComment.Missing
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<?php
|
||||
/**
|
||||
* CodeHack class file.
|
||||
*
|
||||
* @package WooCommerce/Testing
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Testing\CodeHacking\Hacks;
|
||||
|
||||
|
@ -26,6 +31,7 @@ abstract class CodeHack {
|
|||
* @return array Tokenized code.
|
||||
*/
|
||||
protected function tokenize( $code ) {
|
||||
//phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters, PHPCompatibility.Constants.NewConstants
|
||||
return PHP_VERSION_ID >= 70000 ? token_get_all( $code, TOKEN_PARSE ) : token_get_all( $code );
|
||||
}
|
||||
|
||||
|
@ -33,7 +39,7 @@ abstract class CodeHack {
|
|||
* Check if a token is of a given type.
|
||||
*
|
||||
* @param mixed $token Token to check.
|
||||
* @param int $type Type of token to check (see https://www.php.net/manual/en/tokens.php)
|
||||
* @param int $type Type of token to check (see https://www.php.net/manual/en/tokens.php).
|
||||
* @return bool True if it's a token of the given type, false otherwise.
|
||||
*/
|
||||
protected function is_token_of_type( $token, $type ) {
|
||||
|
@ -53,7 +59,7 @@ abstract class CodeHack {
|
|||
/**
|
||||
* Converts a token to its string representation.
|
||||
*
|
||||
* @param $token Token to convert.
|
||||
* @param mixed $token Token to convert.
|
||||
* @return mixed String representation of the token.
|
||||
*/
|
||||
protected function token_to_string( $token ) {
|
||||
|
@ -70,7 +76,7 @@ abstract class CodeHack {
|
|||
*/
|
||||
protected function string_ends_with( $haystack, $needle ) {
|
||||
$length = strlen( $needle );
|
||||
if ( $length == 0 ) {
|
||||
if ( 0 === $length ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* FunctionsMockerHack class file.
|
||||
*
|
||||
* @package WooCommerce/Testing
|
||||
*/
|
||||
|
||||
// phpcs:disable Squiz.Commenting.FunctionComment.Missing
|
||||
|
||||
namespace Automattic\WooCommerce\Testing\CodeHacking\Hacks;
|
||||
|
||||
|
@ -17,6 +24,11 @@ use ReflectionClass;
|
|||
*/
|
||||
final class FunctionsMockerHack extends CodeHack {
|
||||
|
||||
/**
|
||||
* Tokens that precede a non-standalone-function identifier.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $non_global_function_tokens = array(
|
||||
T_PAAMAYIM_NEKUDOTAYIM,
|
||||
T_DOUBLE_COLON,
|
||||
|
@ -28,9 +40,9 @@ final class FunctionsMockerHack extends CodeHack {
|
|||
* FunctionsMockerHack constructor.
|
||||
*
|
||||
* @param string $mock_class Name of the class containing function mocks as public static methods.
|
||||
* @throws ReflectionException
|
||||
* @throws ReflectionException Error when instantiating ReflectionClass.
|
||||
*/
|
||||
public function __construct( string $mock_class ) {
|
||||
public function __construct( $mock_class ) {
|
||||
$this->mock_class = $mock_class;
|
||||
|
||||
$rc = new ReflectionClass( $mock_class );
|
||||
|
@ -53,14 +65,16 @@ final class FunctionsMockerHack extends CodeHack {
|
|||
$token_type = $this->token_type_of( $token );
|
||||
if ( T_WHITESPACE === $token_type ) {
|
||||
$code .= $this->token_to_string( $token );
|
||||
} elseif ( T_STRING === $token_type && ! $previous_token_is_non_global_function_qualifier && in_array( $token[1], $this->mocked_methods ) ) {
|
||||
} elseif ( T_STRING === $token_type && ! $previous_token_is_non_global_function_qualifier && in_array( $token[1], $this->mocked_methods, true ) ) {
|
||||
$code .= "{$this->mock_class}::{$token[1]}";
|
||||
} else {
|
||||
$code .= $this->token_to_string( $token );
|
||||
$previous_token_is_non_global_function_qualifier = in_array( $token_type, self::$non_global_function_tokens );
|
||||
$previous_token_is_non_global_function_qualifier = in_array( $token_type, self::$non_global_function_tokens, true );
|
||||
}
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:enable Squiz.Commenting.FunctionComment.Missing
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
<?php
|
||||
/**
|
||||
* StaticMockerHack class file.
|
||||
*
|
||||
* @package WooCommerce/Testing
|
||||
*/
|
||||
|
||||
// phpcs:disable Squiz.Commenting.FunctionComment.Missing
|
||||
|
||||
namespace Automattic\WooCommerce\Testing\CodeHacking\Hacks;
|
||||
|
||||
|
@ -26,9 +33,9 @@ final class StaticMockerHack extends CodeHack {
|
|||
*
|
||||
* @param string $source_class Name of the original class (the one having the members to be mocked).
|
||||
* @param string $mock_class Name of the mock class (the one having the replacement mock members).
|
||||
* @throws ReflectionException
|
||||
* @throws ReflectionException Error when instantiating ReflectionClass.
|
||||
*/
|
||||
public function __construct( string $source_class, string $mock_class ) {
|
||||
public function __construct( $source_class, $mock_class ) {
|
||||
$this->source_class = $source_class;
|
||||
$this->target_class = $mock_class;
|
||||
|
||||
|
@ -61,12 +68,13 @@ final class StaticMockerHack extends CodeHack {
|
|||
$code = '';
|
||||
$current_token = null;
|
||||
|
||||
// phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
|
||||
while ( $current_token = current( $tokens ) ) {
|
||||
if ( $this->is_token_of_type( $current_token, T_STRING ) && $this->source_class === $current_token[1] ) {
|
||||
$next_token = next( $tokens );
|
||||
if ( $this->is_token_of_type( $next_token, T_DOUBLE_COLON ) ) {
|
||||
$called_member = next( $tokens )[1];
|
||||
if ( in_array( $called_member, $this->members_implemented_in_mock ) ) {
|
||||
if ( in_array( $called_member, $this->members_implemented_in_mock, true ) ) {
|
||||
// Reference to source class member that exists in mock class, replace.
|
||||
$code .= "{$this->target_class}::{$called_member}";
|
||||
} else {
|
||||
|
@ -88,3 +96,5 @@ final class StaticMockerHack extends CodeHack {
|
|||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:enable Squiz.Commenting.FunctionComment.Missing
|
||||
|
|
Loading…
Reference in New Issue