Passed
Pull Request — master (#371)
by Brian
101:52
created

GetPaid_Data::update_meta_data()   B

Complexity

Conditions 10
Paths 17

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 24
c 1
b 0
f 0
nc 17
nop 3
dl 0
loc 40
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Abstract Data.
5
 *
6
 * Handles generic data interaction which is implemented by
7
 * the different data store classes.
8
 *
9
 */
10
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
/**
16
 * Abstract GetPaid Data Class
17
 *
18
 * Implemented by classes using the same CRUD(s) pattern.
19
 *
20
 * @version  1.0.19
21
 */
22
abstract class GetPaid_Data {
23
24
	/**
25
	 * ID for this object.
26
	 *
27
	 * @since 1.0.19
28
	 * @var int
29
	 */
30
	protected $id = 0;
31
32
	/**
33
	 * Core data for this object. Name value pairs (name + default value).
34
	 *
35
	 * @since 1.0.19
36
	 * @var array
37
	 */
38
	protected $data = array();
39
40
	/**
41
	 * Core data changes for this object.
42
	 *
43
	 * @since 1.0.19
44
	 * @var array
45
	 */
46
	protected $changes = array();
47
48
	/**
49
	 * This is false until the object is read from the DB.
50
	 *
51
	 * @since 1.0.19
52
	 * @var bool
53
	 */
54
	protected $object_read = false;
55
56
	/**
57
	 * This is the name of this object type.
58
	 *
59
	 * @since 1.0.19
60
	 * @var string
61
	 */
62
	protected $object_type = 'data';
63
64
	/**
65
	 * Extra data for this object. Name value pairs (name + default value).
66
	 * Used as a standard way for sub classes (like item types) to add
67
	 * additional information to an inherited class.
68
	 *
69
	 * @since 1.0.19
70
	 * @var array
71
	 */
72
	protected $extra_data = array();
73
74
	/**
75
	 * Set to _data on construct so we can track and reset data if needed.
76
	 *
77
	 * @since 1.0.19
78
	 * @var array
79
	 */
80
	protected $default_data = array();
81
82
	/**
83
	 * Contains a reference to the data store for this class.
84
	 *
85
	 * @since 1.0.19
86
	 * @var GetPaid_Data_Store
87
	 */
88
	protected $data_store;
89
90
	/**
91
	 * Stores meta in cache for future reads.
92
	 * A group must be set to to enable caching.
93
	 *
94
	 * @since 1.0.19
95
	 * @var string
96
	 */
97
	protected $cache_group = '';
98
99
	/**
100
	 * Stores the last error.
101
	 *
102
	 * @since 1.0.19
103
	 * @var string
104
	 */
105
	public $last_error = '';
106
107
	/**
108
	 * Stores additional meta data.
109
	 *
110
	 * @since 1.0.19
111
	 * @var array
112
	 */
113
	protected $meta_data = null;
114
115
	/**
116
	 * Default constructor.
117
	 *
118
	 * @param int|object|array $read ID to load from the DB (optional) or already queried data.
119
	 */
120
	public function __construct( $read = 0 ) {
121
		$this->data         = array_merge( $this->data, $this->extra_data );
122
		$this->default_data = $this->data;
123
	}
124
125
	/**
126
	 * Only store the object ID to avoid serializing the data object instance.
127
	 *
128
	 * @return array
129
	 */
130
	public function __sleep() {
131
		return array( 'id' );
132
	}
133
134
	/**
135
	 * Re-run the constructor with the object ID.
136
	 *
137
	 * If the object no longer exists, remove the ID.
138
	 */
139
	public function __wakeup() {
140
		try {
141
			$this->__construct( absint( $this->id ) );
142
		} catch ( Exception $e ) {
143
			$this->set_id( 0 );
144
			$this->set_object_read( true );
145
		}
146
	}
147
148
	/**
149
	 * When the object is cloned, make sure meta is duplicated correctly.
150
	 *
151
	 * @since 1.0.19
152
	 */
153
	public function __clone() {
154
		$this->maybe_read_meta_data();
155
		if ( ! empty( $this->meta_data ) ) {
156
			foreach ( $this->meta_data as $array_key => $meta ) {
157
				$this->meta_data[ $array_key ] = clone $meta;
158
				if ( ! empty( $meta->id ) ) {
159
					$this->meta_data[ $array_key ]->id = null;
160
				}
161
			}
162
		}
163
	}
164
165
	/**
166
	 * Get the data store.
167
	 *
168
	 * @since  1.0.19
169
	 * @return object
170
	 */
171
	public function get_data_store() {
172
		return $this->data_store;
173
	}
174
175
	/**
176
	 * Returns the unique ID for this object.
177
	 *
178
	 * @since  1.0.19
179
	 * @return int
180
	 */
181
	public function get_id() {
182
		return $this->id;
183
	}
184
185
	/**
186
	 * Delete an object, set the ID to 0, and return result.
187
	 *
188
	 * @since  1.0.19
189
	 * @param  bool $force_delete Should the data be deleted permanently.
190
	 * @return bool result
191
	 */
192
	public function delete( $force_delete = false ) {
193
		if ( $this->data_store ) {
194
			$this->data_store->delete( $this, array( 'force_delete' => $force_delete ) );
195
			$this->set_id( 0 );
196
			return true;
197
		}
198
		return false;
199
	}
200
201
	/**
202
	 * Save should create or update based on object existence.
203
	 *
204
	 * @since  1.0.19
205
	 * @return int
206
	 */
207
	public function save() {
208
		if ( ! $this->data_store ) {
209
			return $this->get_id();
210
		}
211
212
		/**
213
		 * Trigger action before saving to the DB. Allows you to adjust object props before save.
214
		 *
215
		 * @param GetPaid_Data          $this The object being saved.
216
		 * @param GetPaid_Data_Store_WP $data_store The data store persisting the data.
217
		 */
218
		do_action( 'getpaid_before_' . $this->object_type . '_object_save', $this, $this->data_store );
219
220
		if ( $this->get_id() ) {
221
			$this->data_store->update( $this );
222
		} else {
223
			$this->data_store->create( $this );
224
		}
225
226
		/**
227
		 * Trigger action after saving to the DB.
228
		 *
229
		 * @param GetPaid_Data          $this The object being saved.
230
		 * @param GetPaid_Data_Store_WP $data_store The data store persisting the data.
231
		 */
232
		do_action( 'getpaid_after_' . $this->object_type . '_object_save', $this, $this->data_store );
233
234
		return $this->get_id();
235
	}
236
237
	/**
238
	 * Change data to JSON format.
239
	 *
240
	 * @since  1.0.19
241
	 * @return string Data in JSON format.
242
	 */
243
	public function __toString() {
244
		return wp_json_encode( $this->get_data() );
0 ignored issues
show
Bug Best Practice introduced by
The expression return wp_json_encode($this->get_data()) could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
245
	}
246
247
	/**
248
	 * Returns all data for this object.
249
	 *
250
	 * @since  1.0.19
251
	 * @return array
252
	 */
253
	public function get_data() {
254
		return array_merge( array( 'id' => $this->get_id() ), $this->data, array( 'meta_data' => $this->get_meta_data() ) );
255
	}
256
257
	/**
258
	 * Returns array of expected data keys for this object.
259
	 *
260
	 * @since   1.0.19
261
	 * @return array
262
	 */
263
	public function get_data_keys() {
264
		return array_keys( $this->data );
265
	}
266
267
	/**
268
	 * Returns all "extra" data keys for an object (for sub objects like item types).
269
	 *
270
	 * @since  1.0.19
271
	 * @return array
272
	 */
273
	public function get_extra_data_keys() {
274
		return array_keys( $this->extra_data );
275
	}
276
277
	/**
278
	 * Filter null meta values from array.
279
	 *
280
	 * @since  1.0.19
281
	 * @param mixed $meta Meta value to check.
282
	 * @return bool
283
	 */
284
	protected function filter_null_meta( $meta ) {
285
		return ! is_null( $meta->value );
286
	}
287
288
	/**
289
	 * Get All Meta Data.
290
	 *
291
	 * @since 1.0.19
292
	 * @return array of objects.
293
	 */
294
	public function get_meta_data() {
295
		$this->maybe_read_meta_data();
296
		return array_values( array_filter( $this->meta_data, array( $this, 'filter_null_meta' ) ) );
297
	}
298
299
	/**
300
	 * Check if the key is an internal one.
301
	 *
302
	 * @since  1.0.19
303
	 * @param  string $key Key to check.
304
	 * @return bool   true if it's an internal key, false otherwise
305
	 */
306
	protected function is_internal_meta_key( $key ) {
307
		$internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, $this->data_store->get_internal_meta_keys(), true );
0 ignored issues
show
Bug introduced by
It seems like $this->data_store->get_internal_meta_keys() can also be of type null; however, parameter $haystack of in_array() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

307
		$internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, /** @scrutinizer ignore-type */ $this->data_store->get_internal_meta_keys(), true );
Loading history...
Bug introduced by
The method get_internal_meta_keys() does not exist on GetPaid_Data_Store. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

307
		$internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, $this->data_store->/** @scrutinizer ignore-call */ get_internal_meta_keys(), true );
Loading history...
308
309
		if ( ! $internal_meta_key ) {
310
			return false;
311
		}
312
313
		$has_setter_or_getter = is_callable( array( $this, 'set_' . $key ) ) || is_callable( array( $this, 'get_' . $key ) );
314
315
		if ( ! $has_setter_or_getter ) {
316
			return false;
317
		}
318
319
		/* translators: %s: $key Key to check */
320
		getpaid_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.', 'getpaid' ), $key ), '1.0.19' );
321
322
		return true;
323
	}
324
325
	/**
326
	 * Get Meta Data by Key.
327
	 *
328
	 * @since  1.0.19
329
	 * @param  string $key Meta Key.
330
	 * @param  bool   $single return first found meta with key, or all with $key.
331
	 * @param  string $context What the value is for. Valid values are view and edit.
332
	 * @return mixed
333
	 */
334
	public function get_meta( $key = '', $single = true, $context = 'view' ) {
335
336
		// Check if this is an internal meta key.
337
		if ( $this->is_internal_meta_key( $key ) ) {
338
			$function = 'get_' . $key;
339
340
			if ( is_callable( array( $this, $function ) ) ) {
341
				return $this->{$function}();
342
			}
343
		}
344
345
		// Read the meta data if not yet read.
346
		$this->maybe_read_meta_data();
347
		$meta_data  = $this->get_meta_data();
348
		$array_keys = array_keys( wp_list_pluck( $meta_data, 'key' ), $key, true );
349
		$value      = $single ? '' : array();
350
351
		if ( ! empty( $array_keys ) ) {
352
			// 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()).
353
			if ( $single ) {
354
				$value = $meta_data[ current( $array_keys ) ]->value;
355
			} else {
356
				$value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
357
			}
358
		}
359
360
		if ( 'view' === $context ) {
361
			$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
362
		}
363
364
		return $value;
365
	}
366
367
	/**
368
	 * See if meta data exists, since get_meta always returns a '' or array().
369
	 *
370
	 * @since  1.0.19
371
	 * @param  string $key Meta Key.
372
	 * @return boolean
373
	 */
374
	public function meta_exists( $key = '' ) {
375
		$this->maybe_read_meta_data();
376
		$array_keys = wp_list_pluck( $this->get_meta_data(), 'key' );
377
		return in_array( $key, $array_keys, true );
378
	}
379
380
	/**
381
	 * Set all meta data from array.
382
	 *
383
	 * @since 1.0.19
384
	 * @param array $data Key/Value pairs.
385
	 */
386
	public function set_meta_data( $data ) {
387
		if ( ! empty( $data ) && is_array( $data ) ) {
388
			$this->maybe_read_meta_data();
389
			foreach ( $data as $meta ) {
390
				$meta = (array) $meta;
391
				if ( isset( $meta['key'], $meta['value'], $meta['id'] ) ) {
392
					$this->meta_data[] = new GetPaid_Meta_Data(
393
						array(
394
							'id'    => $meta['id'],
395
							'key'   => $meta['key'],
396
							'value' => $meta['value'],
397
						)
398
					);
399
				}
400
			}
401
		}
402
	}
403
404
	/**
405
	 * Add meta data.
406
	 *
407
	 * @since 1.0.19
408
	 *
409
	 * @param string       $key Meta key.
410
	 * @param string|array $value Meta value.
411
	 * @param bool         $unique Should this be a unique key?.
412
	 */
413
	public function add_meta_data( $key, $value, $unique = false ) {
414
		if ( $this->is_internal_meta_key( $key ) ) {
415
			$function = 'set_' . $key;
416
417
			if ( is_callable( array( $this, $function ) ) ) {
418
				return $this->{$function}( $value );
419
			}
420
		}
421
422
		$this->maybe_read_meta_data();
423
		if ( $unique ) {
424
			$this->delete_meta_data( $key );
425
		}
426
		$this->meta_data[] = new GetPaid_Meta_Data(
427
			array(
428
				'key'   => $key,
429
				'value' => $value,
430
			)
431
		);
432
	}
433
434
	/**
435
	 * Update meta data by key or ID, if provided.
436
	 *
437
	 * @since  1.0.19
438
	 *
439
	 * @param  string       $key Meta key.
440
	 * @param  string|array $value Meta value.
441
	 * @param  int          $meta_id Meta ID.
442
	 */
443
	public function update_meta_data( $key, $value, $meta_id = 0 ) {
444
		if ( $this->is_internal_meta_key( $key ) ) {
445
			$function = 'set_' . $key;
446
447
			if ( is_callable( array( $this, $function ) ) ) {
448
				return $this->{$function}( $value );
449
			}
450
		}
451
452
		$this->maybe_read_meta_data();
453
454
		$array_key = false;
455
456
		if ( $meta_id ) {
457
			$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), $meta_id, true );
458
			$array_key  = $array_keys ? current( $array_keys ) : false;
459
		} else {
460
			// Find matches by key.
461
			$matches = array();
462
			foreach ( $this->meta_data as $meta_data_array_key => $meta ) {
463
				if ( $meta->key === $key ) {
464
					$matches[] = $meta_data_array_key;
465
				}
466
			}
467
468
			if ( ! empty( $matches ) ) {
469
				// Set matches to null so only one key gets the new value.
470
				foreach ( $matches as $meta_data_array_key ) {
471
					$this->meta_data[ $meta_data_array_key ]->value = null;
472
				}
473
				$array_key = current( $matches );
474
			}
475
		}
476
477
		if ( false !== $array_key ) {
478
			$meta        = $this->meta_data[ $array_key ];
479
			$meta->key   = $key;
480
			$meta->value = $value;
481
		} else {
482
			$this->add_meta_data( $key, $value, true );
483
		}
484
	}
485
486
	/**
487
	 * Delete meta data.
488
	 *
489
	 * @since 1.0.19
490
	 * @param string $key Meta key.
491
	 */
492
	public function delete_meta_data( $key ) {
493
		$this->maybe_read_meta_data();
494
		$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'key' ), $key, true );
495
496
		if ( $array_keys ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $array_keys of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
497
			foreach ( $array_keys as $array_key ) {
498
				$this->meta_data[ $array_key ]->value = null;
499
			}
500
		}
501
	}
502
503
	/**
504
	 * Delete meta data.
505
	 *
506
	 * @since 1.0.19
507
	 * @param int $mid Meta ID.
508
	 */
509
	public function delete_meta_data_by_mid( $mid ) {
510
		$this->maybe_read_meta_data();
511
		$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), (int) $mid, true );
512
513
		if ( $array_keys ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $array_keys of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
514
			foreach ( $array_keys as $array_key ) {
515
				$this->meta_data[ $array_key ]->value = null;
516
			}
517
		}
518
	}
519
520
	/**
521
	 * Read meta data if null.
522
	 *
523
	 * @since 1.0.19
524
	 */
525
	protected function maybe_read_meta_data() {
526
		if ( is_null( $this->meta_data ) ) {
0 ignored issues
show
introduced by
The condition is_null($this->meta_data) is always false.
Loading history...
527
			$this->read_meta_data();
528
		}
529
	}
530
531
	/**
532
	 * Read Meta Data from the database. Ignore any internal properties.
533
	 * Uses it's own caches because get_metadata does not provide meta_ids.
534
	 *
535
	 * @since 1.0.19
536
	 * @param bool $force_read True to force a new DB read (and update cache).
537
	 */
538
	public function read_meta_data( $force_read = false ) {
539
		$this->meta_data = array();
540
		$cache_loaded    = false;
541
542
		if ( ! $this->get_id() ) {
543
			return;
544
		}
545
546
		if ( ! $this->data_store ) {
547
			return;
548
		}
549
550
		if ( ! empty( $this->cache_group ) ) {
551
			$cache_key = GetPaid_Cache_Helper::get_cache_prefix( $this->cache_group ) . GetPaid_Cache_Helper::get_cache_prefix( 'object_' . $this->get_id() ) . 'object_meta_' . $this->get_id();
552
		}
553
554
		if ( ! $force_read ) {
555
			if ( ! empty( $this->cache_group ) ) {
556
				$cached_meta  = wp_cache_get( $cache_key, $this->cache_group );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cache_key does not seem to be defined for all execution paths leading up to this point.
Loading history...
557
				$cache_loaded = ! empty( $cached_meta );
558
			}
559
		}
560
561
		$raw_meta_data = $cache_loaded ? $cached_meta : $this->data_store->read_meta( $this );
0 ignored issues
show
Bug introduced by
The method read_meta() does not exist on GetPaid_Data_Store. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

561
		$raw_meta_data = $cache_loaded ? $cached_meta : $this->data_store->/** @scrutinizer ignore-call */ read_meta( $this );
Loading history...
Comprehensibility Best Practice introduced by
The variable $cached_meta does not seem to be defined for all execution paths leading up to this point.
Loading history...
562
		if ( $raw_meta_data ) {
563
			foreach ( $raw_meta_data as $meta ) {
564
				$this->meta_data[] = new GetPaid_Meta_Data(
565
					array(
566
						'id'    => (int) $meta->meta_id,
567
						'key'   => $meta->meta_key,
568
						'value' => maybe_unserialize( $meta->meta_value ),
569
					)
570
				);
571
			}
572
573
			if ( ! $cache_loaded && ! empty( $this->cache_group ) ) {
574
				wp_cache_set( $cache_key, $raw_meta_data, $this->cache_group );
575
			}
576
		}
577
	}
578
579
	/**
580
	 * Update Meta Data in the database.
581
	 *
582
	 * @since 1.0.19
583
	 */
584
	public function save_meta_data() {
585
		if ( ! $this->data_store || is_null( $this->meta_data ) ) {
586
			return;
587
		}
588
		foreach ( $this->meta_data as $array_key => $meta ) {
589
			if ( is_null( $meta->value ) ) {
590
				if ( ! empty( $meta->id ) ) {
591
					$this->data_store->delete_meta( $this, $meta );
0 ignored issues
show
Bug introduced by
The method delete_meta() does not exist on GetPaid_Data_Store. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

591
					$this->data_store->/** @scrutinizer ignore-call */ 
592
                        delete_meta( $this, $meta );
Loading history...
592
					unset( $this->meta_data[ $array_key ] );
593
				}
594
			} elseif ( empty( $meta->id ) ) {
595
				$meta->id = $this->data_store->add_meta( $this, $meta );
0 ignored issues
show
Bug introduced by
The method add_meta() does not exist on GetPaid_Data_Store. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

595
				/** @scrutinizer ignore-call */ 
596
    $meta->id = $this->data_store->add_meta( $this, $meta );
Loading history...
596
				$meta->apply_changes();
597
			} else {
598
				if ( $meta->get_changes() ) {
599
					$this->data_store->update_meta( $this, $meta );
0 ignored issues
show
Bug introduced by
The method update_meta() does not exist on GetPaid_Data_Store. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

599
					$this->data_store->/** @scrutinizer ignore-call */ 
600
                        update_meta( $this, $meta );
Loading history...
600
					$meta->apply_changes();
601
				}
602
			}
603
		}
604
		if ( ! empty( $this->cache_group ) ) {
605
			$cache_key = GetPaid_Cache_Helper::get_cache_prefix( $this->cache_group ) . GetPaid_Cache_Helper::get_cache_prefix( 'object_' . $this->get_id() ) . 'object_meta_' . $this->get_id();
606
			wp_cache_delete( $cache_key, $this->cache_group );
607
		}
608
	}
609
610
	/**
611
	 * Set ID.
612
	 *
613
	 * @since 1.0.19
614
	 * @param int $id ID.
615
	 */
616
	public function set_id( $id ) {
617
		$this->id = absint( $id );
618
	}
619
620
	/**
621
	 * Set all props to default values.
622
	 *
623
	 * @since 1.0.19
624
	 */
625
	public function set_defaults() {
626
		$this->data    = $this->default_data;
627
		$this->changes = array();
628
		$this->set_object_read( false );
629
	}
630
631
	/**
632
	 * Set object read property.
633
	 *
634
	 * @since 1.0.19
635
	 * @param boolean $read Should read?.
636
	 */
637
	public function set_object_read( $read = true ) {
638
		$this->object_read = (bool) $read;
639
	}
640
641
	/**
642
	 * Get object read property.
643
	 *
644
	 * @since  1.0.19
645
	 * @return boolean
646
	 */
647
	public function get_object_read() {
648
		return (bool) $this->object_read;
649
	}
650
651
	/**
652
	 * Set a collection of props in one go, collect any errors, and return the result.
653
	 * Only sets using public methods.
654
	 *
655
	 * @since  1.0.19
656
	 *
657
	 * @param array  $props Key value pairs to set. Key is the prop and should map to a setter function name.
658
	 * @param string $context In what context to run this.
659
	 *
660
	 * @return bool|WP_Error
661
	 */
662
	public function set_props( $props, $context = 'set' ) {
663
		$errors = false;
664
665
		foreach ( $props as $prop => $value ) {
666
			try {
667
				/**
668
				 * Checks if the prop being set is allowed, and the value is not null.
669
				 */
670
				if ( is_null( $value ) || in_array( $prop, array( 'prop', 'date_prop', 'meta_data' ), true ) ) {
671
					continue;
672
				}
673
				$setter = "set_$prop";
674
675
				if ( is_callable( array( $this, $setter ) ) ) {
676
					$this->{$setter}( $value );
677
				}
678
			} catch ( Exception $e ) {
679
				if ( ! $errors ) {
680
					$errors = new WP_Error();
681
				}
682
				$errors->add( $e->getCode(), $e->getMessage() );
683
				$this->last_error = $e->getMessage();
684
			}
685
		}
686
687
		return $errors && count( $errors->get_error_codes() ) ? $errors : true;
688
	}
689
690
	/**
691
	 * Sets a prop for a setter method.
692
	 *
693
	 * This stores changes in a special array so we can track what needs saving
694
	 * the the DB later.
695
	 *
696
	 * @since 1.0.19
697
	 * @param string $prop Name of prop to set.
698
	 * @param mixed  $value Value of the prop.
699
	 */
700
	protected function set_prop( $prop, $value ) {
701
		if ( array_key_exists( $prop, $this->data ) ) {
702
			if ( true === $this->object_read ) {
703
				if ( $value !== $this->data[ $prop ] || array_key_exists( $prop, $this->changes ) ) {
704
					$this->changes[ $prop ] = $value;
705
				}
706
			} else {
707
				$this->data[ $prop ] = $value;
708
			}
709
		}
710
	}
711
712
	/**
713
	 * Return data changes only.
714
	 *
715
	 * @since 1.0.19
716
	 * @return array
717
	 */
718
	public function get_changes() {
719
		return $this->changes;
720
	}
721
722
	/**
723
	 * Merge changes with data and clear.
724
	 *
725
	 * @since 1.0.19
726
	 */
727
	public function apply_changes() {
728
		$this->data    = array_replace_recursive( $this->data, $this->changes );
729
		$this->changes = array();
730
	}
731
732
	/**
733
	 * Prefix for action and filter hooks on data.
734
	 *
735
	 * @since  1.0.19
736
	 * @return string
737
	 */
738
	protected function get_hook_prefix() {
739
		return 'wpinv_get_' . $this->object_type . '_';
740
	}
741
742
	/**
743
	 * Gets a prop for a getter method.
744
	 *
745
	 * Gets the value from either current pending changes, or the data itself.
746
	 * Context controls what happens to the value before it's returned.
747
	 *
748
	 * @since  1.0.19
749
	 * @param  string $prop Name of prop to get.
750
	 * @param  string $context What the value is for. Valid values are view and edit.
751
	 * @return mixed
752
	 */
753
	protected function get_prop( $prop, $context = 'view' ) {
754
		$value = null;
755
756
		if ( array_key_exists( $prop, $this->data ) ) {
757
			$value = array_key_exists( $prop, $this->changes ) ? $this->changes[ $prop ] : $this->data[ $prop ];
758
759
			if ( 'view' === $context ) {
760
				$value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this );
761
			}
762
		}
763
764
		return $value;
765
	}
766
767
	/**
768
	 * Sets a date prop whilst handling formatting and datetime objects.
769
	 *
770
	 * @since 1.0.19
771
	 * @param string         $prop Name of prop to set.
772
	 * @param string|integer $value Value of the prop.
773
	 */
774
	protected function set_date_prop( $prop, $value ) {
775
776
		if ( empty( $value ) ) {
777
			$this->set_prop( $prop, null );
778
			return;
779
		}
780
		$this->set_prop( $prop, $value );
781
782
	}
783
784
	/**
785
	 * When invalid data is found, throw an exception unless reading from the DB.
786
	 *
787
	 * @throws Exception Data Exception.
788
	 * @since 1.0.19
789
	 * @param string $code             Error code.
790
	 * @param string $message          Error message.
791
	 */
792
	protected function error( $code, $message ) {
793
		throw new Exception( $message, $code );
0 ignored issues
show
Bug introduced by
$code of type string is incompatible with the type integer expected by parameter $code of Exception::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

793
		throw new Exception( $message, /** @scrutinizer ignore-type */ $code );
Loading history...
794
	}
795
}
796