Passed
Pull Request — master (#377)
by Brian
05:01
created

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

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

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

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

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

882
		throw new Exception( $message, /** @scrutinizer ignore-type */ $code );
Loading history...
883
	}
884
885
	/**
886
	 * Handle the status transition.
887
	 */
888
	protected function status_transition() {
889
		$status_transition = $this->status_transition;
890
891
		// Reset status transition variable.
892
		$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...
893
894
		if ( $status_transition ) {
895
			try {
896
897
				$object = $this->object_type;
898
899
				if ( 'invoice' == $object ) {
900
					$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

900
					$this->/** @scrutinizer ignore-call */ 
901
            get_type();
Loading history...
901
				}
902
903
				do_action( "wpinv_{$object}_status_" . $status_transition['to'], $this->get_id(), $this );
904
905
				if ( ! empty( $status_transition['from'] ) ) {
906
					/* translators: 1: old status 2: new status */
907
					$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

907
					$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...
908
909
					// Note the transition occurred.
910
					$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

910
					$this->/** @scrutinizer ignore-call */ 
911
            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...
911
912
					do_action( 'woocommerce_order_status_' . $status_transition['from'] . '_to_' . $status_transition['to'], $this->get_id(), $this );
913
					do_action( 'woocommerce_order_status_changed', $this->get_id(), $status_transition['from'], $status_transition['to'], $this );
914
915
					// Work out if this was for a payment, and trigger a payment_status hook instead.
916
					if (
917
						in_array( $status_transition['from'], apply_filters( 'woocommerce_valid_order_statuses_for_payment', array( 'pending', 'failed' ), $this ), true )
918
						&& 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

918
						&& in_array( $status_transition['to'], /** @scrutinizer ignore-call */ wc_get_is_paid_statuses(), true )
Loading history...
919
					) {
920
						/**
921
						 * Fires when the order progresses from a pending payment status to a paid one.
922
						 *
923
						 * @since 3.9.0
924
						 * @param int Order ID
925
						 * @param WC_Order Order object
926
						 */
927
						do_action( 'woocommerce_order_payment_status_changed', $this->get_id(), $this );
928
					}
929
				} else {
930
					/* translators: %s: new order status */
931
					$transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $status_transition['to'] ) );
932
933
					// Note the transition occurred.
934
					$this->add_status_transition_note( $transition_note, $status_transition );
935
				}
936
			} catch ( Exception $e ) {
937
				$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

937
				$logger = /** @scrutinizer ignore-call */ wc_get_logger();
Loading history...
938
				$logger->error(
939
					sprintf(
940
						'Status transition of order #%d errored!',
941
						$this->get_id()
942
					),
943
					array(
944
						'order' => $this,
945
						'error' => $e,
946
					)
947
				);
948
				$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

948
				$this->/** @scrutinizer ignore-call */ 
949
           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...
949
			}
950
		}
951
	}
952
}
953