diff --git a/includes/abstracts/abstract-wc-data.php b/includes/abstracts/abstract-wc-data.php index f408e188633..815625f6df6 100644 --- a/includes/abstracts/abstract-wc-data.php +++ b/includes/abstracts/abstract-wc-data.php @@ -27,6 +27,12 @@ abstract class WC_Data { */ protected $_data = array(); + /** + * True when reading from the database. + * @var bool + */ + protected $_reading = false; + /** * Stores meta in cache for future reads. * A group must be set to to enable caching. @@ -377,33 +383,17 @@ abstract class WC_Data { * @return mixed */ protected function get_prop() { - $args = func_get_args(); - $target = &$this->_data; + $args = func_get_args(); + $prop = &$this->_data; foreach ( $args as $arg ) { - if ( ! isset( $target[ $arg ] ) ) { + if ( ! isset( $prop[ $arg ] ) ) { return false; } - $target = &$target[ $arg ]; + $prop = &$prop[ $arg ]; } - return $target; - } - - /** - * Set all props to passed key value pairs using setters and ignoring invalid - * values with the try/catch. - * @param array $values - */ - protected function set_props( $values = array() ) { - foreach ( $values as $prop => $value ) { - try { - $this->{"set_$prop"}( $value ); - } catch ( WC_Data_Exception $e ) { - // Default value will be left as-is. - unset( $e ); - } - } + return $prop; } /** @@ -413,7 +403,7 @@ abstract class WC_Data { */ protected function set_prop() { if ( func_num_args() < 2 ) { - $this->throw_exception( 'invalid_value', 'set_prop requires at least 2 parameters' ); + $this->invalid_data( 'invalid_value', 'set_prop requires at least 2 parameters' ); } $args = func_get_args(); @@ -428,12 +418,14 @@ abstract class WC_Data { } /** - * Returns an invalid data WP_Error object. + * When invalid data is found, throw an exception unless reading from the DB. * @param string $message Error Message. * @param mixed $data Data the user tried to set. * @throws WC_Data_Exception */ - protected function throw_exception( $error_code, $error_message, $http_status_code = 400 ) { - throw new WC_Data_Exception( $error_code, $error_message, $http_status_code ); + protected function invalid_data( $error_code, $error_message, $http_status_code = 400 ) { + if ( ! $this->_reading ) { + throw new WC_Data_Exception( $error_code, $error_message, $http_status_code ); + } } } diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index bc704492672..92e7be3ba01 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -108,6 +108,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { * @param int|object|WC_Order $order Order to init. */ public function __construct( $order = 0 ) { + $this->set_defaults(); + if ( is_numeric( $order ) && $order > 0 ) { $this->read( $order ); } elseif ( $order instanceof self ) { @@ -199,6 +201,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { return; } + $this->_reading = true; // Map standard post data $this->set_id( $post_object->ID ); $this->set_parent_id( $post_object->post_parent ); @@ -219,6 +222,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { // Load meta data $this->read_meta_data(); + $this->_reading = false; } /** @@ -605,7 +609,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { */ public function set_parent_id( $value ) { if ( $value && ! get_post( $value ) ) { - $this->throw_exception( 'order_invalid_parent_id', __( 'Invalid parent ID', 'woocommerce' ) ); + $this->invalid_data( 'order_invalid_parent_id', __( 'Invalid parent ID', 'woocommerce' ) ); } $this->set_prop( 'parent_id', absint( $value ) ); } @@ -654,7 +658,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { */ public function set_currency( $value ) { if ( $value && ! in_array( $value, array_keys( get_woocommerce_currencies() ) ) ) { - $this->throw_exception( 'order_invalid_currency', __( 'Invalid currency code', 'woocommerce' ) ); + $this->invalid_data( 'order_invalid_currency', __( 'Invalid currency code', 'woocommerce' ) ); } $this->set_prop( 'currency', $value ); } diff --git a/includes/api/class-wc-rest-orders-controller.php b/includes/api/class-wc-rest-orders-controller.php index 46195ae7e1a..5a0efd2a339 100644 --- a/includes/api/class-wc-rest-orders-controller.php +++ b/includes/api/class-wc-rest-orders-controller.php @@ -434,6 +434,8 @@ class WC_REST_Orders_Controller extends WC_REST_Posts_Controller { } return $order->get_id(); + } catch ( WC_Data_Exception $e ) { + return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } catch ( WC_REST_Exception $e ) { return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); } diff --git a/includes/class-wc-order-item-coupon.php b/includes/class-wc-order-item-coupon.php index 9b800c6f915..d768e5f67d0 100644 --- a/includes/class-wc-order-item-coupon.php +++ b/includes/class-wc-order-item-coupon.php @@ -74,8 +74,10 @@ class WC_Order_Item_Coupon extends WC_Order_Item { public function read( $id ) { parent::read( $id ); if ( $this->get_id() ) { + $this->_reading = true; $this->set_discount( get_metadata( 'order_item', $this->get_id(), 'discount_amount', true ) ); $this->set_discount_tax( get_metadata( 'order_item', $this->get_id(), 'discount_amount_tax', true ) ); + $this->_reading = false; } } diff --git a/includes/class-wc-order-item-fee.php b/includes/class-wc-order-item-fee.php index 3cf657c90c7..e0ae76e67c1 100644 --- a/includes/class-wc-order-item-fee.php +++ b/includes/class-wc-order-item-fee.php @@ -83,11 +83,13 @@ class WC_Order_Item_Fee extends WC_Order_Item { public function read( $id ) { parent::read( $id ); if ( $this->get_id() ) { + $this->_reading = true; $this->set_tax_class( get_metadata( 'order_item', $this->get_id(), '_tax_class', true ) ); $this->set_tax_status( get_metadata( 'order_item', $this->get_id(), '_tax_status', true ) ); $this->set_total( get_metadata( 'order_item', $this->get_id(), '_line_total', true ) ); $this->set_total_tax( get_metadata( 'order_item', $this->get_id(), '_line_tax', true ) ); $this->set_taxes( get_metadata( 'order_item', $this->get_id(), '_line_tax_data', true ) ); + $this->_reading = false; } } @@ -129,7 +131,7 @@ class WC_Order_Item_Fee extends WC_Order_Item { */ public function set_tax_class( $value ) { if ( $value && ! in_array( $value, WC_Tax::get_tax_classes() ) ) { - $this->throw_exception( 'order_item_fee_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); + $this->invalid_data( 'order_item_fee_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); } $this->set_prop( 'tax_class', $value ); } diff --git a/includes/class-wc-order-item-product.php b/includes/class-wc-order-item-product.php index adbe3f83f72..984416eebc1 100644 --- a/includes/class-wc-order-item-product.php +++ b/includes/class-wc-order-item-product.php @@ -96,6 +96,7 @@ class WC_Order_Item_Product extends WC_Order_Item { public function read( $id ) { parent::read( $id ); if ( $this->get_id() ) { + $this->_reading = true; $this->set_product_id( get_metadata( 'order_item', $this->get_id(), '_product_id', true ) ); $this->set_variation_id( get_metadata( 'order_item', $this->get_id(), '_variation_id', true ) ); $this->set_quantity( get_metadata( 'order_item', $this->get_id(), '_qty', true ) ); @@ -105,6 +106,7 @@ class WC_Order_Item_Product extends WC_Order_Item { $this->set_total( get_metadata( 'order_item', $this->get_id(), '_line_total', true ) ); $this->set_total_tax( get_metadata( 'order_item', $this->get_id(), '_line_tax', true ) ); $this->set_taxes( get_metadata( 'order_item', $this->get_id(), '_line_tax_data', true ) ); + $this->_reading = false; } } @@ -235,7 +237,7 @@ class WC_Order_Item_Product extends WC_Order_Item { */ public function set_quantity( $value ) { if ( 0 >= $value ) { - $this->throw_exception( 'order_item_product_invalid_quantity', __( 'Quantity must be positive', 'woocommerce' ) ); + $this->invalid_data( 'order_item_product_invalid_quantity', __( 'Quantity must be positive', 'woocommerce' ) ); } $this->set_prop( 'quantity', wc_stock_amount( $value ) ); } @@ -247,7 +249,7 @@ class WC_Order_Item_Product extends WC_Order_Item { */ public function set_tax_class( $value ) { if ( $value && ! in_array( $value, WC_Tax::get_tax_classes() ) ) { - $this->throw_exception( 'order_item_product_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); + $this->invalid_data( 'order_item_product_invalid_tax_class', __( 'Invalid tax class', 'woocommerce' ) ); } $this->set_prop( 'tax_class', $value ); } @@ -258,8 +260,8 @@ class WC_Order_Item_Product extends WC_Order_Item { * @throws WC_Data_Exception */ public function set_product_id( $value ) { - if ( 0 >= $value || 'product' !== get_post_type( absint( $value ) ) ) { - $this->throw_exception( 'order_item_product_invalid_product_id', __( 'Invalid product ID', 'woocommerce' ) ); + if ( $value > 0 && 'product' !== get_post_type( absint( $value ) ) ) { + $this->invalid_data( 'order_item_product_invalid_product_id', __( 'Invalid product ID', 'woocommerce' ) ); } $this->set_prop( 'product_id', absint( $value ) ); } @@ -270,8 +272,8 @@ class WC_Order_Item_Product extends WC_Order_Item { * @throws WC_Data_Exception */ public function set_variation_id( $value ) { - if ( 0 >= $value || 'product_variation' !== get_post_type( $value ) ) { - $this->throw_exception( 'order_item_product_invalid_variation_id', __( 'Invalid variation ID', 'woocommerce' ) ); + if ( $value > 0 && 'product_variation' !== get_post_type( $value ) ) { + $this->invalid_data( 'order_item_product_invalid_variation_id', __( 'Invalid variation ID', 'woocommerce' ) ); } $this->set_prop( 'variation_id', absint( $value ) ); } @@ -349,7 +351,7 @@ class WC_Order_Item_Product extends WC_Order_Item { */ public function set_product( $product ) { if ( ! is_a( $product, 'WC_Product' ) ) { - $this->throw_exception( 'order_item_product_invalid_product', __( 'Invalid product', 'woocommerce' ) ); + $this->invalid_data( 'order_item_product_invalid_product', __( 'Invalid product', 'woocommerce' ) ); } $this->set_product_id( $product->get_id() ); $this->set_name( $product->get_title() ); diff --git a/includes/class-wc-order-item-shipping.php b/includes/class-wc-order-item-shipping.php index e2aa57cc9b6..10c3be958e0 100644 --- a/includes/class-wc-order-item-shipping.php +++ b/includes/class-wc-order-item-shipping.php @@ -74,10 +74,12 @@ class WC_Order_Item_Shipping extends WC_Order_Item { public function read( $id ) { parent::read( $id ); if ( $this->get_id() ) { + $this->_reading = true; $this->set_method_id( get_metadata( 'order_item', $this->get_id(), 'method_id', true ) ); $this->set_total( get_metadata( 'order_item', $this->get_id(), 'cost', true ) ); $this->set_total_tax( get_metadata( 'order_item', $this->get_id(), 'total_tax', true ) ); $this->set_taxes( get_metadata( 'order_item', $this->get_id(), 'taxes', true ) ); + $this->_reading = false; } } diff --git a/includes/class-wc-order-item-tax.php b/includes/class-wc-order-item-tax.php index c88d3bf3bba..4bc45fe676f 100644 --- a/includes/class-wc-order-item-tax.php +++ b/includes/class-wc-order-item-tax.php @@ -35,11 +35,13 @@ class WC_Order_Item_Tax extends WC_Order_Item { public function read( $id ) { parent::read( $id ); if ( $this->get_id() ) { + $this->_reading = true; $this->set_rate_id( get_metadata( 'order_item', $this->get_id(), 'rate_id', true ) ); $this->set_label( get_metadata( 'order_item', $this->get_id(), 'label', true ) ); $this->set_compound( get_metadata( 'order_item', $this->get_id(), 'compound', true ) ); $this->set_tax_total( get_metadata( 'order_item', $this->get_id(), 'tax_amount', true ) ); $this->set_shipping_tax_total( get_metadata( 'order_item', $this->get_id(), 'shipping_tax_amount', true ) ); + $this->_reading = false; } } diff --git a/includes/class-wc-order-item.php b/includes/class-wc-order-item.php index 0b84b63c73a..d0479173f3e 100644 --- a/includes/class-wc-order-item.php +++ b/includes/class-wc-order-item.php @@ -70,11 +70,13 @@ class WC_Order_Item extends WC_Data implements ArrayAccess { * @access private */ public function set_all( $data ) { + $this->_reading = true; foreach ( $data as $key => $value ) { if ( is_callable( array( $this, "set_$key" ) ) ) { $this->{"set_$key"}( $value ); } } + $this->_reading = false; } /** @@ -244,11 +246,13 @@ class WC_Order_Item extends WC_Data implements ArrayAccess { } if ( $data ) { + $this->_reading = true; $this->set_order_id( $data->order_id ); $this->set_id( $data->order_item_id ); $this->set_name( $data->order_item_name ); $this->set_type( $data->order_item_type ); $this->read_meta_data(); + $this->_reading = false; } } diff --git a/includes/class-wc-order-refund.php b/includes/class-wc-order-refund.php index dfb28d5455c..d7928680001 100644 --- a/includes/class-wc-order-refund.php +++ b/includes/class-wc-order-refund.php @@ -65,6 +65,7 @@ class WC_Order_Refund extends WC_Abstract_Order { // Read additonal order data if ( $this->get_id() ) { + $this->_reading = true; $post_object = get_post( $id ); $this->set_amount( get_post_meta( $this->get_id(), '_refund_amount', true ) ); @@ -73,6 +74,7 @@ class WC_Order_Refund extends WC_Abstract_Order { // post_excerpt was used before refund_reason meta. $this->set_reason( metadata_exists( 'post', $this->get_id(), '_refund_reason' ) ? get_post_meta( $this->get_id(), '_refund_reason', true ) : absint( $post_object->post_excerpt ) ); + $this->_reading = false; } } diff --git a/includes/class-wc-order.php b/includes/class-wc-order.php index 3ab6b384760..6d25d66b77c 100644 --- a/includes/class-wc-order.php +++ b/includes/class-wc-order.php @@ -36,45 +36,6 @@ class WC_Order extends WC_Abstract_Order { '_customer_note', '_date_completed', '_date_paid', '_payment_tokens', ); - /** - * Map of meta data keys to internal prop keys. - * @var array - */ - protected $prop_to_meta_key = array( - 'order_key' => '_order_key', - 'customer_id' => '_customer_user', - 'billing_first_name' => '_billing_first_name', - 'billing_last_name' => '_billing_last_name', - 'billing_company' => '_billing_company', - 'billing_address_1' => '_billing_address_1', - 'billing_address_2' => '_billing_address_2', - 'billing_city' => '_billing_city', - 'billing_state' => '_billing_state', - 'billing_postcode' => '_billing_postcode', - 'billing_country' => '_billing_country', - 'billing_email' => '_billing_email', - 'billing_phone' => '_billing_phone', - 'shipping_first_name' => '_shipping_first_name', - 'shipping_last_name' => '_shipping_last_name', - 'shipping_company' => '_shipping_company', - 'shipping_address_1' => '_shipping_address_1', - 'shipping_address_2' => '_shipping_address_2', - 'shipping_city' => '_shipping_city', - 'shipping_state' => '_shipping_state', - 'shipping_postcode' => '_shipping_postcode', - 'shipping_country' => '_shipping_country', - 'payment_method' => '_payment_method', - 'payment_method_title' => '_payment_method_title', - 'transaction_id' => '_transaction_id', - 'customer_ip_address' => '_customer_ip_address', - 'customer_user_agent' => '_customer_user_agent', - 'created_via' => '_created_via', - 'customer_note' => '_customer_note', - 'date_completed' => '_completed_date', - 'date_paid' => '_paid_date', - 'cart_hash' => '_cart_hash', - ); - /** * Stores data about status changes so relevant hooks can be fired. * @var bool|array @@ -308,17 +269,45 @@ class WC_Order extends WC_Abstract_Order { return; } - $post_object = get_post( $this->get_id() ); - $values = array( - 'customer_note' => $post_object->post_excerpt, - ); + $this->_reading = true; + $post_object = get_post( $this->get_id() ); - foreach ( $this->prop_to_meta_key as $prop => $meta_key ) { - $values[ $prop ] = get_post_meta( $this->get_id(), $meta_key, true ); - } - - $this->set_props( $values ); + // Read additonal order data + $this->set_order_key( get_post_meta( $this->get_id(), '_order_key', true ) ); + $this->set_customer_id( get_post_meta( $this->get_id(), '_customer_user', true ) ); + $this->set_billing_first_name( get_post_meta( $this->get_id(), '_billing_first_name', true ) ); + $this->set_billing_last_name( get_post_meta( $this->get_id(), '_billing_last_name', true ) ); + $this->set_billing_company( get_post_meta( $this->get_id(), '_billing_company', true ) ); + $this->set_billing_address_1( get_post_meta( $this->get_id(), '_billing_address_1', true ) ); + $this->set_billing_address_2( get_post_meta( $this->get_id(), '_billing_address_2', true ) ); + $this->set_billing_city( get_post_meta( $this->get_id(), '_billing_city', true ) ); + $this->set_billing_state( get_post_meta( $this->get_id(), '_billing_state', true ) ); + $this->set_billing_postcode( get_post_meta( $this->get_id(), '_billing_postcode', true ) ); + $this->set_billing_country( get_post_meta( $this->get_id(), '_billing_country', true ) ); + $this->set_billing_email( get_post_meta( $this->get_id(), '_billing_email', true ) ); + $this->set_billing_phone( get_post_meta( $this->get_id(), '_billing_phone', true ) ); + $this->set_shipping_first_name( get_post_meta( $this->get_id(), '_shipping_first_name', true ) ); + $this->set_shipping_last_name( get_post_meta( $this->get_id(), '_shipping_last_name', true ) ); + $this->set_shipping_company( get_post_meta( $this->get_id(), '_shipping_company', true ) ); + $this->set_shipping_address_1( get_post_meta( $this->get_id(), '_shipping_address_1', true ) ); + $this->set_shipping_address_2( get_post_meta( $this->get_id(), '_shipping_address_2', true ) ); + $this->set_shipping_city( get_post_meta( $this->get_id(), '_shipping_city', true ) ); + $this->set_shipping_state( get_post_meta( $this->get_id(), '_shipping_state', true ) ); + $this->set_shipping_postcode( get_post_meta( $this->get_id(), '_shipping_postcode', true ) ); + $this->set_shipping_country( get_post_meta( $this->get_id(), '_shipping_country', true ) ); + $this->set_payment_method( get_post_meta( $this->get_id(), '_payment_method', true ) ); + $this->set_payment_method_title( get_post_meta( $this->get_id(), '_payment_method_title', true ) ); + $this->set_transaction_id( get_post_meta( $this->get_id(), '_transaction_id', true ) ); + $this->set_customer_ip_address( get_post_meta( $this->get_id(), '_customer_ip_address', true ) ); + $this->set_customer_user_agent( get_post_meta( $this->get_id(), '_customer_user_agent', true ) ); + $this->set_created_via( get_post_meta( $this->get_id(), '_created_via', true ) ); + $this->set_customer_note( get_post_meta( $this->get_id(), '_customer_note', true ) ); + $this->set_date_completed( get_post_meta( $this->get_id(), '_completed_date', true ) ); + $this->set_date_paid( get_post_meta( $this->get_id(), '_paid_date', true ) ); + $this->set_cart_hash( get_post_meta( $this->get_id(), '_cart_hash', true ) ); + $this->set_customer_note( $post_object->post_excerpt ); $this->maybe_set_user_billing_email(); + $this->_reading = false; } /** @@ -957,7 +946,7 @@ class WC_Order extends WC_Abstract_Order { */ public function set_billing_email( $value ) { if ( $value && ! is_email( $value ) ) { - $this->throw_exception( 'order_invalid_billing_email', __( 'Invalid order billing email address', 'woocommerce' ) ); + $this->invalid_data( 'order_invalid_billing_email', __( 'Invalid order billing email address', 'woocommerce' ) ); } $this->set_prop( 'billing', 'email', sanitize_email( $value ) ); } diff --git a/tests/unit-tests/order/crud.php b/tests/unit-tests/order/crud.php index 4035e81c48c..880c32974e1 100644 --- a/tests/unit-tests/order/crud.php +++ b/tests/unit-tests/order/crud.php @@ -799,6 +799,7 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { $this->assertEquals( $set_to, $object->get_billing_email() ); $set_to = 'not an email'; + $this->setExpectedException( 'WC_Data_Exception' ); $object->set_billing_email( $set_to ); $this->assertEquals( 'test@test.com', $object->get_billing_email() ); }