PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 5.0.1
WooCommerce v5.0.1
10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / abstracts / abstract-wc-data.php
woocommerce / includes / abstracts Last commit date
abstract-wc-data.php 5 years ago abstract-wc-deprecated-hooks.php 6 years ago abstract-wc-integration.php 5 years ago abstract-wc-log-handler.php 5 years ago abstract-wc-object-query.php 5 years ago abstract-wc-order.php 5 years ago abstract-wc-payment-gateway.php 5 years ago abstract-wc-payment-token.php 5 years ago abstract-wc-privacy.php 5 years ago abstract-wc-product.php 5 years ago abstract-wc-session.php 5 years ago abstract-wc-settings-api.php 5 years ago abstract-wc-shipping-method.php 5 years ago abstract-wc-widget.php 5 years ago class-wc-background-process.php 5 years ago
abstract-wc-data.php
859 lines
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
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Abstract WC Data Class
19 *
20 * Implemented by classes using the same CRUD(s) pattern.
21 *
22 * @version 2.6.0
23 * @package WooCommerce\Abstracts
24 */
25 abstract class WC_Data {
26
27 /**
28 * ID for this object.
29 *
30 * @since 3.0.0
31 * @var int
32 */
33 protected $id = 0;
34
35 /**
36 * Core data for this object. Name value pairs (name + default value).
37 *
38 * @since 3.0.0
39 * @var array
40 */
41 protected $data = array();
42
43 /**
44 * Core data changes for this object.
45 *
46 * @since 3.0.0
47 * @var array
48 */
49 protected $changes = array();
50
51 /**
52 * This is false until the object is read from the DB.
53 *
54 * @since 3.0.0
55 * @var bool
56 */
57 protected $object_read = false;
58
59 /**
60 * This is the name of this object type.
61 *
62 * @since 3.0.0
63 * @var string
64 */
65 protected $object_type = 'data';
66
67 /**
68 * Extra data for this object. Name value pairs (name + default value).
69 * Used as a standard way for sub classes (like product types) to add
70 * additional information to an inherited class.
71 *
72 * @since 3.0.0
73 * @var array
74 */
75 protected $extra_data = array();
76
77 /**
78 * Set to _data on construct so we can track and reset data if needed.
79 *
80 * @since 3.0.0
81 * @var array
82 */
83 protected $default_data = array();
84
85 /**
86 * Contains a reference to the data store for this class.
87 *
88 * @since 3.0.0
89 * @var object
90 */
91 protected $data_store;
92
93 /**
94 * Stores meta in cache for future reads.
95 * A group must be set to to enable caching.
96 *
97 * @since 3.0.0
98 * @var string
99 */
100 protected $cache_group = '';
101
102 /**
103 * Stores additional meta data.
104 *
105 * @since 3.0.0
106 * @var array
107 */
108 protected $meta_data = null;
109
110 /**
111 * Default constructor.
112 *
113 * @param int|object|array $read ID to load from the DB (optional) or already queried data.
114 */
115 public function __construct( $read = 0 ) {
116 $this->data = array_merge( $this->data, $this->extra_data );
117 $this->default_data = $this->data;
118 }
119
120 /**
121 * Only store the object ID to avoid serializing the data object instance.
122 *
123 * @return array
124 */
125 public function __sleep() {
126 return array( 'id' );
127 }
128
129 /**
130 * Re-run the constructor with the object ID.
131 *
132 * If the object no longer exists, remove the ID.
133 */
134 public function __wakeup() {
135 try {
136 $this->__construct( absint( $this->id ) );
137 } catch ( Exception $e ) {
138 $this->set_id( 0 );
139 $this->set_object_read( true );
140 }
141 }
142
143 /**
144 * When the object is cloned, make sure meta is duplicated correctly.
145 *
146 * @since 3.0.2
147 */
148 public function __clone() {
149 $this->maybe_read_meta_data();
150 if ( ! empty( $this->meta_data ) ) {
151 foreach ( $this->meta_data as $array_key => $meta ) {
152 $this->meta_data[ $array_key ] = clone $meta;
153 if ( ! empty( $meta->id ) ) {
154 $this->meta_data[ $array_key ]->id = null;
155 }
156 }
157 }
158 }
159
160 /**
161 * Get the data store.
162 *
163 * @since 3.0.0
164 * @return object
165 */
166 public function get_data_store() {
167 return $this->data_store;
168 }
169
170 /**
171 * Returns the unique ID for this object.
172 *
173 * @since 2.6.0
174 * @return int
175 */
176 public function get_id() {
177 return $this->id;
178 }
179
180 /**
181 * Delete an object, set the ID to 0, and return result.
182 *
183 * @since 2.6.0
184 * @param bool $force_delete Should the date be deleted permanently.
185 * @return bool result
186 */
187 public function delete( $force_delete = false ) {
188 if ( $this->data_store ) {
189 $this->data_store->delete( $this, array( 'force_delete' => $force_delete ) );
190 $this->set_id( 0 );
191 return true;
192 }
193 return false;
194 }
195
196 /**
197 * Save should create or update based on object existence.
198 *
199 * @since 2.6.0
200 * @return int
201 */
202 public function save() {
203 if ( ! $this->data_store ) {
204 return $this->get_id();
205 }
206
207 /**
208 * Trigger action before saving to the DB. Allows you to adjust object props before save.
209 *
210 * @param WC_Data $this The object being saved.
211 * @param WC_Data_Store_WP $data_store THe data store persisting the data.
212 */
213 do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store );
214
215 if ( $this->get_id() ) {
216 $this->data_store->update( $this );
217 } else {
218 $this->data_store->create( $this );
219 }
220
221 /**
222 * Trigger action after saving to the DB.
223 *
224 * @param WC_Data $this The object being saved.
225 * @param WC_Data_Store_WP $data_store THe data store persisting the data.
226 */
227 do_action( 'woocommerce_after_' . $this->object_type . '_object_save', $this, $this->data_store );
228
229 return $this->get_id();
230 }
231
232 /**
233 * Change data to JSON format.
234 *
235 * @since 2.6.0
236 * @return string Data in JSON format.
237 */
238 public function __toString() {
239 return wp_json_encode( $this->get_data() );
240 }
241
242 /**
243 * Returns all data for this object.
244 *
245 * @since 2.6.0
246 * @return array
247 */
248 public function get_data() {
249 return array_merge( array( 'id' => $this->get_id() ), $this->data, array( 'meta_data' => $this->get_meta_data() ) );
250 }
251
252 /**
253 * Returns array of expected data keys for this object.
254 *
255 * @since 3.0.0
256 * @return array
257 */
258 public function get_data_keys() {
259 return array_keys( $this->data );
260 }
261
262 /**
263 * Returns all "extra" data keys for an object (for sub objects like product types).
264 *
265 * @since 3.0.0
266 * @return array
267 */
268 public function get_extra_data_keys() {
269 return array_keys( $this->extra_data );
270 }
271
272 /**
273 * Filter null meta values from array.
274 *
275 * @since 3.0.0
276 * @param mixed $meta Meta value to check.
277 * @return bool
278 */
279 protected function filter_null_meta( $meta ) {
280 return ! is_null( $meta->value );
281 }
282
283 /**
284 * Get All Meta Data.
285 *
286 * @since 2.6.0
287 * @return array of objects.
288 */
289 public function get_meta_data() {
290 $this->maybe_read_meta_data();
291 return array_values( array_filter( $this->meta_data, array( $this, 'filter_null_meta' ) ) );
292 }
293
294 /**
295 * Check if the key is an internal one.
296 *
297 * @since 3.2.0
298 * @param string $key Key to check.
299 * @return bool true if it's an internal key, false otherwise
300 */
301 protected function is_internal_meta_key( $key ) {
302 $internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, $this->data_store->get_internal_meta_keys(), true );
303
304 if ( ! $internal_meta_key ) {
305 return false;
306 }
307
308 $has_setter_or_getter = is_callable( array( $this, 'set_' . $key ) ) || is_callable( array( $this, 'get_' . $key ) );
309
310 if ( ! $has_setter_or_getter ) {
311 return false;
312 }
313 /* translators: %s: $key Key to check */
314 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' );
315
316 return true;
317 }
318
319 /**
320 * Get Meta Data by Key.
321 *
322 * @since 2.6.0
323 * @param string $key Meta Key.
324 * @param bool $single return first found meta with key, or all with $key.
325 * @param string $context What the value is for. Valid values are view and edit.
326 * @return mixed
327 */
328 public function get_meta( $key = '', $single = true, $context = 'view' ) {
329 if ( $this->is_internal_meta_key( $key ) ) {
330 $function = 'get_' . $key;
331
332 if ( is_callable( array( $this, $function ) ) ) {
333 return $this->{$function}();
334 }
335 }
336
337 $this->maybe_read_meta_data();
338 $meta_data = $this->get_meta_data();
339 $array_keys = array_keys( wp_list_pluck( $meta_data, 'key' ), $key, true );
340 $value = $single ? '' : array();
341
342 if ( ! empty( $array_keys ) ) {
343 // 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()).
344 if ( $single ) {
345 $value = $meta_data[ current( $array_keys ) ]->value;
346 } else {
347 $value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
348 }
349 }
350
351 if ( 'view' === $context ) {
352 $value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
353 }
354
355 return $value;
356 }
357
358 /**
359 * See if meta data exists, since get_meta always returns a '' or array().
360 *
361 * @since 3.0.0
362 * @param string $key Meta Key.
363 * @return boolean
364 */
365 public function meta_exists( $key = '' ) {
366 $this->maybe_read_meta_data();
367 $array_keys = wp_list_pluck( $this->get_meta_data(), 'key' );
368 return in_array( $key, $array_keys, true );
369 }
370
371 /**
372 * Set all meta data from array.
373 *
374 * @since 2.6.0
375 * @param array $data Key/Value pairs.
376 */
377 public function set_meta_data( $data ) {
378 if ( ! empty( $data ) && is_array( $data ) ) {
379 $this->maybe_read_meta_data();
380 foreach ( $data as $meta ) {
381 $meta = (array) $meta;
382 if ( isset( $meta['key'], $meta['value'], $meta['id'] ) ) {
383 $this->meta_data[] = new WC_Meta_Data(
384 array(
385 'id' => $meta['id'],
386 'key' => $meta['key'],
387 'value' => $meta['value'],
388 )
389 );
390 }
391 }
392 }
393 }
394
395 /**
396 * Add meta data.
397 *
398 * @since 2.6.0
399 *
400 * @param string $key Meta key.
401 * @param string|array $value Meta value.
402 * @param bool $unique Should this be a unique key?.
403 */
404 public function add_meta_data( $key, $value, $unique = false ) {
405 if ( $this->is_internal_meta_key( $key ) ) {
406 $function = 'set_' . $key;
407
408 if ( is_callable( array( $this, $function ) ) ) {
409 return $this->{$function}( $value );
410 }
411 }
412
413 $this->maybe_read_meta_data();
414 if ( $unique ) {
415 $this->delete_meta_data( $key );
416 }
417 $this->meta_data[] = new WC_Meta_Data(
418 array(
419 'key' => $key,
420 'value' => $value,
421 )
422 );
423 }
424
425 /**
426 * Update meta data by key or ID, if provided.
427 *
428 * @since 2.6.0
429 *
430 * @param string $key Meta key.
431 * @param string|array $value Meta value.
432 * @param int $meta_id Meta ID.
433 */
434 public function update_meta_data( $key, $value, $meta_id = 0 ) {
435 if ( $this->is_internal_meta_key( $key ) ) {
436 $function = 'set_' . $key;
437
438 if ( is_callable( array( $this, $function ) ) ) {
439 return $this->{$function}( $value );
440 }
441 }
442
443 $this->maybe_read_meta_data();
444
445 $array_key = false;
446
447 if ( $meta_id ) {
448 $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), $meta_id, true );
449 $array_key = $array_keys ? current( $array_keys ) : false;
450 } else {
451 // Find matches by key.
452 $matches = array();
453 foreach ( $this->meta_data as $meta_data_array_key => $meta ) {
454 if ( $meta->key === $key ) {
455 $matches[] = $meta_data_array_key;
456 }
457 }
458
459 if ( ! empty( $matches ) ) {
460 // Set matches to null so only one key gets the new value.
461 foreach ( $matches as $meta_data_array_key ) {
462 $this->meta_data[ $meta_data_array_key ]->value = null;
463 }
464 $array_key = current( $matches );
465 }
466 }
467
468 if ( false !== $array_key ) {
469 $meta = $this->meta_data[ $array_key ];
470 $meta->key = $key;
471 $meta->value = $value;
472 } else {
473 $this->add_meta_data( $key, $value, true );
474 }
475 }
476
477 /**
478 * Delete meta data.
479 *
480 * @since 2.6.0
481 * @param string $key Meta key.
482 */
483 public function delete_meta_data( $key ) {
484 $this->maybe_read_meta_data();
485 $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'key' ), $key, true );
486
487 if ( $array_keys ) {
488 foreach ( $array_keys as $array_key ) {
489 $this->meta_data[ $array_key ]->value = null;
490 }
491 }
492 }
493
494 /**
495 * Delete meta data.
496 *
497 * @since 2.6.0
498 * @param int $mid Meta ID.
499 */
500 public function delete_meta_data_by_mid( $mid ) {
501 $this->maybe_read_meta_data();
502 $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), (int) $mid, true );
503
504 if ( $array_keys ) {
505 foreach ( $array_keys as $array_key ) {
506 $this->meta_data[ $array_key ]->value = null;
507 }
508 }
509 }
510
511 /**
512 * Read meta data if null.
513 *
514 * @since 3.0.0
515 */
516 protected function maybe_read_meta_data() {
517 if ( is_null( $this->meta_data ) ) {
518 $this->read_meta_data();
519 }
520 }
521
522 /**
523 * Helper method to compute meta cache key. Different from WP Meta cache key in that meta data cached using this key also contains meta_id column.
524 *
525 * @since 4.7.0
526 *
527 * @return string
528 */
529 public function get_meta_cache_key() {
530 if ( ! $this->get_id() ) {
531 wc_doing_it_wrong( 'get_meta_cache_key', 'ID needs to be set before fetching a cache key.', '4.7.0' );
532 return false;
533 }
534 return self::generate_meta_cache_key( $this->get_id(), $this->cache_group );
535 }
536
537 /**
538 * Generate cache key from id and group.
539 *
540 * @since 4.7.0
541 *
542 * @param int|string $id Object ID.
543 * @param string $cache_group Group name use to store cache. Whole group cache can be invalidated in one go.
544 *
545 * @return string Meta cache key.
546 */
547 public static function generate_meta_cache_key( $id, $cache_group ) {
548 return WC_Cache_Helper::get_cache_prefix( $cache_group ) . WC_Cache_Helper::get_cache_prefix( 'object_' . $id ) . 'object_meta_' . $id;
549 }
550
551 /**
552 * Prime caches for raw meta data. This includes meta_id column as well, which is not included by default in WP meta data.
553 *
554 * @since 4.7.0
555 *
556 * @param array $raw_meta_data_collection Array of objects of { object_id => array( meta_row_1, meta_row_2, ... }.
557 * @param string $cache_group Name of cache group.
558 */
559 public static function prime_raw_meta_data_cache( $raw_meta_data_collection, $cache_group ) {
560 foreach ( $raw_meta_data_collection as $object_id => $raw_meta_data_array ) {
561 $cache_key = self::generate_meta_cache_key( $object_id, $cache_group );
562 wp_cache_set( $cache_key, $raw_meta_data_array, $cache_group );
563 }
564 }
565
566 /**
567 * Read Meta Data from the database. Ignore any internal properties.
568 * Uses it's own caches because get_metadata does not provide meta_ids.
569 *
570 * @since 2.6.0
571 * @param bool $force_read True to force a new DB read (and update cache).
572 */
573 public function read_meta_data( $force_read = false ) {
574 $this->meta_data = array();
575 $cache_loaded = false;
576
577 if ( ! $this->get_id() ) {
578 return;
579 }
580
581 if ( ! $this->data_store ) {
582 return;
583 }
584
585 if ( ! empty( $this->cache_group ) ) {
586 // Prefix by group allows invalidation by group until https://core.trac.wordpress.org/ticket/4476 is implemented.
587 $cache_key = $this->get_meta_cache_key();
588 }
589
590 if ( ! $force_read ) {
591 if ( ! empty( $this->cache_group ) ) {
592 $cached_meta = wp_cache_get( $cache_key, $this->cache_group );
593 $cache_loaded = ! empty( $cached_meta );
594 }
595 }
596
597 // We filter the raw meta data again when loading from cache, in case we cached in an earlier version where filter conditions were different.
598 $raw_meta_data = $cache_loaded ? $this->data_store->filter_raw_meta_data( $this, $cached_meta ) : $this->data_store->read_meta( $this );
599
600 if ( $raw_meta_data ) {
601 foreach ( $raw_meta_data as $meta ) {
602 $this->meta_data[] = new WC_Meta_Data(
603 array(
604 'id' => (int) $meta->meta_id,
605 'key' => $meta->meta_key,
606 'value' => maybe_unserialize( $meta->meta_value ),
607 )
608 );
609 }
610
611 if ( ! $cache_loaded && ! empty( $this->cache_group ) ) {
612 wp_cache_set( $cache_key, $raw_meta_data, $this->cache_group );
613 }
614 }
615 }
616
617 /**
618 * Update Meta Data in the database.
619 *
620 * @since 2.6.0
621 */
622 public function save_meta_data() {
623 if ( ! $this->data_store || is_null( $this->meta_data ) ) {
624 return;
625 }
626 foreach ( $this->meta_data as $array_key => $meta ) {
627 if ( is_null( $meta->value ) ) {
628 if ( ! empty( $meta->id ) ) {
629 $this->data_store->delete_meta( $this, $meta );
630 unset( $this->meta_data[ $array_key ] );
631 }
632 } elseif ( empty( $meta->id ) ) {
633 $meta->id = $this->data_store->add_meta( $this, $meta );
634 $meta->apply_changes();
635 } else {
636 if ( $meta->get_changes() ) {
637 $this->data_store->update_meta( $this, $meta );
638 $meta->apply_changes();
639 }
640 }
641 }
642 if ( ! empty( $this->cache_group ) ) {
643 $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();
644 wp_cache_delete( $cache_key, $this->cache_group );
645 }
646 }
647
648 /**
649 * Set ID.
650 *
651 * @since 3.0.0
652 * @param int $id ID.
653 */
654 public function set_id( $id ) {
655 $this->id = absint( $id );
656 }
657
658 /**
659 * Set all props to default values.
660 *
661 * @since 3.0.0
662 */
663 public function set_defaults() {
664 $this->data = $this->default_data;
665 $this->changes = array();
666 $this->set_object_read( false );
667 }
668
669 /**
670 * Set object read property.
671 *
672 * @since 3.0.0
673 * @param boolean $read Should read?.
674 */
675 public function set_object_read( $read = true ) {
676 $this->object_read = (bool) $read;
677 }
678
679 /**
680 * Get object read property.
681 *
682 * @since 3.0.0
683 * @return boolean
684 */
685 public function get_object_read() {
686 return (bool) $this->object_read;
687 }
688
689 /**
690 * Set a collection of props in one go, collect any errors, and return the result.
691 * Only sets using public methods.
692 *
693 * @since 3.0.0
694 *
695 * @param array $props Key value pairs to set. Key is the prop and should map to a setter function name.
696 * @param string $context In what context to run this.
697 *
698 * @return bool|WP_Error
699 */
700 public function set_props( $props, $context = 'set' ) {
701 $errors = false;
702
703 foreach ( $props as $prop => $value ) {
704 try {
705 /**
706 * Checks if the prop being set is allowed, and the value is not null.
707 */
708 if ( is_null( $value ) || in_array( $prop, array( 'prop', 'date_prop', 'meta_data' ), true ) ) {
709 continue;
710 }
711 $setter = "set_$prop";
712
713 if ( is_callable( array( $this, $setter ) ) ) {
714 $this->{$setter}( $value );
715 }
716 } catch ( WC_Data_Exception $e ) {
717 if ( ! $errors ) {
718 $errors = new WP_Error();
719 }
720 $errors->add( $e->getErrorCode(), $e->getMessage() );
721 }
722 }
723
724 return $errors && count( $errors->get_error_codes() ) ? $errors : true;
725 }
726
727 /**
728 * Sets a prop for a setter method.
729 *
730 * This stores changes in a special array so we can track what needs saving
731 * the the DB later.
732 *
733 * @since 3.0.0
734 * @param string $prop Name of prop to set.
735 * @param mixed $value Value of the prop.
736 */
737 protected function set_prop( $prop, $value ) {
738 if ( array_key_exists( $prop, $this->data ) ) {
739 if ( true === $this->object_read ) {
740 if ( $value !== $this->data[ $prop ] || array_key_exists( $prop, $this->changes ) ) {
741 $this->changes[ $prop ] = $value;
742 }
743 } else {
744 $this->data[ $prop ] = $value;
745 }
746 }
747 }
748
749 /**
750 * Return data changes only.
751 *
752 * @since 3.0.0
753 * @return array
754 */
755 public function get_changes() {
756 return $this->changes;
757 }
758
759 /**
760 * Merge changes with data and clear.
761 *
762 * @since 3.0.0
763 */
764 public function apply_changes() {
765 $this->data = array_replace_recursive( $this->data, $this->changes ); // @codingStandardsIgnoreLine
766 $this->changes = array();
767 }
768
769 /**
770 * Prefix for action and filter hooks on data.
771 *
772 * @since 3.0.0
773 * @return string
774 */
775 protected function get_hook_prefix() {
776 return 'woocommerce_' . $this->object_type . '_get_';
777 }
778
779 /**
780 * Gets a prop for a getter method.
781 *
782 * Gets the value from either current pending changes, or the data itself.
783 * Context controls what happens to the value before it's returned.
784 *
785 * @since 3.0.0
786 * @param string $prop Name of prop to get.
787 * @param string $context What the value is for. Valid values are view and edit.
788 * @return mixed
789 */
790 protected function get_prop( $prop, $context = 'view' ) {
791 $value = null;
792
793 if ( array_key_exists( $prop, $this->data ) ) {
794 $value = array_key_exists( $prop, $this->changes ) ? $this->changes[ $prop ] : $this->data[ $prop ];
795
796 if ( 'view' === $context ) {
797 $value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this );
798 }
799 }
800
801 return $value;
802 }
803
804 /**
805 * Sets a date prop whilst handling formatting and datetime objects.
806 *
807 * @since 3.0.0
808 * @param string $prop Name of prop to set.
809 * @param string|integer $value Value of the prop.
810 */
811 protected function set_date_prop( $prop, $value ) {
812 try {
813 if ( empty( $value ) ) {
814 $this->set_prop( $prop, null );
815 return;
816 }
817
818 if ( is_a( $value, 'WC_DateTime' ) ) {
819 $datetime = $value;
820 } elseif ( is_numeric( $value ) ) {
821 // Timestamps are handled as UTC timestamps in all cases.
822 $datetime = new WC_DateTime( "@{$value}", new DateTimeZone( 'UTC' ) );
823 } else {
824 // Strings are defined in local WP timezone. Convert to UTC.
825 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 ) ) {
826 $offset = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset();
827 $timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
828 } else {
829 $timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $value ) ) ) );
830 }
831 $datetime = new WC_DateTime( "@{$timestamp}", new DateTimeZone( 'UTC' ) );
832 }
833
834 // Set local timezone or offset.
835 if ( get_option( 'timezone_string' ) ) {
836 $datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
837 } else {
838 $datetime->set_utc_offset( wc_timezone_offset() );
839 }
840
841 $this->set_prop( $prop, $datetime );
842 } catch ( Exception $e ) {} // @codingStandardsIgnoreLine.
843 }
844
845 /**
846 * When invalid data is found, throw an exception unless reading from the DB.
847 *
848 * @throws WC_Data_Exception Data Exception.
849 * @since 3.0.0
850 * @param string $code Error code.
851 * @param string $message Error message.
852 * @param int $http_status_code HTTP status code.
853 * @param array $data Extra error data.
854 */
855 protected function error( $code, $message, $http_status_code = 400, $data = array() ) {
856 throw new WC_Data_Exception( $code, $message, $http_status_code, $data );
857 }
858 }
859