* Make AccessiblePrivateMethods::_accessible_private_methods static. This prevents the contents of the property to be included in the object serialization. * Add changelog file * Small optimization * Change 'self' to 'static' for consistency * Add unit test for serialization * Drop changelog * Add changelog entry to readme.txt --------- Co-authored-by: Nestor Soriano <konamiman@konamiman.com>
This commit is contained in:
parent
6937fc68a3
commit
a9845e6a1d
|
@ -173,6 +173,7 @@ WooCommerce comes with some sample data you can use to see how products look; im
|
||||||
|
|
||||||
**WooCommerce**
|
**WooCommerce**
|
||||||
|
|
||||||
|
* Update - Turn AccessiblePrivateMethods::_accessible_private_methods into a static property. [#50806](https://github.com/woocommerce/woocommerce/pull/50806)
|
||||||
* Fix - Correct label of shipping dimensions length field. [#50180](https://github.com/woocommerce/woocommerce/pull/50180)
|
* Fix - Correct label of shipping dimensions length field. [#50180](https://github.com/woocommerce/woocommerce/pull/50180)
|
||||||
* Fix - Allow new accounts to set new password even if logged in. [#50700](https://github.com/woocommerce/woocommerce/pull/50700)
|
* Fix - Allow new accounts to set new password even if logged in. [#50700](https://github.com/woocommerce/woocommerce/pull/50700)
|
||||||
* Fix - Remove global_unique_id from interface and add warning in case it is not implemented [#50685](https://github.com/woocommerce/woocommerce/pull/50685)
|
* Fix - Remove global_unique_id from interface and add warning in case it is not implemented [#50685](https://github.com/woocommerce/woocommerce/pull/50685)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Automattic\WooCommerce\Internal\Traits;
|
namespace Automattic\WooCommerce\Internal\Traits;
|
||||||
|
|
||||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
use SplObjectStorage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This trait allows making private methods of a class accessible from outside.
|
* This trait allows making private methods of a class accessible from outside.
|
||||||
|
@ -41,10 +41,11 @@ trait AccessiblePrivateMethods {
|
||||||
//phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
|
//phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
|
||||||
/**
|
/**
|
||||||
* List of instance methods marked as externally accessible.
|
* List of instance methods marked as externally accessible.
|
||||||
|
* This is actually a dictionary where keys are object instances and values are arrays of method names.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var SplObjectStorage
|
||||||
*/
|
*/
|
||||||
private $_accessible_private_methods = array();
|
private static $_accessible_private_methods = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of static methods marked as externally accessible.
|
* List of static methods marked as externally accessible.
|
||||||
|
@ -52,6 +53,13 @@ trait AccessiblePrivateMethods {
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $_accessible_static_private_methods = array();
|
private static $_accessible_static_private_methods = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating that $_accessible_private_methods already contains an entry for this object.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private bool $_accessible_private_methods_is_initialized_for_this = false;
|
||||||
//phpcs:enable PSR2.Classes.PropertyDeclaration.Underscore
|
//phpcs:enable PSR2.Classes.PropertyDeclaration.Underscore
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +78,7 @@ trait AccessiblePrivateMethods {
|
||||||
* @param int $accepted_args Optional. The number of arguments the function accepts. Default 1.
|
* @param int $accepted_args Optional. The number of arguments the function accepts. Default 1.
|
||||||
*/
|
*/
|
||||||
protected static function add_action( string $hook_name, $callback, int $priority = 10, int $accepted_args = 1 ): void {
|
protected static function add_action( string $hook_name, $callback, int $priority = 10, int $accepted_args = 1 ): void {
|
||||||
self::process_callback_before_hooking( $callback );
|
static::process_callback_before_hooking( $callback );
|
||||||
add_action( $hook_name, $callback, $priority, $accepted_args );
|
add_action( $hook_name, $callback, $priority, $accepted_args );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +98,7 @@ trait AccessiblePrivateMethods {
|
||||||
* @param int $accepted_args Optional. The number of arguments the function accepts. Default 1.
|
* @param int $accepted_args Optional. The number of arguments the function accepts. Default 1.
|
||||||
*/
|
*/
|
||||||
protected static function add_filter( string $hook_name, $callback, int $priority = 10, int $accepted_args = 1 ): void {
|
protected static function add_filter( string $hook_name, $callback, int $priority = 10, int $accepted_args = 1 ): void {
|
||||||
self::process_callback_before_hooking( $callback );
|
static::process_callback_before_hooking( $callback );
|
||||||
add_filter( $hook_name, $callback, $priority, $accepted_args );
|
add_filter( $hook_name, $callback, $priority, $accepted_args );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +131,15 @@ trait AccessiblePrivateMethods {
|
||||||
// Note that an "is_callable" check would be useless here:
|
// Note that an "is_callable" check would be useless here:
|
||||||
// "is_callable" always returns true if the class implements __call.
|
// "is_callable" always returns true if the class implements __call.
|
||||||
if ( method_exists( $this, $method_name ) ) {
|
if ( method_exists( $this, $method_name ) ) {
|
||||||
$this->_accessible_private_methods[ $method_name ] = $method_name;
|
if ( is_null( static::$_accessible_private_methods ) ) {
|
||||||
|
static::$_accessible_private_methods = new SplObjectStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$methods = $this->_accessible_private_methods_is_initialized_for_this ? static::$_accessible_private_methods[ $this ] : array();
|
||||||
|
$methods[ $method_name ] = $method_name;
|
||||||
|
static::$_accessible_private_methods[ $this ] = $methods;
|
||||||
|
|
||||||
|
$this->_accessible_private_methods_is_initialized_for_this = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +170,8 @@ trait AccessiblePrivateMethods {
|
||||||
* @throws \Error The called instance method doesn't exist or is private/protected and not marked as externally accessible.
|
* @throws \Error The called instance method doesn't exist or is private/protected and not marked as externally accessible.
|
||||||
*/
|
*/
|
||||||
public function __call( $name, $arguments ) {
|
public function __call( $name, $arguments ) {
|
||||||
if ( isset( $this->_accessible_private_methods[ $name ] ) ) {
|
// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||||
|
if ( $this->_accessible_private_methods_is_initialized_for_this && isset( static::$_accessible_private_methods[ $this ][ $name ] ) ) {
|
||||||
return call_user_func_array( array( $this, $name ), $arguments );
|
return call_user_func_array( array( $this, $name ), $arguments );
|
||||||
} elseif ( is_callable( array( 'parent', '__call' ) ) ) {
|
} elseif ( is_callable( array( 'parent', '__call' ) ) ) {
|
||||||
return parent::__call( $name, $arguments );
|
return parent::__call( $name, $arguments );
|
||||||
|
@ -163,6 +180,7 @@ trait AccessiblePrivateMethods {
|
||||||
} else {
|
} else {
|
||||||
throw new \Error( 'Call to undefined method ' . get_class( $this ) . '::' . $name );
|
throw new \Error( 'Call to undefined method ' . get_class( $this ) . '::' . $name );
|
||||||
}
|
}
|
||||||
|
// phpcs:enable WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,6 +192,7 @@ trait AccessiblePrivateMethods {
|
||||||
* @throws \Error The called static method doesn't exist or is private/protected and not marked as externally accessible.
|
* @throws \Error The called static method doesn't exist or is private/protected and not marked as externally accessible.
|
||||||
*/
|
*/
|
||||||
public static function __callStatic( $name, $arguments ) {
|
public static function __callStatic( $name, $arguments ) {
|
||||||
|
// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||||
if ( isset( static::$_accessible_static_private_methods[ $name ] ) ) {
|
if ( isset( static::$_accessible_static_private_methods[ $name ] ) ) {
|
||||||
return call_user_func_array( array( __CLASS__, $name ), $arguments );
|
return call_user_func_array( array( __CLASS__, $name ), $arguments );
|
||||||
} elseif ( is_callable( array( 'parent', '__callStatic' ) ) ) {
|
} elseif ( is_callable( array( 'parent', '__callStatic' ) ) ) {
|
||||||
|
@ -186,5 +205,20 @@ trait AccessiblePrivateMethods {
|
||||||
} else {
|
} else {
|
||||||
throw new \Error( 'Call to undefined method ' . __CLASS__ . '::' . $name );
|
throw new \Error( 'Call to undefined method ' . __CLASS__ . '::' . $name );
|
||||||
}
|
}
|
||||||
|
// phpcs:enable WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class destructor, needed to remove this object from the dictionary of accessible instance methods.
|
||||||
|
*/
|
||||||
|
public function __destruct() {
|
||||||
|
if ( $this->_accessible_private_methods_is_initialized_for_this ) {
|
||||||
|
static::$_accessible_private_methods->detach( $this );
|
||||||
|
$this->_accessible_private_methods_is_initialized_for_this = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( is_callable( array( 'parent', '__destruct' ) ) ) {
|
||||||
|
parent::__destruct();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -425,7 +425,23 @@ class AccessiblePrivateMethodsTest extends \WC_Unit_Test_Case {
|
||||||
$this->expectException( \Error::class );
|
$this->expectException( \Error::class );
|
||||||
$this->expectExceptionMessage( get_class( $sut ) . '::' . "$method_name can't be called statically, did you mean '$proper_method_name'?" );
|
$this->expectExceptionMessage( get_class( $sut ) . '::' . "$method_name can't be called statically, did you mean '$proper_method_name'?" );
|
||||||
|
|
||||||
$sut::$method_name( 'some_action', function() {} );
|
$sut::$method_name( 'some_action', function () {} );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox The serialization of an object doesn't include information about the private methods made accessible.
|
||||||
|
*/
|
||||||
|
public function test_serialized_object_does_not_contain_accessible_methods_data() {
|
||||||
|
SerializableClass::init();
|
||||||
|
$instance = new SerializableClass();
|
||||||
|
|
||||||
|
$this->assertEquals( 'instance', $instance->private_instance_method() );
|
||||||
|
$this->assertEquals( 'static', $instance::private_static_method() );
|
||||||
|
|
||||||
|
//phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
|
||||||
|
$serialized = serialize( $instance );
|
||||||
|
$this->assertStringNotContainsString( 'private_instance_method', $serialized );
|
||||||
|
$this->assertStringNotContainsString( 'private_static_method', $serialized );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,4 +465,30 @@ class BaseClass {
|
||||||
//phpcs:enable Squiz.Commenting.FunctionComment.Missing
|
//phpcs:enable Squiz.Commenting.FunctionComment.Missing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to test serialization (instances of anonymous classes can't be serialized).
|
||||||
|
*/
|
||||||
|
class SerializableClass {
|
||||||
|
//phpcs:disable Squiz.Commenting.FunctionComment.Missing
|
||||||
|
use AccessiblePrivateMethods;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->mark_method_as_accessible( 'private_instance_method' );
|
||||||
|
}
|
||||||
|
|
||||||
|
//phpcs:ignore WooCommerce.Functions.InternalInjectionMethod.MissingInternalTag
|
||||||
|
final public static function init() {
|
||||||
|
self::mark_static_method_as_accessible( 'private_static_method' );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function private_instance_method() {
|
||||||
|
return 'instance';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function private_static_method() {
|
||||||
|
return 'static';
|
||||||
|
}
|
||||||
|
//phpcs:enable Squiz.Commenting.FunctionComment.Missing
|
||||||
|
}
|
||||||
|
|
||||||
//phpcs:enable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch
|
//phpcs:enable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch
|
||||||
|
|
Loading…
Reference in New Issue