* 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**
|
||||
|
||||
* 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 - 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)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Automattic\WooCommerce\Internal\Traits;
|
||||
|
||||
use Automattic\WooCommerce\Utilities\ArrayUtil;
|
||||
use SplObjectStorage;
|
||||
|
||||
/**
|
||||
* This trait allows making private methods of a class accessible from outside.
|
||||
|
@ -41,10 +41,11 @@ trait AccessiblePrivateMethods {
|
|||
//phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
|
||||
/**
|
||||
* 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.
|
||||
|
@ -52,6 +53,13 @@ trait AccessiblePrivateMethods {
|
|||
* @var 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
|
||||
|
||||
/**
|
||||
|
@ -70,7 +78,7 @@ trait AccessiblePrivateMethods {
|
|||
* @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 {
|
||||
self::process_callback_before_hooking( $callback );
|
||||
static::process_callback_before_hooking( $callback );
|
||||
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.
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
|
||||
|
@ -123,7 +131,15 @@ trait AccessiblePrivateMethods {
|
|||
// Note that an "is_callable" check would be useless here:
|
||||
// "is_callable" always returns true if the class implements __call.
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
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 );
|
||||
} elseif ( is_callable( array( 'parent', '__call' ) ) ) {
|
||||
return parent::__call( $name, $arguments );
|
||||
|
@ -163,6 +180,7 @@ trait AccessiblePrivateMethods {
|
|||
} else {
|
||||
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.
|
||||
*/
|
||||
public static function __callStatic( $name, $arguments ) {
|
||||
// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||
if ( isset( static::$_accessible_static_private_methods[ $name ] ) ) {
|
||||
return call_user_func_array( array( __CLASS__, $name ), $arguments );
|
||||
} elseif ( is_callable( array( 'parent', '__callStatic' ) ) ) {
|
||||
|
@ -186,5 +205,20 @@ trait AccessiblePrivateMethods {
|
|||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -427,6 +427,22 @@ class AccessiblePrivateMethodsTest extends \WC_Unit_Test_Case {
|
|||
|
||||
$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 );
|
||||
}
|
||||
}
|
||||
|
||||
//phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch
|
||||
|
@ -449,4 +465,30 @@ class BaseClass {
|
|||
//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
|
||||
|
|
Loading…
Reference in New Issue