Passed
Pull Request — master (#377)
by Brian
06:57
created

GetPaid_Data::error()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 2
rs 10
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
	 * Get the object type.
177
	 *
178
	 * @since  1.0.19
179
	 * @return string
180
	 */
181
	public function get_object_type() {
182
		return $this->object_type;
183
	}
184
185
	/**
186
	 * Returns the unique ID for this object.
187
	 *
188
	 * @since  1.0.19
189
	 * @return int
190
	 */
191
	public function get_id() {
192
		return $this->id;
193
	}
194
195
	/**
196
	 * Get form status.
197
	 *
198
	 * @since 1.0.19
199
	 * @param  string $context View or edit context.
200
	 * @return string
201
	 */
202
	public function get_status( $context = 'view' ) {
203
		return $this->get_prop( 'status', $context );
204
    }
205
206
	/**
207
	 * Delete an object, set the ID to 0, and return result.
208
	 *
209
	 * @since  1.0.19
210
	 * @param  bool $force_delete Should the data be deleted permanently.
211
	 * @return bool result
212
	 */
213
	public function delete( $force_delete = false ) {
214
		if ( $this->data_store ) {
215
			$this->data_store->delete( $this, array( 'force_delete' => $force_delete ) );
216
			$this->set_id( 0 );
217
			return true;
218
		}
219
		return false;
220
	}
221
222
	/**
223
	 * Save should create or update based on object existence.
224
	 *
225
	 * @since  1.0.19
226
	 * @return int
227
	 */
228
	public function save() {
229
		if ( ! $this->data_store ) {
230
			return $this->get_id();
231
		}
232
233
		/**
234
		 * Trigger action before saving to the DB. Allows you to adjust object props before save.
235
		 *
236
		 * @param GetPaid_Data          $this The object being saved.
237
		 * @param GetPaid_Data_Store_WP $data_store The data store persisting the data.
238
		 */
239
		do_action( 'getpaid_before_' . $this->object_type . '_object_save', $this, $this->data_store );
240
241
		if ( $this->get_id() ) {
242
			$this->data_store->update( $this );
243
		} else {
244
			$this->data_store->create( $this );
245
		}
246
247
		/**
248
		 * Trigger action after saving to the DB.
249
		 *
250
		 * @param GetPaid_Data          $this The object being saved.
251
		 * @param GetPaid_Data_Store_WP $data_store The data store persisting the data.
252
		 */
253
		do_action( 'getpaid_after_' . $this->object_type . '_object_save', $this, $this->data_store );
254
255
		return $this->get_id();
256
	}
257
258
	/**
259
	 * Change data to JSON format.
260
	 *
261
	 * @since  1.0.19
262
	 * @return string Data in JSON format.
263
	 */
264
	public function __toString() {
265
		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...
266
	}
267
268
	/**
269
	 * Returns all data for this object.
270
	 *
271
	 * @since  1.0.19
272
	 * @return array
273
	 */
274
	public function get_data() {
275
		return array_merge( array( 'id' => $this->get_id() ), $this->data, array( 'meta_data' => $this->get_meta_data() ) );
276
	}
277
278
	/**
279
	 * Returns array of expected data keys for this object.
280
	 *
281
	 * @since   1.0.19
282
	 * @return array
283
	 */
284
	public function get_data_keys() {
285
		return array_keys( $this->data );
286
	}
287
288
	/**
289
	 * Returns all "extra" data keys for an object (for sub objects like item types).
290
	 *
291
	 * @since  1.0.19
292
	 * @return array
293
	 */
294
	public function get_extra_data_keys() {
295
		return array_keys( $this->extra_data );
296
	}
297
298
	/**
299
	 * Filter null meta values from array.
300
	 *
301
	 * @since  1.0.19
302
	 * @param mixed $meta Meta value to check.
303
	 * @return bool
304
	 */
305
	protected function filter_null_meta( $meta ) {
306
		return ! is_null( $meta->value );
307
	}
308
309
	/**
310
	 * Get All Meta Data.
311
	 *
312
	 * @since 1.0.19
313
	 * @return array of objects.
314
	 */
315
	public function get_meta_data() {
316
		$this->maybe_read_meta_data();
317
		return array_values( array_filter( $this->meta_data, array( $this, 'filter_null_meta' ) ) );
318
	}
319
320
	/**
321
	 * Check if the key is an internal one.
322
	 *
323
	 * @since  1.0.19
324
	 * @param  string $key Key to check.
325
	 * @return bool   true if it's an internal key, false otherwise
326
	 */
327
	protected function is_internal_meta_key( $key ) {
328
		$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
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

328
		$internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, $this->data_store->/** @scrutinizer ignore-call */ get_internal_meta_keys(), true );
Loading history...
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

328
		$internal_meta_key = ! empty( $key ) && $this->data_store && in_array( $key, /** @scrutinizer ignore-type */ $this->data_store->get_internal_meta_keys(), true );
Loading history...
329
330
		if ( ! $internal_meta_key ) {
331
			return false;
332
		}
333
334
		$has_setter_or_getter = is_callable( array( $this, 'set_' . $key ) ) || is_callable( array( $this, 'get_' . $key ) );
335
336
		if ( ! $has_setter_or_getter ) {
337
			return false;
338
		}
339
340
		/* translators: %s: $key Key to check */
341
		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' );
342
343
		return true;
344
	}
345
346
	/**
347
	 * Magic method for setting data fields.
348
	 *
349
	 * This method does not update custom fields in the database.
350
	 *
351
	 * @since 1.0.19
352
	 * @access public
353
	 *
354
	 */
355
	public function __set( $key, $value ) {
356
357
		if ( 'id' == strtolower( $key ) ) {
358
			return $this->set_id( $value );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->set_id($value) targeting GetPaid_Data::set_id() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
359
		}
360
361
		if ( method_exists( $this, "set_$key") ) {
362
363
			/* translators: %s: $key Key to set */
364
			getpaid_doing_it_wrong( __FUNCTION__, sprintf( __( 'Object data such as "%s" should not be accessed directly. Use getters and setters.', 'getpaid' ), $key ), '1.0.19' );
365
366
			call_user_func( array( $this, "set_$key" ), $value );
367
		} else {
368
			$this->set_prop( $key, $value );
369
		}
370
371
	}
372
373
	/**
374
     * Margic method for retrieving a property.
375
     */
376
    public function __get( $key ) {
377
378
        // Check if we have a helper method for that.
379
        if ( method_exists( $this, 'get_' . $key ) ) {
380
381
			if ( 'post_type' != $key ) {
382
				/* translators: %s: $key Key to set */
383
				getpaid_doing_it_wrong( __FUNCTION__, sprintf( __( 'Object data such as "%s" should not be accessed directly. Use getters and setters.', 'getpaid' ), $key ), '1.0.19' );
384
			}
385
386
            return call_user_func( array( $this, 'get_' . $key ) );
387
        }
388
389
        // Check if the key is in the associated $post object.
390
        if ( ! empty( $this->post ) && isset( $this->post->$key ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property post does not exist on GetPaid_Data. Since you implemented __get, consider adding a @property annotation.
Loading history...
391
            return $this->post->$key;
392
        }
393
394
		return $this->get_prop( $key );
395
396
    }
397
398
	/**
399
	 * Get Meta Data by Key.
400
	 *
401
	 * @since  1.0.19
402
	 * @param  string $key Meta Key.
403
	 * @param  bool   $single return first found meta with key, or all with $key.
404
	 * @param  string $context What the value is for. Valid values are view and edit.
405
	 * @return mixed
406
	 */
407
	public function get_meta( $key = '', $single = true, $context = 'view' ) {
408
409
		// Check if this is an internal meta key.
410
		if ( $this->is_internal_meta_key( $key ) ) {
411
			$function = 'get_' . $key;
412
413
			if ( is_callable( array( $this, $function ) ) ) {
414
				return $this->{$function}();
415
			}
416
		}
417
418
		// Read the meta data if not yet read.
419
		$this->maybe_read_meta_data();
420
		$meta_data  = $this->get_meta_data();
421
		$array_keys = array_keys( wp_list_pluck( $meta_data, 'key' ), $key, true );
422
		$value      = $single ? '' : array();
423
424
		if ( ! empty( $array_keys ) ) {
425
			// 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()).
426
			if ( $single ) {
427
				$value = $meta_data[ current( $array_keys ) ]->value;
428
			} else {
429
				$value = array_intersect_key( $meta_data, array_flip( $array_keys ) );
430
			}
431
		}
432
433
		if ( 'view' === $context ) {
434
			$value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
435
		}
436
437
		return $value;
438
	}
439
440
	/**
441
	 * See if meta data exists, since get_meta always returns a '' or array().
442
	 *
443
	 * @since  1.0.19
444
	 * @param  string $key Meta Key.
445
	 * @return boolean
446
	 */
447
	public function meta_exists( $key = '' ) {
448
		$this->maybe_read_meta_data();
449
		$array_keys = wp_list_pluck( $this->get_meta_data(), 'key' );
450
		return in_array( $key, $array_keys, true );
451
	}
452
453
	/**
454
	 * Set all meta data from array.
455
	 *
456
	 * @since 1.0.19
457
	 * @param array $data Key/Value pairs.
458
	 */
459
	public function set_meta_data( $data ) {
460
		if ( ! empty( $data ) && is_array( $data ) ) {
461
			$this->maybe_read_meta_data();
462
			foreach ( $data as $meta ) {
463
				$meta = (array) $meta;
464
				if ( isset( $meta['key'], $meta['value'], $meta['id'] ) ) {
465
					$this->meta_data[] = new GetPaid_Meta_Data(
466
						array(
467
							'id'    => $meta['id'],
468
							'key'   => $meta['key'],
469
							'value' => $meta['value'],
470
						)
471
					);
472
				}
473
			}
474
		}
475
	}
476
477
	/**
478
	 * Add meta data.
479
	 *
480
	 * @since 1.0.19
481
	 *
482
	 * @param string       $key Meta key.
483
	 * @param string|array $value Meta value.
484
	 * @param bool         $unique Should this be a unique key?.
485
	 */
486
	public function add_meta_data( $key, $value, $unique = false ) {
487
		if ( $this->is_internal_meta_key( $key ) ) {
488
			$function = 'set_' . $key;
489
490
			if ( is_callable( array( $this, $function ) ) ) {
491
				return $this->{$function}( $value );
492
			}
493
		}
494
495
		$this->maybe_read_meta_data();
496
		if ( $unique ) {
497
			$this->delete_meta_data( $key );
498
		}
499
		$this->meta_data[] = new GetPaid_Meta_Data(
500
			array(
501
				'key'   => $key,
502
				'value' => $value,
503
			)
504
		);
505
	}
506
507
	/**
508
	 * Update meta data by key or ID, if provided.
509
	 *
510
	 * @since  1.0.19
511
	 *
512
	 * @param  string       $key Meta key.
513
	 * @param  string|array $value Meta value.
514
	 * @param  int          $meta_id Meta ID.
515
	 */
516
	public function update_meta_data( $key, $value, $meta_id = 0 ) {
517
		if ( $this->is_internal_meta_key( $key ) ) {
518
			$function = 'set_' . $key;
519
520
			if ( is_callable( array( $this, $function ) ) ) {
521
				return $this->{$function}( $value );
522
			}
523
		}
524
525
		$this->maybe_read_meta_data();
526
527
		$array_key = false;
528
529
		if ( $meta_id ) {
530
			$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), $meta_id, true );
531
			$array_key  = $array_keys ? current( $array_keys ) : false;
532
		} else {
533
			// Find matches by key.
534
			$matches = array();
535
			foreach ( $this->meta_data as $meta_data_array_key => $meta ) {
536
				if ( $meta->key === $key ) {
537
					$matches[] = $meta_data_array_key;
538
				}
539
			}
540
541
			if ( ! empty( $matches ) ) {
542
				// Set matches to null so only one key gets the new value.
543
				foreach ( $matches as $meta_data_array_key ) {
544
					$this->meta_data[ $meta_data_array_key ]->value = null;
545
				}
546
				$array_key = current( $matches );
547
			}
548
		}
549
550
		if ( false !== $array_key ) {
551
			$meta        = $this->meta_data[ $array_key ];
552
			$meta->key   = $key;
553
			$meta->value = $value;
554
		} else {
555
			$this->add_meta_data( $key, $value, true );
556
		}
557
	}
558
559
	/**
560
	 * Delete meta data.
561
	 *
562
	 * @since 1.0.19
563
	 * @param string $key Meta key.
564
	 */
565
	public function delete_meta_data( $key ) {
566
		$this->maybe_read_meta_data();
567
		$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'key' ), $key, true );
568
569
		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...
570
			foreach ( $array_keys as $array_key ) {
571
				$this->meta_data[ $array_key ]->value = null;
572
			}
573
		}
574
	}
575
576
	/**
577
	 * Delete meta data.
578
	 *
579
	 * @since 1.0.19
580
	 * @param int $mid Meta ID.
581
	 */
582
	public function delete_meta_data_by_mid( $mid ) {
583
		$this->maybe_read_meta_data();
584
		$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), (int) $mid, true );
585
586
		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...
587
			foreach ( $array_keys as $array_key ) {
588
				$this->meta_data[ $array_key ]->value = null;
589
			}
590
		}
591
	}
592
593
	/**
594
	 * Read meta data if null.
595
	 *
596
	 * @since 1.0.19
597
	 */
598
	protected function maybe_read_meta_data() {
599
		if ( is_null( $this->meta_data ) ) {
0 ignored issues
show
introduced by
The condition is_null($this->meta_data) is always false.
Loading history...
600
			$this->read_meta_data();
601
		}
602
	}
603
604
	/**
605
	 * Read Meta Data from the database. Ignore any internal properties.
606
	 * Uses it's own caches because get_metadata does not provide meta_ids.
607
	 *
608
	 * @since 1.0.19
609
	 * @param bool $force_read True to force a new DB read (and update cache).
610
	 */
611
	public function read_meta_data( $force_read = false ) {
612
		$this->meta_data = array();
613
		$cache_loaded    = false;
614
615
		if ( ! $this->get_id() ) {
616
			return;
617
		}
618
619
		if ( ! $this->data_store ) {
620
			return;
621
		}
622
623
		if ( ! empty( $this->cache_group ) ) {
624
			$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();
625
		}
626
627
		if ( ! $force_read ) {
628
			if ( ! empty( $this->cache_group ) ) {
629
				$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...
630
				$cache_loaded = ! empty( $cached_meta );
631
			}
632
		}
633
634
		$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

634
		$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...
635
		if ( $raw_meta_data ) {
636
			foreach ( $raw_meta_data as $meta ) {
637
				$this->meta_data[] = new GetPaid_Meta_Data(
638
					array(
639
						'id'    => (int) $meta->meta_id,
640
						'key'   => $meta->meta_key,
641
						'value' => maybe_unserialize( $meta->meta_value ),
642
					)
643
				);
644
			}
645
646
			if ( ! $cache_loaded && ! empty( $this->cache_group ) ) {
647
				wp_cache_set( $cache_key, $raw_meta_data, $this->cache_group );
648
			}
649
		}
650
	}
651
652
	/**
653
	 * Update Meta Data in the database.
654
	 *
655
	 * @since 1.0.19
656
	 */
657
	public function save_meta_data() {
658
		if ( ! $this->data_store || is_null( $this->meta_data ) ) {
659
			return;
660
		}
661
		foreach ( $this->meta_data as $array_key => $meta ) {
662
			if ( is_null( $meta->value ) ) {
663
				if ( ! empty( $meta->id ) ) {
664
					$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

664
					$this->data_store->/** @scrutinizer ignore-call */ 
665
                        delete_meta( $this, $meta );
Loading history...
665
					unset( $this->meta_data[ $array_key ] );
666
				}
667
			} elseif ( empty( $meta->id ) ) {
668
				$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

668
				/** @scrutinizer ignore-call */ 
669
    $meta->id = $this->data_store->add_meta( $this, $meta );
Loading history...
669
				$meta->apply_changes();
670
			} else {
671
				if ( $meta->get_changes() ) {
672
					$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

672
					$this->data_store->/** @scrutinizer ignore-call */ 
673
                        update_meta( $this, $meta );
Loading history...
673
					$meta->apply_changes();
674
				}
675
			}
676
		}
677
		if ( ! empty( $this->cache_group ) ) {
678
			$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();
679
			wp_cache_delete( $cache_key, $this->cache_group );
680
		}
681
	}
682
683
	/**
684
	 * Set ID.
685
	 *
686
	 * @since 1.0.19
687
	 * @param int $id ID.
688
	 */
689
	public function set_id( $id ) {
690
		$this->id = absint( $id );
691
	}
692
693
	/**
694
	 * Sets item status.
695
	 *
696
	 * @since 1.0.19
697
	 * @param string $status New status.
698
	 * @return array details of change.
699
	 */
700
	public function set_status( $status ) {
701
        $old_status = $this->get_status();
702
703
		$this->set_prop( 'status', $status );
704
705
		return array(
706
			'from' => $old_status,
707
			'to'   => $status,
708
		);
709
    }
710
711
	/**
712
	 * Set all props to default values.
713
	 *
714
	 * @since 1.0.19
715
	 */
716
	public function set_defaults() {
717
		$this->data    = $this->default_data;
718
		$this->changes = array();
719
		$this->set_object_read( false );
720
	}
721
722
	/**
723
	 * Set object read property.
724
	 *
725
	 * @since 1.0.19
726
	 * @param boolean $read Should read?.
727
	 */
728
	public function set_object_read( $read = true ) {
729
		$this->object_read = (bool) $read;
730
	}
731
732
	/**
733
	 * Get object read property.
734
	 *
735
	 * @since  1.0.19
736
	 * @return boolean
737
	 */
738
	public function get_object_read() {
739
		return (bool) $this->object_read;
740
	}
741
742
	/**
743
	 * Set a collection of props in one go, collect any errors, and return the result.
744
	 * Only sets using public methods.
745
	 *
746
	 * @since  1.0.19
747
	 *
748
	 * @param array  $props Key value pairs to set. Key is the prop and should map to a setter function name.
749
	 * @param string $context In what context to run this.
750
	 *
751
	 * @return bool|WP_Error
752
	 */
753
	public function set_props( $props, $context = 'set' ) {
754
		$errors = false;
755
756
		foreach ( $props as $prop => $value ) {
757
			try {
758
				/**
759
				 * Checks if the prop being set is allowed, and the value is not null.
760
				 */
761
				if ( is_null( $value ) || in_array( $prop, array( 'prop', 'date_prop', 'meta_data' ), true ) ) {
762
					continue;
763
				}
764
				$setter = "set_$prop";
765
766
				if ( is_callable( array( $this, $setter ) ) ) {
767
					$this->{$setter}( $value );
768
				}
769
			} catch ( Exception $e ) {
770
				if ( ! $errors ) {
771
					$errors = new WP_Error();
772
				}
773
				$errors->add( $e->getCode(), $e->getMessage() );
774
				$this->last_error = $e->getMessage();
775
			}
776
		}
777
778
		return $errors && count( $errors->get_error_codes() ) ? $errors : true;
779
	}
780
781
	/**
782
	 * Sets a prop for a setter method.
783
	 *
784
	 * This stores changes in a special array so we can track what needs saving
785
	 * the the DB later.
786
	 *
787
	 * @since 1.0.19
788
	 * @param string $prop Name of prop to set.
789
	 * @param mixed  $value Value of the prop.
790
	 */
791
	protected function set_prop( $prop, $value ) {
792
		if ( array_key_exists( $prop, $this->data ) ) {
793
			if ( true === $this->object_read ) {
794
				if ( $value !== $this->data[ $prop ] || array_key_exists( $prop, $this->changes ) ) {
795
					$this->changes[ $prop ] = $value;
796
				}
797
			} else {
798
				$this->data[ $prop ] = $value;
799
			}
800
		}
801
	}
802
803
	/**
804
	 * Return data changes only.
805
	 *
806
	 * @since 1.0.19
807
	 * @return array
808
	 */
809
	public function get_changes() {
810
		return $this->changes;
811
	}
812
813
	/**
814
	 * Merge changes with data and clear.
815
	 *
816
	 * @since 1.0.19
817
	 */
818
	public function apply_changes() {
819
		$this->data    = array_replace_recursive( $this->data, $this->changes );
820
		$this->changes = array();
821
	}
822
823
	/**
824
	 * Prefix for action and filter hooks on data.
825
	 *
826
	 * @since  1.0.19
827
	 * @return string
828
	 */
829
	protected function get_hook_prefix() {
830
		return 'wpinv_get_' . $this->object_type . '_';
831
	}
832
833
	/**
834
	 * Gets a prop for a getter method.
835
	 *
836
	 * Gets the value from either current pending changes, or the data itself.
837
	 * Context controls what happens to the value before it's returned.
838
	 *
839
	 * @since  1.0.19
840
	 * @param  string $prop Name of prop to get.
841
	 * @param  string $context What the value is for. Valid values are view and edit.
842
	 * @return mixed
843
	 */
844
	protected function get_prop( $prop, $context = 'view' ) {
845
		$value = null;
846
847
		if ( array_key_exists( $prop, $this->data ) ) {
848
			$value = array_key_exists( $prop, $this->changes ) ? $this->changes[ $prop ] : $this->data[ $prop ];
849
850
			if ( 'view' === $context ) {
851
				$value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this );
852
			}
853
		}
854
855
		return $value;
856
	}
857
858
	/**
859
	 * Sets a date prop whilst handling formatting and datetime objects.
860
	 *
861
	 * @since 1.0.19
862
	 * @param string         $prop Name of prop to set.
863
	 * @param string|integer $value Value of the prop.
864
	 */
865
	protected function set_date_prop( $prop, $value ) {
866
867
		if ( empty( $value ) ) {
868
			$this->set_prop( $prop, null );
869
			return;
870
		}
871
		$this->set_prop( $prop, $value );
872
873
	}
874
875
	/**
876
	 * When invalid data is found, throw an exception unless reading from the DB.
877
	 *
878
	 * @throws Exception Data Exception.
879
	 * @since 1.0.19
880
	 * @param string $code             Error code.
881
	 * @param string $message          Error message.
882
	 */
883
	protected function error( $code, $message ) {
884
		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

884
		throw new Exception( $message, /** @scrutinizer ignore-type */ $code );
Loading history...
885
	}
886
887
	/**
888
	 * Handle the status transition.
889
	 */
890
	protected function status_transition() {
891
		$status_transition = $this->status_transition;
892
893
		// Reset status transition variable.
894
		$this->status_transition = false;
0 ignored issues
show
Bug Best Practice introduced by
The property status_transition does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
895
896
		if ( $status_transition ) {
897
			try {
898
899
				$object = $this->object_type;
900
901
				if ( 'invoice' == $object ) {
902
					$this->get_type();
0 ignored issues
show
Bug introduced by
The method get_type() does not exist on GetPaid_Data. It seems like you code against a sub-type of said class. However, the method does not exist in GetPaid_Payment_Form. Are you sure you never get one of those? ( Ignorable by Annotation )

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

902
					$this->/** @scrutinizer ignore-call */ 
903
            get_type();
Loading history...
903
				}
904
905
				do_action( "wpinv_{$object}_status_" . $status_transition['to'], $this->get_id(), $this );
906
907
				if ( ! empty( $status_transition['from'] ) ) {
908
					/* translators: 1: old status 2: new status */
909
					$transition_note = sprintf( __( 'Order status changed from %1$s to %2$s.', 'woocommerce' ), wc_get_order_status_name( $status_transition['from'] ), wc_get_order_status_name( $status_transition['to'] ) );
0 ignored issues
show
Bug introduced by
The function wc_get_order_status_name was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

909
					$transition_note = sprintf( __( 'Order status changed from %1$s to %2$s.', 'woocommerce' ), /** @scrutinizer ignore-call */ wc_get_order_status_name( $status_transition['from'] ), wc_get_order_status_name( $status_transition['to'] ) );
Loading history...
910
911
					// Note the transition occurred.
912
					$this->add_status_transition_note( $transition_note, $status_transition );
0 ignored issues
show
Bug introduced by
The method add_status_transition_note() does not exist on GetPaid_Data. Did you maybe mean status_transition()? ( Ignorable by Annotation )

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

912
					$this->/** @scrutinizer ignore-call */ 
913
            add_status_transition_note( $transition_note, $status_transition );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
913
914
					do_action( 'woocommerce_order_status_' . $status_transition['from'] . '_to_' . $status_transition['to'], $this->get_id(), $this );
915
					do_action( 'woocommerce_order_status_changed', $this->get_id(), $status_transition['from'], $status_transition['to'], $this );
916
917
					// Work out if this was for a payment, and trigger a payment_status hook instead.
918
					if (
919
						in_array( $status_transition['from'], apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ), $this ), true )
920
						&& in_array( $status_transition['to'], wc_get_is_paid_statuses(), true )
0 ignored issues
show
Bug introduced by
The function wc_get_is_paid_statuses was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

920
						&& in_array( $status_transition['to'], /** @scrutinizer ignore-call */ wc_get_is_paid_statuses(), true )
Loading history...
921
					) {
922
						/**
923
						 * Fires when the order progresses from a pending payment status to a paid one.
924
						 *
925
						 * @since 3.9.0
926
						 * @param int Order ID
927
						 * @param WC_Order Order object
928
						 */
929
						do_action( 'woocommerce_order_payment_status_changed', $this->get_id(), $this );
930
					}
931
				} else {
932
					/* translators: %s: new order status */
933
					$transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $status_transition['to'] ) );
934
935
					// Note the transition occurred.
936
					$this->add_status_transition_note( $transition_note, $status_transition );
937
				}
938
			} catch ( Exception $e ) {
939
				$logger = wc_get_logger();
0 ignored issues
show
Bug introduced by
The function wc_get_logger was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

939
				$logger = /** @scrutinizer ignore-call */ wc_get_logger();
Loading history...
940
				$logger->error(
941
					sprintf(
942
						'Status transition of order #%d errored!',
943
						$this->get_id()
944
					),
945
					array(
946
						'order' => $this,
947
						'error' => $e,
948
					)
949
				);
950
				$this->add_order_note( __( 'Error during status transition.', 'woocommerce' ) . ' ' . $e->getMessage() );
0 ignored issues
show
Bug introduced by
The method add_order_note() does not exist on GetPaid_Data. ( Ignorable by Annotation )

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

950
				$this->/** @scrutinizer ignore-call */ 
951
           add_order_note( __( 'Error during status transition.', 'woocommerce' ) . ' ' . $e->getMessage() );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
951
			}
952
		}
953
	}
954
}
955