abstract-wc-data.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. <?php
  2. /**
  3. * Abstract Data.
  4. *
  5. * Handles generic data interaction which is implemented by
  6. * the different data store classes.
  7. *
  8. * @class WC_Data
  9. * @version 3.0.0
  10. * @package WooCommerce/Classes
  11. */
  12. if ( ! defined( 'ABSPATH' ) ) {
  13. exit;
  14. }
  15. /**
  16. * Abstract WC Data Class
  17. *
  18. * Implemented by classes using the same CRUD(s) pattern.
  19. *
  20. * @version 2.6.0
  21. * @package WooCommerce/Abstracts
  22. */
  23. abstract class WC_Data {
  24. /**
  25. * ID for this object.
  26. *
  27. * @since 3.0.0
  28. * @var int
  29. */
  30. protected $id = 0;
  31. /**
  32. * Core data for this object. Name value pairs (name + default value).
  33. *
  34. * @since 3.0.0
  35. * @var array
  36. */
  37. protected $data = array();
  38. /**
  39. * Core data changes for this object.
  40. *
  41. * @since 3.0.0
  42. * @var array
  43. */
  44. protected $changes = array();
  45. /**
  46. * This is false until the object is read from the DB.
  47. *
  48. * @since 3.0.0
  49. * @var bool
  50. */
  51. protected $object_read = false;
  52. /**
  53. * This is the name of this object type.
  54. *
  55. * @since 3.0.0
  56. * @var string
  57. */
  58. protected $object_type = 'data';
  59. /**
  60. * Extra data for this object. Name value pairs (name + default value).
  61. * Used as a standard way for sub classes (like product types) to add
  62. * additional information to an inherited class.
  63. *
  64. * @since 3.0.0
  65. * @var array
  66. */
  67. protected $extra_data = array();
  68. /**
  69. * Set to _data on construct so we can track and reset data if needed.
  70. *
  71. * @since 3.0.0
  72. * @var array
  73. */
  74. protected $default_data = array();
  75. /**
  76. * Contains a reference to the data store for this class.
  77. *
  78. * @since 3.0.0
  79. * @var object
  80. */
  81. protected $data_store;
  82. /**
  83. * Stores meta in cache for future reads.
  84. * A group must be set to to enable caching.
  85. *
  86. * @since 3.0.0
  87. * @var string
  88. */
  89. protected $cache_group = '';
  90. /**
  91. * Stores additional meta data.
  92. *
  93. * @since 3.0.0
  94. * @var array
  95. */
  96. protected $meta_data = null;
  97. /**
  98. * Default constructor.
  99. *
  100. * @param int|object|array $read ID to load from the DB (optional) or already queried data.
  101. */
  102. public function __construct( $read = 0 ) {
  103. $this->data = array_merge( $this->data, $this->extra_data );
  104. $this->default_data = $this->data;
  105. }
  106. /**
  107. * Only store the object ID to avoid serializing the data object instance.
  108. *
  109. * @return array
  110. */
  111. public function __sleep() {
  112. return array( 'id' );
  113. }
  114. /**
  115. * Re-run the constructor with the object ID.
  116. *
  117. * If the object no longer exists, remove the ID.
  118. */
  119. public function __wakeup() {
  120. try {
  121. $this->__construct( absint( $this->id ) );
  122. } catch ( Exception $e ) {
  123. $this->set_id( 0 );
  124. $this->set_object_read( true );
  125. }
  126. }
  127. /**
  128. * When the object is cloned, make sure meta is duplicated correctly.
  129. *
  130. * @since 3.0.2
  131. */
  132. public function __clone() {
  133. $this->maybe_read_meta_data();
  134. if ( ! empty( $this->meta_data ) ) {
  135. foreach ( $this->meta_data as $array_key => $meta ) {
  136. $this->meta_data[ $array_key ] = clone $meta;
  137. if ( ! empty( $meta->id ) ) {
  138. $this->meta_data[ $array_key ]->id = null;
  139. }
  140. }
  141. }
  142. }
  143. /**
  144. * Get the data store.
  145. *
  146. * @since 3.0.0
  147. * @return object
  148. */
  149. public function get_data_store() {
  150. return $this->data_store;
  151. }
  152. /**
  153. * Returns the unique ID for this object.
  154. *
  155. * @since 2.6.0
  156. * @return int
  157. */
  158. public function get_id() {
  159. return $this->id;
  160. }
  161. /**
  162. * Delete an object, set the ID to 0, and return result.
  163. *
  164. * @since 2.6.0
  165. * @param bool $force_delete Should the date be deleted permanently.
  166. * @return bool result
  167. */
  168. public function delete( $force_delete = false ) {
  169. if ( $this->data_store ) {
  170. $this->data_store->delete( $this, array( 'force_delete' => $force_delete ) );
  171. $this->set_id( 0 );
  172. return true;
  173. }
  174. return false;
  175. }
  176. /**
  177. * Save should create or update based on object existence.
  178. *
  179. * @since 2.6.0
  180. * @return int
  181. */
  182. public function save() {
  183. if ( $this->data_store ) {
  184. // Trigger action before saving to the DB. Allows you to adjust object props before save.
  185. do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store );
  186. if ( $this->get_id() ) {
  187. $this->data_store->update( $this );
  188. } else {
  189. $this->data_store->create( $this );
  190. }
  191. }
  192. return $this->get_id();
  193. }
  194. /**
  195. * Change data to JSON format.
  196. *
  197. * @since 2.6.0
  198. * @return string Data in JSON format.
  199. */
  200. public function __toString() {
  201. return json_encode( $this->get_data() );
  202. }
  203. /**
  204. * Returns all data for this object.
  205. *
  206. * @since 2.6.0
  207. * @return array
  208. */
  209. public function get_data() {
  210. return array_merge( array( 'id' => $this->get_id() ), $this->data, array( 'meta_data' => $this->get_meta_data() ) );
  211. }
  212. /**
  213. * Returns array of expected data keys for this object.
  214. *
  215. * @since 3.0.0
  216. * @return array
  217. */
  218. public function get_data_keys() {
  219. return array_keys( $this->data );
  220. }
  221. /**
  222. * Returns all "extra" data keys for an object (for sub objects like product types).
  223. *
  224. * @since 3.0.0
  225. * @return array
  226. */
  227. public function get_extra_data_keys() {
  228. return array_keys( $this->extra_data );
  229. }
  230. /**
  231. * Filter null meta values from array.
  232. *
  233. * @since 3.0.0
  234. * @param mixed $meta Meta value to check.
  235. * @return bool
  236. */
  237. protected function filter_null_meta( $meta ) {
  238. return ! is_null( $meta->value );
  239. }
  240. /**
  241. * Get All Meta Data.
  242. *
  243. * @since 2.6.0
  244. * @return array of objects.
  245. */
  246. public function get_meta_data() {
  247. $this->maybe_read_meta_data();
  248. return array_values( array_filter( $this->meta_data, array( $this, 'filter_null_meta' ) ) );
  249. }
  250. /**
  251. * Check if the key is an internal one.
  252. *
  253. * @since 3.2.0
  254. * @param string $key Key to check.
  255. * @return bool true if it's an internal key, false otherwise
  256. */
  257. protected function is_internal_meta_key( $key ) {
  258. $internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, $this->data_store->get_internal_meta_keys() );
  259. if ( ! $internal_meta_key ) {
  260. return false;
  261. }
  262. $has_setter_or_getter = is_callable( array( $this, 'set_' . $key ) ) || is_callable( array( $this, 'get_' . $key ) );
  263. if ( ! $has_setter_or_getter ) {
  264. return false;
  265. }
  266. /* translators: %s: $key Key to check */
  267. wc_doing_it_wrong( __FUNCTION__, sprintf( __( 'Generic add/update/get meta methods should not be used for internal meta data, including "%s". Use getters and setters.', 'woocommerce' ), $key ), '3.2.0' );
  268. return true;
  269. }
  270. /**
  271. * Get Meta Data by Key.
  272. *
  273. * @since 2.6.0
  274. * @param string $key Meta Key.
  275. * @param bool $single return first found meta with key, or all with $key.
  276. * @param string $context What the value is for. Valid values are view and edit.
  277. * @return mixed
  278. */
  279. public function get_meta( $key = '', $single = true, $context = 'view' ) {
  280. if ( $this->is_internal_meta_key( $key ) ) {
  281. $function = 'get_' . $key;
  282. if ( is_callable( array( $this, $function ) ) ) {
  283. return $this->{$function}();
  284. }
  285. }
  286. $this->maybe_read_meta_data();
  287. $meta_data = $this->get_meta_data();
  288. $array_keys = array_keys( wp_list_pluck( $meta_data, 'key' ), $key );
  289. $value = $single ? '' : array();
  290. if ( ! empty( $array_keys ) ) {
  291. // We don't use the $this->meta_data property directly here because we don't want meta with a null value (i.e. meta which has been deleted via $this->delete_meta_data()).
  292. if ( $single ) {
  293. $value = $meta_data[ current( $array_keys ) ]->value;
  294. } else {
  295. $value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
  296. }
  297. if ( 'view' === $context ) {
  298. $value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
  299. }
  300. }
  301. return $value;
  302. }
  303. /**
  304. * See if meta data exists, since get_meta always returns a '' or array().
  305. *
  306. * @since 3.0.0
  307. * @param string $key Meta Key.
  308. * @return boolean
  309. */
  310. public function meta_exists( $key = '' ) {
  311. $this->maybe_read_meta_data();
  312. $array_keys = wp_list_pluck( $this->get_meta_data(), 'key' );
  313. return in_array( $key, $array_keys );
  314. }
  315. /**
  316. * Set all meta data from array.
  317. *
  318. * @since 2.6.0
  319. * @param array $data Key/Value pairs.
  320. */
  321. public function set_meta_data( $data ) {
  322. if ( ! empty( $data ) && is_array( $data ) ) {
  323. $this->maybe_read_meta_data();
  324. foreach ( $data as $meta ) {
  325. $meta = (array) $meta;
  326. if ( isset( $meta['key'], $meta['value'], $meta['id'] ) ) {
  327. $this->meta_data[] = new WC_Meta_Data( array(
  328. 'id' => $meta['id'],
  329. 'key' => $meta['key'],
  330. 'value' => $meta['value'],
  331. ) );
  332. }
  333. }
  334. }
  335. }
  336. /**
  337. * Add meta data.
  338. *
  339. * @since 2.6.0
  340. * @param string $key Meta key.
  341. * @param string $value Meta value.
  342. * @param bool $unique Should this be a unique key?.
  343. */
  344. public function add_meta_data( $key, $value, $unique = false ) {
  345. if ( $this->is_internal_meta_key( $key ) ) {
  346. $function = 'set_' . $key;
  347. if ( is_callable( array( $this, $function ) ) ) {
  348. return $this->{$function}( $value );
  349. }
  350. }
  351. $this->maybe_read_meta_data();
  352. if ( $unique ) {
  353. $this->delete_meta_data( $key );
  354. }
  355. $this->meta_data[] = new WC_Meta_Data( array(
  356. 'key' => $key,
  357. 'value' => $value,
  358. ) );
  359. }
  360. /**
  361. * Update meta data by key or ID, if provided.
  362. *
  363. * @since 2.6.0
  364. * @param string $key Meta key.
  365. * @param string $value Meta value.
  366. * @param int $meta_id Meta ID.
  367. */
  368. public function update_meta_data( $key, $value, $meta_id = 0 ) {
  369. if ( $this->is_internal_meta_key( $key ) ) {
  370. $function = 'set_' . $key;
  371. if ( is_callable( array( $this, $function ) ) ) {
  372. return $this->{$function}( $value );
  373. }
  374. }
  375. $this->maybe_read_meta_data();
  376. $array_key = $meta_id ? array_keys( wp_list_pluck( $this->meta_data, 'id' ), $meta_id ) : '';
  377. if ( $array_key ) {
  378. $meta = $this->meta_data[ current( $array_key ) ];
  379. $meta->key = $key;
  380. $meta->value = $value;
  381. } else {
  382. $this->add_meta_data( $key, $value, true );
  383. }
  384. }
  385. /**
  386. * Delete meta data.
  387. *
  388. * @since 2.6.0
  389. * @param string $key Meta key.
  390. */
  391. public function delete_meta_data( $key ) {
  392. $this->maybe_read_meta_data();
  393. $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'key' ), $key );
  394. if ( $array_keys ) {
  395. foreach ( $array_keys as $array_key ) {
  396. $this->meta_data[ $array_key ]->value = null;
  397. }
  398. }
  399. }
  400. /**
  401. * Delete meta data.
  402. *
  403. * @since 2.6.0
  404. * @param int $mid Meta ID.
  405. */
  406. public function delete_meta_data_by_mid( $mid ) {
  407. $this->maybe_read_meta_data();
  408. $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), $mid );
  409. if ( $array_keys ) {
  410. foreach ( $array_keys as $array_key ) {
  411. $this->meta_data[ $array_key ]->value = null;
  412. }
  413. }
  414. }
  415. /**
  416. * Read meta data if null.
  417. *
  418. * @since 3.0.0
  419. */
  420. protected function maybe_read_meta_data() {
  421. if ( is_null( $this->meta_data ) ) {
  422. $this->read_meta_data();
  423. }
  424. }
  425. /**
  426. * Read Meta Data from the database. Ignore any internal properties.
  427. * Uses it's own caches because get_metadata does not provide meta_ids.
  428. *
  429. * @since 2.6.0
  430. * @param bool $force_read True to force a new DB read (and update cache).
  431. */
  432. public function read_meta_data( $force_read = false ) {
  433. $this->meta_data = array();
  434. $cache_loaded = false;
  435. if ( ! $this->get_id() ) {
  436. return;
  437. }
  438. if ( ! $this->data_store ) {
  439. return;
  440. }
  441. if ( ! empty( $this->cache_group ) ) {
  442. // Prefix by group allows invalidation by group until https://core.trac.wordpress.org/ticket/4476 is implemented.
  443. $cache_key = WC_Cache_Helper::get_cache_prefix( $this->cache_group ) . WC_Cache_Helper::get_cache_prefix( 'object_' . $this->get_id() ) . 'object_meta_' . $this->get_id();
  444. }
  445. if ( ! $force_read ) {
  446. if ( ! empty( $this->cache_group ) ) {
  447. $cached_meta = wp_cache_get( $cache_key, $this->cache_group );
  448. $cache_loaded = ! empty( $cached_meta );
  449. }
  450. }
  451. $raw_meta_data = $cache_loaded ? $cached_meta : $this->data_store->read_meta( $this );
  452. if ( $raw_meta_data ) {
  453. foreach ( $raw_meta_data as $meta ) {
  454. $this->meta_data[] = new WC_Meta_Data( array(
  455. 'id' => (int) $meta->meta_id,
  456. 'key' => $meta->meta_key,
  457. 'value' => maybe_unserialize( $meta->meta_value ),
  458. ) );
  459. }
  460. if ( ! $cache_loaded && ! empty( $this->cache_group ) ) {
  461. wp_cache_set( $cache_key, $raw_meta_data, $this->cache_group );
  462. }
  463. }
  464. }
  465. /**
  466. * Update Meta Data in the database.
  467. *
  468. * @since 2.6.0
  469. */
  470. public function save_meta_data() {
  471. if ( ! $this->data_store || is_null( $this->meta_data ) ) {
  472. return;
  473. }
  474. foreach ( $this->meta_data as $array_key => $meta ) {
  475. if ( is_null( $meta->value ) ) {
  476. if ( ! empty( $meta->id ) ) {
  477. $this->data_store->delete_meta( $this, $meta );
  478. unset( $this->meta_data[ $array_key ] );
  479. }
  480. } elseif ( empty( $meta->id ) ) {
  481. $meta->id = $this->data_store->add_meta( $this, $meta );
  482. $meta->apply_changes();
  483. } else {
  484. if ( $meta->get_changes() ) {
  485. $this->data_store->update_meta( $this, $meta );
  486. $meta->apply_changes();
  487. }
  488. }
  489. }
  490. if ( ! empty( $this->cache_group ) ) {
  491. $cache_key = WC_Cache_Helper::get_cache_prefix( $this->cache_group ) . WC_Cache_Helper::get_cache_prefix( 'object_' . $this->get_id() ) . 'object_meta_' . $this->get_id();
  492. wp_cache_delete( $cache_key, $this->cache_group );
  493. }
  494. }
  495. /**
  496. * Set ID.
  497. *
  498. * @since 3.0.0
  499. * @param int $id ID.
  500. */
  501. public function set_id( $id ) {
  502. $this->id = absint( $id );
  503. }
  504. /**
  505. * Set all props to default values.
  506. *
  507. * @since 3.0.0
  508. */
  509. public function set_defaults() {
  510. $this->data = $this->default_data;
  511. $this->changes = array();
  512. $this->set_object_read( false );
  513. }
  514. /**
  515. * Set object read property.
  516. *
  517. * @since 3.0.0
  518. * @param boolean $read Should read?.
  519. */
  520. public function set_object_read( $read = true ) {
  521. $this->object_read = (bool) $read;
  522. }
  523. /**
  524. * Get object read property.
  525. *
  526. * @since 3.0.0
  527. * @return boolean
  528. */
  529. public function get_object_read() {
  530. return (bool) $this->object_read;
  531. }
  532. /**
  533. * Set a collection of props in one go, collect any errors, and return the result.
  534. * Only sets using public methods.
  535. *
  536. * @since 3.0.0
  537. *
  538. * @param array $props Key value pairs to set. Key is the prop and should map to a setter function name.
  539. * @param string $context In what context to run this.
  540. *
  541. * @return bool|WP_Error
  542. */
  543. public function set_props( $props, $context = 'set' ) {
  544. $errors = new WP_Error();
  545. foreach ( $props as $prop => $value ) {
  546. try {
  547. if ( 'meta_data' === $prop ) {
  548. continue;
  549. }
  550. $setter = "set_$prop";
  551. if ( ! is_null( $value ) && is_callable( array( $this, $setter ) ) ) {
  552. $reflection = new ReflectionMethod( $this, $setter );
  553. if ( $reflection->isPublic() ) {
  554. $this->{$setter}( $value );
  555. }
  556. }
  557. } catch ( WC_Data_Exception $e ) {
  558. $errors->add( $e->getErrorCode(), $e->getMessage() );
  559. }
  560. }
  561. return count( $errors->get_error_codes() ) ? $errors : true;
  562. }
  563. /**
  564. * Sets a prop for a setter method.
  565. *
  566. * This stores changes in a special array so we can track what needs saving
  567. * the the DB later.
  568. *
  569. * @since 3.0.0
  570. * @param string $prop Name of prop to set.
  571. * @param mixed $value Value of the prop.
  572. */
  573. protected function set_prop( $prop, $value ) {
  574. if ( array_key_exists( $prop, $this->data ) ) {
  575. if ( true === $this->object_read ) {
  576. if ( $value !== $this->data[ $prop ] || array_key_exists( $prop, $this->changes ) ) {
  577. $this->changes[ $prop ] = $value;
  578. }
  579. } else {
  580. $this->data[ $prop ] = $value;
  581. }
  582. }
  583. }
  584. /**
  585. * Return data changes only.
  586. *
  587. * @since 3.0.0
  588. * @return array
  589. */
  590. public function get_changes() {
  591. return $this->changes;
  592. }
  593. /**
  594. * Merge changes with data and clear.
  595. *
  596. * @since 3.0.0
  597. */
  598. public function apply_changes() {
  599. $this->data = array_replace_recursive( $this->data, $this->changes ); // @codingStandardsIgnoreLine
  600. $this->changes = array();
  601. }
  602. /**
  603. * Prefix for action and filter hooks on data.
  604. *
  605. * @since 3.0.0
  606. * @return string
  607. */
  608. protected function get_hook_prefix() {
  609. return 'woocommerce_' . $this->object_type . '_get_';
  610. }
  611. /**
  612. * Gets a prop for a getter method.
  613. *
  614. * Gets the value from either current pending changes, or the data itself.
  615. * Context controls what happens to the value before it's returned.
  616. *
  617. * @since 3.0.0
  618. * @param string $prop Name of prop to get.
  619. * @param string $context What the value is for. Valid values are view and edit.
  620. * @return mixed
  621. */
  622. protected function get_prop( $prop, $context = 'view' ) {
  623. $value = null;
  624. if ( array_key_exists( $prop, $this->data ) ) {
  625. $value = array_key_exists( $prop, $this->changes ) ? $this->changes[ $prop ] : $this->data[ $prop ];
  626. if ( 'view' === $context ) {
  627. $value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this );
  628. }
  629. }
  630. return $value;
  631. }
  632. /**
  633. * Sets a date prop whilst handling formatting and datetime objects.
  634. *
  635. * @since 3.0.0
  636. * @param string $prop Name of prop to set.
  637. * @param string|integer $value Value of the prop.
  638. */
  639. protected function set_date_prop( $prop, $value ) {
  640. try {
  641. if ( empty( $value ) ) {
  642. $this->set_prop( $prop, null );
  643. return;
  644. }
  645. if ( is_a( $value, 'WC_DateTime' ) ) {
  646. $datetime = $value;
  647. } elseif ( is_numeric( $value ) ) {
  648. // Timestamps are handled as UTC timestamps in all cases.
  649. $datetime = new WC_DateTime( "@{$value}", new DateTimeZone( 'UTC' ) );
  650. } else {
  651. // Strings are defined in local WP timezone. Convert to UTC.
  652. if ( 1 === preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))$/', $value, $date_bits ) ) {
  653. $offset = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset();
  654. $timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
  655. } else {
  656. $timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $value ) ) ) );
  657. }
  658. $datetime = new WC_DateTime( "@{$timestamp}", new DateTimeZone( 'UTC' ) );
  659. }
  660. // Set local timezone or offset.
  661. if ( get_option( 'timezone_string' ) ) {
  662. $datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
  663. } else {
  664. $datetime->set_utc_offset( wc_timezone_offset() );
  665. }
  666. $this->set_prop( $prop, $datetime );
  667. } catch ( Exception $e ) {} // @codingStandardsIgnoreLine.
  668. }
  669. /**
  670. * When invalid data is found, throw an exception unless reading from the DB.
  671. *
  672. * @throws WC_Data_Exception Data Exception.
  673. * @since 3.0.0
  674. * @param string $code Error code.
  675. * @param string $message Error message.
  676. * @param int $http_status_code HTTP status code.
  677. * @param array $data Extra error data.
  678. */
  679. protected function error( $code, $message, $http_status_code = 400, $data = array() ) {
  680. throw new WC_Data_Exception( $code, $message, $http_status_code, $data );
  681. }
  682. }