Skip to content
145 changes: 145 additions & 0 deletions woocommerce/Helpers/OrderHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php
/**
* Helper class for managing WooCommerce order properties.
*
* This class provides helper methods for getting and setting order properties
* in a standardized way using the Dynamic_Props class. It handles payment details,
* customer information, and custom order properties.
*
* @package SkyVerge/WooCommerce/Helpers
* @since x.x.x
*/

namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers;

use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Dynamic_Props;

/**
* OrderHelper class
*
* @since x.x.x
*/
class OrderHelper {

/**
* Gets the payment object associated with an order.
*
* Retrieves the payment details stored as a dynamic property on the order object.
* If no payment object exists, returns an empty stdClass instance.
*
* @since x.x.x
*
* @param \WC_Order $order The order object.
* @return \stdClass The payment object containing payment details.
*/
public static function get_payment( \WC_Order $order ) {
return Dynamic_Props::get( $order, 'payment', null, new \stdClass() );
}

/**
* Gets the payment total for an order.
*
* Retrieves the total payment amount stored as a dynamic property on the order.
*
* @since x.x.x
*
* @param \WC_Order $order The order object.
* @return mixed The payment total amount, or null if not set.
*/
public static function get_payment_total( \WC_Order $order ) {
return Dynamic_Props::get( $order, 'payment_total' );
}

/**
* Gets the customer ID associated with an order.
*
* Retrieves the customer ID stored as a dynamic property on the order.
*
* @since x.x.x
*
* @param \WC_Order $order The order object.
* @return mixed The customer ID, or null if not set.
*/
public static function get_customer_id( \WC_Order $order ) {
return Dynamic_Props::get( $order, 'customer_id' );
}

/**
* Gets a dynamic property from an order.
*
* Provides a generic way to retrieve any dynamic property stored on an order object.
* Supports nested properties through the optional nested_key parameter.
*
* @since x.x.x
*
* @param \WC_Order $order The order object.
* @param string $key The property key to retrieve.
* @param string $nested_key Optional. A nested key within the property. Default null.
* @param mixed $default Optional. The default value if the property doesn't exist. Default null.
* @return mixed The property value if found, or the default value if not found.
*/
public static function get_property( \WC_Order $order, string $key, $nested_key = null, $default = null ): mixed {
return Dynamic_Props::get( $order, $key, $nested_key, $default );
}

/**
* Sets the payment object for an order.
*
* Stores payment details as a dynamic property on the order object.
* This method uses pass-by-reference to modify the order object directly.
*
* @since x.x.x
*
* @param \WC_Order $order The order object (passed by reference).
* @param \stdClass $payment The payment object containing payment details.
*/
public static function set_payment( \WC_Order &$order, \stdClass $payment ) {
Dynamic_Props::set( $order, 'payment', $payment );
}

/**
* Sets the payment total for an order.
*
* Stores the payment total as a dynamic property on the order object.
* This method uses pass-by-reference to modify the order object directly.
*
* @since x.x.x
*
* @param \WC_Order $order The order object (passed by reference).
* @param float|string $payment_total The payment total amount.
*/
public static function set_payment_total( \WC_Order &$order, $payment_total ) {
Dynamic_Props::set( $order, 'payment_total', $payment_total );
}

/**
* Sets the customer ID for an order.
*
* Stores the customer ID as a dynamic property on the order object.
* This method uses pass-by-reference to modify the order object directly.
*
* @since x.x.x
*
* @param \WC_Order $order The order object (passed by reference).
* @param mixed $customer_id The customer ID to set.
*/
public static function set_customer_id( \WC_Order &$order, $customer_id ) {
Dynamic_Props::set( $order, 'customer_id', $customer_id );
}

/**
* Sets a dynamic property on an order.
*
* Provides a generic way to store any dynamic property on an order object.
* This method uses pass-by-reference to modify the order object directly.
*
* @since x.x.x
*
* @param \WC_Order $order The order object (passed by reference).
* @param string $key The property key to set.
* @param mixed $value The value to set for the property.
*/
public static function set_property( \WC_Order &$order, string $key, $value ) {
Dynamic_Props::set( $order, $key, $value );
}
}
190 changes: 190 additions & 0 deletions woocommerce/payment-gateway/Dynamic_Props.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<?php
/**
* Dynamic property storage handler for WooCommerce order objects.
*
* Provides a PHP 8.2+ compatible way to store dynamic properties on order objects
* while maintaining backwards compatibility with PHP 7.4+.
*
* @package SkyVerge/WooCommerce/Payment-Gateway/Classes
* @since x.x.x
*/

namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway;

/**
* Dynamic property storage handler for WooCommerce order objects.
*
* This class provides a way to store dynamic properties on order objects without using
* dynamic properties (deprecated in PHP 8.2+) while maintaining backwards compatibility
* with PHP 7.4+. It uses WeakMap for PHP 8.0+ and falls back to dynamic properties
* for PHP 7.4+.
*
* @since x.x.x
*
* @example
* ```php
* // Store properties
* Dynamic_Props::set($order, 'customer_id', 123);
* Dynamic_Props::set($order, 'payment_total', 99.99);
*
* // Retrieve properties
* $customer_id = Dynamic_Props::get($order, 'customer_id');
* $total = Dynamic_Props::get($order, 'payment_total');
* ```
*/
class Dynamic_Props {
/**
* Storage container for dynamic properties using WeakMap in PHP 8.0+.
*
* Uses WeakMap to store properties without memory leaks, as WeakMap allows garbage
* collection of its keys when they're no longer referenced elsewhere.
*
* @since x.x.x
* @var \WeakMap<object, \stdClass>|null
*/
private static ?\WeakMap $map = null;

/**
* Sets a property on the order object.
*
* Stores a dynamic property either using WeakMap (PHP 8.0+) or direct property
* access (PHP 7.4+). The storage method is automatically determined based on
* PHP version and WeakMap availability.
*
* @since x.x.x
*
* @param \WC_Order $order The order object to store data on.
* @param string $key The property key.
* @param mixed $value The value to store.
* @return void
*
* @example
* ```php
* Dynamic_Props::set($order, 'customer_id', 123);
* Dynamic_Props::set($order, 'payment_total', '99.99');
* ```
*/
public static function set( \WC_Order &$order, string $key, mixed $value ): void {
if ( self::use_weak_map() ) {
self::init_weak_map();
if ( ! isset( self::$map[ $order ] ) ) {
self::$map[ $order ] = new \stdClass();
}
self::$map[ $order ]->{ $key } = $value;
} else {
$order->{ $key } = $value;
}
}

/**
* Gets a property from the order object.
*
* Retrieves a stored dynamic property using the appropriate storage method
* based on PHP version. Supports nested property access.
*
* @since x.x.x
*
* @param \WC_Order $order The order object to retrieve data from.
* @param string $key The property key.
* @param string $nested_key Optional. The nested property key. Default null.
* @param mixed $default Optional. Default value if not found. Default null.
* @return mixed The stored value or default if not found.
*
* @example
* ```php
* $customer_id = Dynamic_Props::get($order, 'customer_id');
* $total = Dynamic_Props::get($order, 'payment_total');
* $token = Dynamic_Props::get($order, 'payment', 'token', 'DEFAULT_TOKEN');
* ```
*/
public static function get( \WC_Order $order, string $key, $nested_key = null, $default = null ): mixed {
if ( self::use_weak_map() ) {
self::init_weak_map();
if ( is_null( $nested_key ) ) {
return self::$map[ $order ]->{ $key } ?? $default;
} else {
return self::$map[ $order ]->{ $key }->{ $nested_key } ?? $default;
}
}
if ( is_null( $nested_key ) ) {
return $order->{ $key } ?? $default;
} else {
return $order->{ $key }->{ $nested_key } ?? $default;
}
}

/**
* Unsets a property on the order object.
*
* Removes a stored dynamic property using the appropriate storage method
* based on PHP version.
*
* @since x.x.x
*
* @param \WC_Order $order The order object to unset data from.
* @param string $key The property key to remove.
* @return void
*/
public static function unset( \WC_Order &$order, string $key ): void {
if ( self::use_weak_map() ) {
self::init_weak_map();
unset( self::$map[ $order ]->{ $key } );
} else {
unset( $order->{ $key } );
}
}

/**
* Checks if Dynamic_Props class should be used based on the filter.
*
* @return bool True if Dynamic_Props class should be used, false otherwise.
*/
private static function use_dynamic_props_class(): bool {
static $use_dynamic_props_class = null;
if ( null === $use_dynamic_props_class ) {
/**
* Filters whether to use Dynamic_Props class for storing order data.
*
* @since x.x.x
*
* @var bool Whether to Dynamic_Props class for storing order data.
*/
$use_dynamic_props_class = apply_filters( 'sv_wc_plugin_framework_use_dynamic_props_class', false );
}
return $use_dynamic_props_class;
}

/**
* Checks if WeakMap should be used based on PHP version.
*
* Determines whether to use WeakMap storage based on PHP version (8.0+)
* and WeakMap class availability. Result is cached for performance.
*
* @since x.x.x
* @return bool True if WeakMap should be used, false otherwise.
*/
private static function use_weak_map(): bool {
static $use_weak_map = null;

if ( null === $use_weak_map ) {
$use_weak_map = version_compare( PHP_VERSION, '8.0', '>=' ) && self::use_dynamic_props_class();
}

return $use_weak_map;
}

/**
* Initializes WeakMap storage if not already initialized.
*
* Ensures the WeakMap storage is initialized only once when needed.
* This lazy initialization helps with performance and memory usage.
*
* @since x.x.x
* @return void
*/
private static function init_weak_map(): void {
if ( null === self::$map ) {
self::$map = new \WeakMap();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Handlers;

use SkyVerge\WooCommerce\PluginFramework\v6_0_0 as FrameworkBase;
use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper;

if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_0\\Payment_Gateway\\Handlers\\Abstract_Hosted_Payment_Handler' ) ) :

Expand Down Expand Up @@ -325,20 +326,25 @@ protected function get_order_from_response( FrameworkBase\SV_WC_Payment_Gateway_

$order = $this->get_gateway()->get_order( $order );

$order->payment->account_number = $response->get_account_number();
$payment = OrderHelper::get_payment( $order );

$payment->account_number = $response->get_account_number();

if ( $response instanceof FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_Credit_Card_Response ) {

$order->payment->exp_month = $response->get_exp_month();
$order->payment->exp_year = $response->get_exp_year();
$order->payment->card_type = $response->get_card_type();
$payment->exp_month = $response->get_exp_month();
$payment->exp_year = $response->get_exp_year();
$payment->card_type = $response->get_card_type();

} elseif ( $response instanceof FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_eCheck_Response ) {

$order->payment->account_type = $response->get_account_type();
$order->payment->check_number = $response->get_check_number();
$payment->account_type = $response->get_account_type();
$payment->check_number = $response->get_check_number();
}

// Set payment info on the order object.
OrderHelper::set_payment( $order, $payment );

return $order;
}

Expand Down
Loading
Loading