PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 3.8.3
WooCommerce v3.8.3
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 6 years ago abstract-wc-deprecated-hooks.php 8 years ago abstract-wc-integration.php 8 years ago abstract-wc-log-handler.php 8 years ago abstract-wc-object-query.php 8 years ago abstract-wc-order.php 6 years ago abstract-wc-payment-gateway.php 6 years ago abstract-wc-payment-token.php 8 years ago abstract-wc-privacy.php 7 years ago abstract-wc-product.php 6 years ago abstract-wc-session.php 8 years ago abstract-wc-settings-api.php 7 years ago abstract-wc-shipping-method.php 7 years ago abstract-wc-widget.php 7 years ago class-wc-background-process.php 7 years ago
abstract-wc-data.php
813 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 if ( 'view' === $context ) {
351 $value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
352 }
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 * Read Meta Data from the database. Ignore any internal properties.
524 * Uses it's own caches because get_metadata does not provide meta_ids.
525 *
526 * @since 2.6.0
527 * @param bool $force_read True to force a new DB read (and update cache).
528 */
529 public function read_meta_data( $force_read = false ) {
530 $this->meta_data = array();
531 $cache_loaded = false;
532
533 if ( ! $this->get_id() ) {
534 return;
535 }
536
537 if ( ! $this->data_store ) {
538 return;
539 }
540
541 if ( ! empty( $this->cache_group ) ) {
542 // Prefix by group allows invalidation by group until https://core.trac.wordpress.org/ticket/4476 is implemented.
543 $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();
544 }
545
546 if ( ! $force_read ) {
547 if ( ! empty( $this->cache_group ) ) {
548 $cached_meta = wp_cache_get( $cache_key, $this->cache_group );
549 $cache_loaded = ! empty( $cached_meta );
550 }
551 }
552
553 $raw_meta_data = $cache_loaded ? $cached_meta : $this->data_store->read_meta( $this );
554 if ( $raw_meta_data ) {
555 foreach ( $raw_meta_data as $meta ) {
556 $this->meta_data[] = new WC_Meta_Data(
557 array(
558 'id' => (int) $meta->meta_id,
559 'key' => $meta->meta_key,
560 'value' => maybe_unserialize( $meta->meta_value ),
561 )
562 );
563 }
564
565 if ( ! $cache_loaded && ! empty( $this->cache_group ) ) {
566 wp_cache_set( $cache_key, $raw_meta_data, $this->cache_group );
567 }
568 }
569 }
570
571 /**
572 * Update Meta Data in the database.
573 *
574 * @since 2.6.0
575 */
576 public function save_meta_data() {
577 if ( ! $this->data_store || is_null( $this->meta_data ) ) {
578 return;
579 }
580 foreach ( $this->meta_data as $array_key => $meta ) {
581 if ( is_null( $meta->value ) ) {
582 if ( ! empty( $meta->id ) ) {
583 $this->data_store->delete_meta( $this, $meta );
584 unset( $this->meta_data[ $array_key ] );
585 }
586 } elseif ( empty( $meta->id ) ) {
587 $meta->id = $this->data_store->add_meta( $this, $meta );
588 $meta->apply_changes();
589 } else {
590 if ( $meta->get_changes() ) {
591 $this->data_store->update_meta( $this, $meta );
592 $meta->apply_changes();
593 }
594 }
595 }
596 if ( ! empty( $this->cache_group ) ) {
597 $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();
598 wp_cache_delete( $cache_key, $this->cache_group );
599 }
600 }
601
602 /**
603 * Set ID.
604 *
605 * @since 3.0.0
606 * @param int $id ID.
607 */
608 public function set_id( $id ) {
609 $this->id = absint( $id );
610 }
611
612 /**
613 * Set all props to default values.
614 *
615 * @since 3.0.0
616 */
617 public function set_defaults() {
618 $this->data = $this->default_data;
619 $this->changes = array();
620 $this->set_object_read( false );
621 }
622
623 /**
624 * Set object read property.
625 *
626 * @since 3.0.0
627 * @param boolean $read Should read?.
628 */
629 public function set_object_read( $read = true ) {
630 $this->object_read = (bool) $read;
631 }
632
633 /**
634 * Get object read property.
635 *
636 * @since 3.0.0
637 * @return boolean
638 */
639 public function get_object_read() {
640 return (bool) $this->object_read;
641 }
642
643 /**
644 * Set a collection of props in one go, collect any errors, and return the result.
645 * Only sets using public methods.
646 *
647 * @since 3.0.0
648 *
649 * @param array $props Key value pairs to set. Key is the prop and should map to a setter function name.
650 * @param string $context In what context to run this.
651 *
652 * @return bool|WP_Error
653 */
654 public function set_props( $props, $context = 'set' ) {
655 $errors = false;
656
657 foreach ( $props as $prop => $value ) {
658 try {
659 /**
660 * Checks if the prop being set is allowed, and the value is not null.
661 */
662 if ( is_null( $value ) || in_array( $prop, array( 'prop', 'date_prop', 'meta_data' ), true ) ) {
663 continue;
664 }
665 $setter = "set_$prop";
666
667 if ( is_callable( array( $this, $setter ) ) ) {
668 $this->{$setter}( $value );
669 }
670 } catch ( WC_Data_Exception $e ) {
671 if ( ! $errors ) {
672 $errors = new WP_Error();
673 }
674 $errors->add( $e->getErrorCode(), $e->getMessage() );
675 }
676 }
677
678 return $errors && count( $errors->get_error_codes() ) ? $errors : true;
679 }
680
681 /**
682 * Sets a prop for a setter method.
683 *
684 * This stores changes in a special array so we can track what needs saving
685 * the the DB later.
686 *
687 * @since 3.0.0
688 * @param string $prop Name of prop to set.
689 * @param mixed $value Value of the prop.
690 */
691 protected function set_prop( $prop, $value ) {
692 if ( array_key_exists( $prop, $this->data ) ) {
693 if ( true === $this->object_read ) {
694 if ( $value !== $this->data[ $prop ] || array_key_exists( $prop, $this->changes ) ) {
695 $this->changes[ $prop ] = $value;
696 }
697 } else {
698 $this->data[ $prop ] = $value;
699 }
700 }
701 }
702
703 /**
704 * Return data changes only.
705 *
706 * @since 3.0.0
707 * @return array
708 */
709 public function get_changes() {
710 return $this->changes;
711 }
712
713 /**
714 * Merge changes with data and clear.
715 *
716 * @since 3.0.0
717 */
718 public function apply_changes() {
719 $this->data = array_replace_recursive( $this->data, $this->changes ); // @codingStandardsIgnoreLine
720 $this->changes = array();
721 }
722
723 /**
724 * Prefix for action and filter hooks on data.
725 *
726 * @since 3.0.0
727 * @return string
728 */
729 protected function get_hook_prefix() {
730 return 'woocommerce_' . $this->object_type . '_get_';
731 }
732
733 /**
734 * Gets a prop for a getter method.
735 *
736 * Gets the value from either current pending changes, or the data itself.
737 * Context controls what happens to the value before it's returned.
738 *
739 * @since 3.0.0
740 * @param string $prop Name of prop to get.
741 * @param string $context What the value is for. Valid values are view and edit.
742 * @return mixed
743 */
744 protected function get_prop( $prop, $context = 'view' ) {
745 $value = null;
746
747 if ( array_key_exists( $prop, $this->data ) ) {
748 $value = array_key_exists( $prop, $this->changes ) ? $this->changes[ $prop ] : $this->data[ $prop ];
749
750 if ( 'view' === $context ) {
751 $value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this );
752 }
753 }
754
755 return $value;
756 }
757
758 /**
759 * Sets a date prop whilst handling formatting and datetime objects.
760 *
761 * @since 3.0.0
762 * @param string $prop Name of prop to set.
763 * @param string|integer $value Value of the prop.
764 */
765 protected function set_date_prop( $prop, $value ) {
766 try {
767 if ( empty( $value ) ) {
768 $this->set_prop( $prop, null );
769 return;
770 }
771
772 if ( is_a( $value, 'WC_DateTime' ) ) {
773 $datetime = $value;
774 } elseif ( is_numeric( $value ) ) {
775 // Timestamps are handled as UTC timestamps in all cases.
776 $datetime = new WC_DateTime( "@{$value}", new DateTimeZone( 'UTC' ) );
777 } else {
778 // Strings are defined in local WP timezone. Convert to UTC.
779 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 ) ) {
780 $offset = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset();
781 $timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
782 } else {
783 $timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $value ) ) ) );
784 }
785 $datetime = new WC_DateTime( "@{$timestamp}", new DateTimeZone( 'UTC' ) );
786 }
787
788 // Set local timezone or offset.
789 if ( get_option( 'timezone_string' ) ) {
790 $datetime->setTimezone( new DateTimeZone( wc_timezone_string() ) );
791 } else {
792 $datetime->set_utc_offset( wc_timezone_offset() );
793 }
794
795 $this->set_prop( $prop, $datetime );
796 } catch ( Exception $e ) {} // @codingStandardsIgnoreLine.
797 }
798
799 /**
800 * When invalid data is found, throw an exception unless reading from the DB.
801 *
802 * @throws WC_Data_Exception Data Exception.
803 * @since 3.0.0
804 * @param string $code Error code.
805 * @param string $message Error message.
806 * @param int $http_status_code HTTP status code.
807 * @param array $data Extra error data.
808 */
809 protected function error( $code, $message, $http_status_code = 400, $data = array() ) {
810 throw new WC_Data_Exception( $code, $message, $http_status_code, $data );
811 }
812 }
813