Passed
Pull Request — master (#257)
by
unknown
04:49
created

WPInv_Discount::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
/**
3
 * Contains Discount calculation class
4
 *
5
 * @since   1.0.14
6
 */
7
8
defined( 'ABSPATH' ) || exit;
9
10
/**
11
 * Discount class.
12
 * 
13
 * @since 1.0.14
14
 *
15
 */
16
class WPInv_Discount {
17
	
18
	/**
19
	 * Discount ID.
20
	 *
21
	 * @since 1.0.14
22
	 * @var array
23
	 */
24
	public $ID = null;
25
26
	/**
27
	 * Old discount status.
28
	 *
29
	 * @since 1.0.14
30
	 * @var string
31
	 */
32
	public $old_status = 'draft';
33
	
34
	/**
35
	 * Data array, with defaults.
36
	 *
37
	 * @since 1.0.14
38
	 * @var array
39
	 */
40
	protected $data = array();
41
42
	/**
43
	 * Discount constructor.
44
	 *
45
	 * @param int|array|string|WPInv_Discount $discount discount data, object, ID or code.
46
	 * @since 1.0.14
47
	 */
48
	public function __construct( $discount = array() ) {
49
        
50
        // If the discount is an instance of this class...
51
		if ( $discount instanceof WPInv_Discount ) {
52
			$this->init( $discount->data );
53
			return;
54
        }
55
        
56
        // If the discount is an array of discount details...
57
        if ( is_array( $discount ) ) {
58
			$this->init( $discount );
59
			return;
60
		}
61
		
62
		// Try fetching the discount by its post id.
63
		$data = false;
64
		
65
		if ( ! empty( $discount ) && is_numeric( $discount ) ) {
66
			$discount = absint( $discount );
67
			$data = self::get_data_by( 'id', $discount );
68
		}
69
70
		if ( $data ) {
71
			$this->init( $data );
72
			return;
73
		}
74
		
75
		// Try fetching the discount by its discount code.
76
		if ( ! empty( $discount ) && is_string( $discount ) ) {
77
			$data = self::get_data_by( 'discount_code', $discount );
78
		}
79
80
		if ( $data ) {
81
			$this->init( $data );
82
			return;
83
		} 
84
		
85
		// If we are here then the discount does not exist.
86
		$this->init( array() );
87
	}
88
	
89
	/**
90
	 * Sets up object properties
91
	 *
92
	 * @since 1.0.14
93
	 * @param array $data An array containing the discount's data
94
	 */
95
	public function init( $data ) {
96
		$data       	  = self::sanitize_discount_data( $data );
97
		$this->data 	  = $data;
98
		$this->old_status = $data['status'];
99
		$this->ID   	  = $data['ID'];
100
	}
101
	
102
	/**
103
	 * Fetch a discount from the db/cache
104
	 *
105
	 *
106
	 * @static
107
	 *
108
	 *
109
	 * @param string $field The field to query against: 'ID', 'discount_code'
110
	 * @param string|int $value The field value
111
	 * @since 1.0.14
112
	 * @return array|false array of discount details on success. False otherwise.
113
	 */
114
	public static function get_data_by( $field, $value ) {
115
116
		// 'ID' is an alias of 'id'.
117
		if ( 'ID' === $field ) {
118
			$field = 'id';
119
		}
120
121
		if ( 'id' == $field ) {
122
			// Make sure the value is numeric to avoid casting objects, for example,
123
			// to int 1.
124
			if ( ! is_numeric( $value ) )
125
				return false;
126
			$value = intval( $value );
127
			if ( $value < 1 )
128
				return false;
129
		} else {
130
			$value = trim( $value );
131
		}
132
133
		if ( !$value || ! is_string( $field ) )
134
			return false;
135
136
		// prepare query args
137
		switch ( strtolower( $field ) ) {
138
			case 'id':
139
				$discount_id = $value;
140
				$args		 = array( 'include' => array( $value ) );
141
				break;
142
			case 'discount_code':
143
			case 'code':
144
				$discount_id = wp_cache_get( $value, 'WPInv_Discount_Codes' );
145
				$args		 = array( 'meta_key' => '_wpi_discount_code', 'meta_value' => $value );
146
				break;
147
			case 'name':
148
				$discount_id = 0;
149
				$args		 = array( 'name' => $value );
150
				break;
151
			default:
152
				return false;
153
		}
154
155
		// Check if there is a cached value.
156
		if ( false !== $discount_id && $discount = wp_cache_get( $discount_id, 'WPInv_Discounts' ) ) {
0 ignored issues
show
Bug introduced by
It seems like $discount_id can also be of type true; however, parameter $key of wp_cache_get() does only seem to accept integer|string, 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

156
		if ( false !== $discount_id && $discount = wp_cache_get( /** @scrutinizer ignore-type */ $discount_id, 'WPInv_Discounts' ) ) {
Loading history...
157
			return $discount;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $discount also could return the type true which is incompatible with the documented return type array|false.
Loading history...
158
		}
159
160
		$args = wp_parse_args(
161
			$args,
0 ignored issues
show
Security Variable Injection introduced by
$args can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and wpinv_store_discount() is called
    in includes/admin/admin-meta-boxes.php on line 379
  2. Enters via parameter $data
    in includes/wpinv-discount-functions.php on line 252
  3. Data is passed through wp_parse_args(), and wp_parse_args($data, $existing_data) is assigned to $data
    in includes/wpinv-discount-functions.php on line 274
  4. wpinv_get_discount_obj() is called
    in includes/wpinv-discount-functions.php on line 275
  5. Enters via parameter $discount
    in includes/wpinv-discount-functions.php on line 215
  6. WPInv_Discount::__construct() is called
    in includes/wpinv-discount-functions.php on line 216
  7. Enters via parameter $discount
    in includes/class-wpinv-discount.php on line 48
  8. WPInv_Discount::get_data_by() is called
    in includes/class-wpinv-discount.php on line 77
  9. Enters via parameter $value
    in includes/class-wpinv-discount.php on line 114
  10. Data is passed through trim(), and trim($value) is assigned to $value
    in includes/class-wpinv-discount.php on line 130
  11. array('include' => array($value)) is assigned to $args
    in includes/class-wpinv-discount.php on line 140

Used in variable context

  1. wp_parse_args() is called
    in includes/class-wpinv-discount.php on line 161
  2. Enters via parameter $args
    in wordpress/wp-includes/functions.php on line 4294
  3. wp_parse_str() is called
    in wordpress/wp-includes/functions.php on line 4300
  4. Enters via parameter $string
    in wordpress/wp-includes/formatting.php on line 4865
  5. parse_str() is called
    in wordpress/wp-includes/formatting.php on line 4866

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
162
			array(
163
				'post_type'      => 'wpi_discount',
164
				'posts_per_page' => 1,
165
				'post_status'    => array( 'publish', 'pending', 'draft', 'expired' )
166
			)
167
		);
168
169
		$discount = get_posts( $args );
0 ignored issues
show
Security Variable Injection introduced by
$args can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and wpinv_store_discount() is called
    in includes/admin/admin-meta-boxes.php on line 379
  2. Enters via parameter $data
    in includes/wpinv-discount-functions.php on line 252
  3. Data is passed through wp_parse_args(), and wp_parse_args($data, $existing_data) is assigned to $data
    in includes/wpinv-discount-functions.php on line 274
  4. wpinv_get_discount_obj() is called
    in includes/wpinv-discount-functions.php on line 275
  5. Enters via parameter $discount
    in includes/wpinv-discount-functions.php on line 215
  6. WPInv_Discount::__construct() is called
    in includes/wpinv-discount-functions.php on line 216
  7. Enters via parameter $discount
    in includes/class-wpinv-discount.php on line 48
  8. WPInv_Discount::get_data_by() is called
    in includes/class-wpinv-discount.php on line 77
  9. Enters via parameter $value
    in includes/class-wpinv-discount.php on line 114
  10. Data is passed through trim(), and trim($value) is assigned to $value
    in includes/class-wpinv-discount.php on line 130
  11. array('include' => array($value)) is assigned to $args
    in includes/class-wpinv-discount.php on line 140
  12. Data is passed through wp_parse_args(), and wp_parse_args($args, array('post_type' => 'wpi_discount', 'posts_per_page' => 1, 'post_status' => array('publish', 'pending', 'draft', 'expired'))) is assigned to $args
    in includes/class-wpinv-discount.php on line 160

Used in variable context

  1. get_posts() is called
    in includes/class-wpinv-discount.php on line 169
  2. Enters via parameter $args
    in wordpress/wp-includes/post.php on line 2009
  3. wp_parse_args() is called
    in wordpress/wp-includes/post.php on line 2023
  4. Enters via parameter $args
    in wordpress/wp-includes/functions.php on line 4294
  5. wp_parse_str() is called
    in wordpress/wp-includes/functions.php on line 4300
  6. Enters via parameter $string
    in wordpress/wp-includes/formatting.php on line 4865
  7. parse_str() is called
    in wordpress/wp-includes/formatting.php on line 4866

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
170
				
171
		if( empty( $discount ) ) {
172
			return false;
173
		}
174
175
		$discount = $discount[0];
176
		
177
		// Prepare the return data.
178
		$return = array(
179
            'ID'                          => $discount->ID,
180
            'code'                        => get_post_meta( $discount->ID, '_wpi_discount_code', true ),
181
            'amount'                      => get_post_meta( $discount->ID, '_wpi_discount_amount', true ),
182
            'date_created'                => $discount->post_date,
183
			'date_modified'               => $discount->post_modified,
184
			'status'               		  => $discount->post_status,
185
			'start'                  	  => get_post_meta( $discount->ID, '_wpi_discount_start', true ),
186
            'expiration'                  => get_post_meta( $discount->ID, '_wpi_discount_expiration', true ),
187
            'type'               		  => get_post_meta( $discount->ID, '_wpi_discount_type', true ),
188
            'description'                 => $discount->post_excerpt,
189
            'uses'                 		  => get_post_meta( $discount->ID, '_wpi_discount_uses', true ),
190
            'is_single_use'               => get_post_meta( $discount->ID, '_wpi_discount_is_single_use', true ),
191
            'items'              	      => get_post_meta( $discount->ID, '_wpi_discount_items', true ),
192
            'excluded_items'              => get_post_meta( $discount->ID, '_wpi_discount_excluded_items', true ),
193
            'max_uses'                    => get_post_meta( $discount->ID, '_wpi_discount_max_uses', true ),
194
            'is_recurring'                => get_post_meta( $discount->ID, '_wpi_discount_is_recurring', true ),
195
            'min_total'                   => get_post_meta( $discount->ID, '_wpi_discount_min_total', true ),
196
            'max_total'                   => get_post_meta( $discount->ID, '_wpi_discount_max_total', true ),
197
        );
198
		
199
		$return = self::sanitize_discount_data( $return );
200
		$return = apply_filters( 'wpinv_discount_properties', $return );
201
202
		// Update the cache with our data
203
		wp_cache_add( $discount->ID, $return, 'WPInv_Discounts' );
204
		wp_cache_add( $return['code'], $discount->ID, 'WPInv_Discount_Codes' );
205
206
		return $return;
207
	}
208
	
209
	/**
210
	 * Sanitizes discount data
211
	 *
212
	 * @static
213
	 * @since 1.0.14
214
	 * @access public
215
	 *
216
	 * @return array the sanitized data
217
	 */
218
	public static function sanitize_discount_data( $data ) {
219
		
220
		$allowed_discount_types = array_keys( wpinv_get_discount_types() );
221
		
222
		$return = array(
223
            'ID'                          => null,
224
            'code'                        => '',
225
            'amount'                      => 0,
226
            'date_created'                => current_time('mysql'),
227
            'date_modified'               => current_time('mysql'),
228
			'expiration'                  => null,
229
			'start'                  	  => current_time('mysql'),
230
			'status'                  	  => 'draft',
231
            'type'               		  => 'percent',
232
            'description'                 => '',
233
            'uses'                        => 0,
234
            'is_single_use'               => false,
235
            'items'              		  => array(),
236
            'excluded_items'              => array(),
237
            'max_uses'                    => 0,
238
            'is_recurring'                => false,
239
            'min_total'                   => '',
240
			'max_total'              	  => '',
241
		);
242
		
243
				
244
		// Arrays only please.
245
		if (! is_array( $data ) ) {
246
            return $return;
247
        }
248
249
		// If an id is provided, ensure it is a valid discount.
250
        if (! empty( $data['ID'] ) && is_numeric( $data['ID'] ) && 'wpi_discount' !== get_post_type( $data['ID'] ) ) {
251
            return $return;
252
		}
253
254
        $return = wp_parse_args( $data, $return );
0 ignored issues
show
Security Variable Injection introduced by
$data can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and wpinv_store_discount() is called
    in includes/admin/admin-meta-boxes.php on line 379
  2. Enters via parameter $data
    in includes/wpinv-discount-functions.php on line 252
  3. Data is passed through wp_parse_args(), and wp_parse_args($data, $existing_data) is assigned to $data
    in includes/wpinv-discount-functions.php on line 274
  4. wpinv_get_discount_obj() is called
    in includes/wpinv-discount-functions.php on line 275
  5. Enters via parameter $discount
    in includes/wpinv-discount-functions.php on line 215
  6. WPInv_Discount::__construct() is called
    in includes/wpinv-discount-functions.php on line 216
  7. Enters via parameter $discount
    in includes/class-wpinv-discount.php on line 48
  8. WPInv_Discount::init() is called
    in includes/class-wpinv-discount.php on line 58
  9. Enters via parameter $data
    in includes/class-wpinv-discount.php on line 95
  10. WPInv_Discount::sanitize_discount_data() is called
    in includes/class-wpinv-discount.php on line 96
  11. Enters via parameter $data
    in includes/class-wpinv-discount.php on line 218

Used in variable context

  1. wp_parse_args() is called
    in includes/class-wpinv-discount.php on line 256
  2. Enters via parameter $args
    in wordpress/wp-includes/functions.php on line 4294
  3. wp_parse_str() is called
    in wordpress/wp-includes/functions.php on line 4300
  4. Enters via parameter $string
    in wordpress/wp-includes/formatting.php on line 4865
  5. parse_str() is called
    in wordpress/wp-includes/formatting.php on line 4866

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
255
256
        // Sanitize some keys.
257
        $return['amount']         = wpinv_sanitize_amount( $return['amount'] );
258
		$return['is_single_use']  = (bool) $return['is_single_use'];
259
		$return['is_recurring']   = (bool) $return['is_recurring'];
260
		$return['uses']	          = (int) $return['uses'];
261
		$return['max_uses']	      = (int) $return['max_uses'];
262
		$return['min_total'] 	  = wpinv_sanitize_amount( $return['min_total'] );
263
        $return['max_total'] 	  = wpinv_sanitize_amount( $return['max_total'] );
264
265
		// Trim all values.
266
		$return = wpinv_clean( $return );
267
		
268
		// Ensure the discount type is supported.
269
        if ( ! in_array( $return['type'], $allowed_discount_types, true ) ) {
270
            $return['type'] = 'percent';
271
		}
272
		$return['type_name'] = wpinv_get_discount_type_name( $return['type'] );
273
		
274
		// Do not offer more than a 100% discount.
275
		if ( $return['type'] == 'percent' && (float) $return['amount'] > 100 ) {
276
			$return['amount'] = 100;
277
		}
278
279
		// Format dates.
280
		foreach( wpinv_parse_list( 'date_created date_modified expiration start') as $prop ) {
281
			if( ! empty( $return[$prop] ) ) {
282
				$return[$prop]      = date_i18n( 'Y-m-d H:i:s', strtotime( $return[$prop] ) );
283
			}
284
		}
285
286
		// Formart items.
287
		foreach( wpinv_parse_list( 'excluded_items items') as $prop ) {
288
289
			if( ! empty( $return[$prop] ) ) {
290
				// Ensure that the property is an array of non-empty integers.
291
				$return[$prop]      = array_filter( array_map( 'intval', wpinv_parse_list( $return[$prop] ) ) );
292
			} else {
293
				$return[$prop]      = array();
294
			}
295
296
		}
297
		
298
		return apply_filters( 'sanitize_discount_data', $return, $data );
299
	}
300
	
301
	/**
302
	 * Magic method for checking the existence of a certain custom field.
303
	 *
304
	 * @since 1.0.14
305
	 * @access public
306
	 *
307
	 * @return bool Whether the given discount field is set.
308
	 */
309
	public function __isset( $key ){
310
		return isset( $this->data[$key] );
311
	}
312
	
313
	/**
314
	 * Magic method for accessing discount properties.
315
	 *
316
	 * @since 1.0.14
317
	 * @access public
318
	 *
319
	 * @param string $key Discount data to retrieve
320
	 * @return mixed Value of the given discount property (if set).
321
	 */
322
	public function __get( $key ) {
323
		
324
		if ( $key == 'id' ) {
325
			$key = 'ID';
326
		}
327
		
328
		if( method_exists( $this, "get_$key") ) {
329
			$value 	= call_user_func( array( $this, "get_$key" ) );
330
		} else if( isset( $this->data[$key] ) ) {
331
			$value 	= $this->data[$key];
332
		} else {
333
			$value = null;
334
		}
335
		
336
		return apply_filters( "wpinv_get_discount_{$key}", $value, $this->ID, $this, $this->data['code'], $this->data );
337
338
	}
339
	
340
	/**
341
	 * Magic method for setting discount fields.
342
	 *
343
	 * This method does not update custom fields in the database.
344
	 *
345
	 * @since 1.0.14
346
	 * @access public
347
	 *
348
	 */
349
	public function __set( $key, $value ) {
350
		
351
		if ( 'id' == strtolower( $key ) ) {
352
			
353
			$this->ID = $value;
354
			$this->data['ID'] = $value;
355
			return;
356
			
357
		}
358
		
359
		$value = apply_filters( "wpinv_set_discount_{$key}", $value, $this->ID, $this, $this->code, $this->data );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
360
		$this->data[$key] = $value;
361
		
362
	}
363
	
364
	/**
365
	 * Saves (or updates) a discount to the database
366
	 *
367
	 * @since 1.0.14
368
	 * @access public
369
	 * @return bool
370
	 *
371
	 */
372
	public function save(){
373
		
374
		$data = self::sanitize_discount_data( $this->data );
375
376
		// Should we create a new post?
377
		if(! $data[ 'ID' ] ) {
378
379
			$id = wp_insert_post( array(
380
				'post_status'           => $data['status'],
381
				'post_type'             => 'wpi_discount',
382
				'post_excerpt'          => $data['description'],
383
			) );
384
385
			if( empty( $id ) ) {
386
				return false;
387
			}
388
389
			$data[ 'ID' ] = $id;
390
			$this->ID = $id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $id of type WP_Error or integer is incompatible with the declared type array of property $ID.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
391
			$this->data['ID'] = $id;
392
393
		} else {
394
			$this->update_status( $data['post_status'] );
395
		}
396
397
		$meta = apply_filters( 'wpinv_update_discount', $data, $this->ID, $this );
398
399
		do_action( 'wpinv_pre_update_discount', $meta, $this->ID, $this );
400
401
		foreach( wpinv_parse_list( 'ID date_created date_modified status description type_name' ) as $prop ) {
402
			unset( $meta[$prop] );
403
		}
404
405
		if( empty( $meta['uses'] ) ) {
406
			unset( $meta['uses'] );
407
		}
408
409
		// Save the metadata
410
		foreach( $meta as $key => $value ) {
411
			update_post_meta( $this->ID, "_wpi_discount_$key", $value );
412
		}
413
		
414
		// Empty the cache for this discount.
415
		wp_cache_delete( $this->ID, 'WPInv_Discounts' );
416
		wp_cache_delete( $data['code'], 'WPInv_Discount_Codes' );
417
418
		do_action( 'wpinv_post_update_discount', $meta, $this->ID, $this );
419
420
		$data = self::get_data_by( 'id', $this->ID );
421
		$this->init( $data );
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type false; however, parameter $data of WPInv_Discount::init() 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

421
		$this->init( /** @scrutinizer ignore-type */ $data );
Loading history...
422
423
		return true;		
424
	}
425
426
	/**
427
	 * Saves (or updates) a discount to the database
428
	 *
429
	 * @since 1.0.14
430
	 * @access public
431
	 * @return bool
432
	 *
433
	 */
434
	public function update_status( $status = 'publish' ){
435
436
437
		if( $this->exists() && $this->old_status != $status ) {
438
439
			do_action( 'wpinv_pre_update_discount_status', $this->ID, $this->old_status, $status );
440
        	$updated = wp_update_post( array( 'ID' => $this->ID, 'post_status' => $status ) );
441
			do_action( 'wpinv_post_update_discount_status', $this->ID, $this->old_status, $status );
442
443
			wp_cache_delete( $this->ID, 'WPInv_Discounts' );
0 ignored issues
show
Bug introduced by
$this->ID of type array is incompatible with the type integer|string expected by parameter $key of wp_cache_delete(). ( Ignorable by Annotation )

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

443
			wp_cache_delete( /** @scrutinizer ignore-type */ $this->ID, 'WPInv_Discounts' );
Loading history...
444
			wp_cache_delete( $this->code, 'WPInv_Discount_Codes' );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
445
446
			return $updated !== 0;
447
			
448
		}
449
450
		return false;		
451
	}
452
	
453
	
454
	/**
455
	 * Checks whether a discount exists in the database or not
456
	 * 
457
	 * @since 1.0.14
458
	 */
459
	public function exists(){
460
		return ! empty( $this->ID );
461
	}
462
	
463
	// Boolean methods
464
	
465
	/**
466
	 * Checks the discount type.
467
	 * 
468
	 * 
469
	 * @param  string $type the discount type to check against
470
	 * @since 1.0.14
471
	 * @return bool
472
	 */
473
	public function is_type( $type ) {
474
		return $this->type == $type;
0 ignored issues
show
Bug Best Practice introduced by
The property type does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
475
	}
476
	
477
	/**
478
	 * Checks whether the discount is published or not
479
	 * 
480
	 * @since 1.0.14
481
	 * @return bool
482
	 */
483
	public function is_active() {
484
		return $this->post_status == 'publish';
0 ignored issues
show
Bug Best Practice introduced by
The property post_status does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
485
	}
486
	
487
	/**
488
	 * Checks whether the discount is has exided the usage limit or not
489
	 * 
490
	 * @since 1.0.14
491
	 * @return bool
492
	 */
493
	public function has_exceeded_limit() {
494
		if( empty( $this->max_uses ) || empty( $this->uses ) ) { 
0 ignored issues
show
Bug Best Practice introduced by
The property max_uses does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
495
			return false ;
496
		}
497
		
498
		$exceeded =  $this->uses >= $this->max_uses;
499
		return apply_filters( 'wpinv_is_discount_maxed_out', $exceeded, $this->ID, $this, $this->code );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
500
	}
501
	
502
	/**
503
	 * Checks if the discount is expired
504
	 * 
505
	 * @since 1.0.14
506
	 * @return bool
507
	 */
508
	public function is_expired() {
509
		$expired = empty ( $this->expires ) ? false : current_time( 'timestamp' ) > strtotime( $this->expires );
0 ignored issues
show
Bug Best Practice introduced by
The property expires does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
510
		return apply_filters( 'wpinv_is_discount_expired', $expired, $this->ID, $this, $this->code );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
511
	}
512
513
	/**
514
	 * Checks the discount start date.
515
	 * 
516
	 * @since 1.0.14
517
	 * @return bool
518
	 */
519
	public function has_started() {
520
		$started = empty ( $this->start ) ? true : current_time( 'timestamp' ) > strtotime( $this->start );
0 ignored issues
show
Bug Best Practice introduced by
The property start does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
521
		return apply_filters( 'wpinv_is_discount_started', $started, $this->ID, $this, $this->code );		
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
522
	}
523
	
524
	/**
525
	 * Check if a discount is valid for a given item id.
526
	 *
527
	 * @param  int|array  $item_ids
528
	 * @since 1.0.14
529
	 * @return boolean
530
	 */
531
	public function is_valid_for_items( $item_ids ) {
532
		 
533
		$item_ids = wpinv_parse_list( $item_ids );
534
		$included = array_intersect( $item_ids, $this->items );
0 ignored issues
show
Bug Best Practice introduced by
The property items does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
535
		$excluded = array_intersect( $item_ids, $this->excluded_items );
0 ignored issues
show
Bug Best Practice introduced by
The property excluded_items does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
536
537
		if( ! empty( $this->excluded_items ) && ! empty( $excluded ) ) {
538
			return false;
539
		}
540
541
		if( ! empty( $this->included_items ) && empty( $included ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property included_items does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
542
			return false;
543
		}
544
		return true;
545
	}
546
	
547
	/**
548
	 * Check if a discount is valid for the given amount
549
	 *
550
	 * @param  float  $amount The amount to check against
551
	 * @since 1.0.14
552
	 * @return boolean
553
	 */
554
	public function is_valid_for_amount( $amount ) {
555
		
556
		// Amount passed is not valid;
557
		if(! is_numeric ( $amount ) ) {
0 ignored issues
show
introduced by
The condition is_numeric($amount) is always true.
Loading history...
558
			return false;
559
		}
560
561
		$amount = floatval( $amount );
562
563
		// check if it meets the minimum amount valid.
564
		if( $this->min_total > 0 && $amount < $this->min_total ) {
0 ignored issues
show
Bug Best Practice introduced by
The property min_total does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
565
			return false;
566
		}
567
568
		// check if it meets the maximum amount valid.
569
		if( $this->max_total > 0 && $amount > $this->max_total ) {
0 ignored issues
show
Bug Best Practice introduced by
The property max_total does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
570
			return false;
571
		}
572
573
		return true;
574
	}
575
576
	/**
577
	 * Checks if the minimum amount is met
578
	 *
579
	 * @param  float  $amount The amount to check against
580
	 * @since 1.0.14
581
	 * @return boolean
582
	 */
583
	public function is_minimum_amount_met( $amount ) {
584
		
585
		// Amount passed is not valid;
586
		if(! is_numeric ( $amount ) ) {
0 ignored issues
show
introduced by
The condition is_numeric($amount) is always true.
Loading history...
587
			return false;
588
		}
589
590
		$amount = floatval( $amount );
591
		$min_met= ! ( $this->min_total > 0 && $amount < $this->min_total );
0 ignored issues
show
Bug Best Practice introduced by
The property min_total does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
592
		return apply_filters( 'wpinv_is_discount_min_met', $min_met, $this->ID, $this, $this->code, $amount );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
593
	}
594
595
	/**
596
	 * Checks if the maximum amount is met
597
	 *
598
	 * @param  float  $amount The amount to check against
599
	 * @since 1.0.14
600
	 * @return boolean
601
	 */
602
	public function is_maximum_amount_met( $amount ) {
603
		
604
		// Amount passed is not valid;
605
		if(! is_numeric ( $amount ) ) {
0 ignored issues
show
introduced by
The condition is_numeric($amount) is always true.
Loading history...
606
			return false;
607
		}
608
609
		$amount = floatval( $amount );
610
		$max_met= ! ( $this->max_total > 0 && $amount > $this->max_total );
0 ignored issues
show
Bug Best Practice introduced by
The property max_total does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
611
		return apply_filters( 'wpinv_is_discount_max_met', $max_met, $this->ID, $this, $this->code, $amount );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
612
	}
613
614
	/**
615
	 * Check if a discount is valid for the given user
616
	 *
617
	 * @param  int|string  $user
618
	 * @since 1.0.14
619
	 * @return boolean
620
	 */
621
	public function is_valid_for_user( $user ) {
622
		global $wpi_checkout_id;
623
624
		if( empty( $user ) || empty( $this->is_single_use ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property is_single_use does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
625
			return true;
626
		}
627
628
		$user_id = 0;
629
        if ( is_int( $user ) ) {
630
            $user_id = absint( $user );
631
        } else if ( is_email( $user ) && $user_data = get_user_by( 'email', $user ) ) {
632
            $user_id = $user_data->ID;
633
        } else if ( $user_data = get_user_by( 'login', $user ) ) {
634
            $user_id = $user_data->ID;
635
        } else if ( absint( $user ) > 0 ) {
636
            $user_id = absint( $user );
637
		}
638
639
		if( empty( $user_id ) ) {
640
			return true;
641
		}
642
		
643
		// Get all payments with matching user id
644
        $payments = wpinv_get_invoices( array( 'user' => $user_id, 'limit' => false ) ); 
645
		$code     = strtolower( $this->code );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
646
647
		foreach ( $payments as $payment ) {
648
649
			// Don't count discount used for current invoice checkout.
650
			if ( !empty( $wpi_checkout_id ) && $wpi_checkout_id == $payment->ID ) {
651
				continue;
652
			}
653
			
654
			if ( $payment->has_status( array( 'wpi-cancelled', 'wpi-failed' ) ) ) {
655
				continue;
656
			}
657
658
			$discounts = $payment->get_discounts( true );
659
			if ( empty( $discounts ) ) {
660
				continue;
661
			}
662
663
			$discounts = array_map( 'strtolower', wpinv_parse_list( $discounts ) );
664
			if ( ! empty( $discounts ) && in_array( $code, $discounts ) ) {
665
				return false;
666
			}
667
		}
668
669
		return true;
670
	}
671
672
	/**
673
	 * Deletes the discount from the database
674
	 *
675
	 * @since 1.0.14
676
	 * @return boolean
677
	 */
678
	public function remove() {
679
680
		if( empty( $this->ID ) ) {
681
			return true;
682
		}
683
684
		do_action( 'wpinv_pre_delete_discount', $this->ID );
685
		wp_cache_delete( $this->ID, 'WPInv_Discounts' );
0 ignored issues
show
Bug introduced by
$this->ID of type array is incompatible with the type integer|string expected by parameter $key of wp_cache_delete(). ( Ignorable by Annotation )

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

685
		wp_cache_delete( /** @scrutinizer ignore-type */ $this->ID, 'WPInv_Discounts' );
Loading history...
686
    	wp_delete_post( $this->ID, true );
0 ignored issues
show
Bug introduced by
$this->ID of type array is incompatible with the type integer expected by parameter $postid of wp_delete_post(). ( Ignorable by Annotation )

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

686
    	wp_delete_post( /** @scrutinizer ignore-type */ $this->ID, true );
Loading history...
687
		wp_cache_delete( $this->code, 'WPInv_Discount_Codes' );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
688
    	do_action( 'wpinv_post_delete_discount', $this->ID );
689
690
		$this->ID = null;
691
		$this->data['id'] = null;
692
		return true;
693
	}
694
695
	/**
696
	 * Increases a discount's usage.
697
	 *
698
	 * @since 1.0.14
699
	 * @param int $by The number of usages to increas by.
700
	 * @return bool
701
	 */
702
	public function increase_usage( $by = 1 ) {
703
704
		$this->uses = $this->uses + $by;
0 ignored issues
show
Bug Best Practice introduced by
The property uses does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
705
706
		if( $this->uses  < 0 ) {
707
			$this->uses = 0;
708
		}
709
710
		$this->save();
711
712
		if( $by > 0 ) {
713
			do_action( 'wpinv_discount_increase_use_count', $this->uses, $this->ID, $this->code, $by );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
714
		} else {
715
			do_action( 'wpinv_discount_decrease_use_count', $this->uses, $this->ID, $this->code, absint( $by ) );
716
		}
717
		
718
		return $this->uses;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->uses returns the type integer which is incompatible with the documented return type boolean.
Loading history...
719
	}
720
721
	/**
722
	 * Retrieves discount data
723
	 *
724
	 * @since 1.0.14
725
	 * @return array
726
	 */
727
	public function get_data() {
728
		$return = array();
729
		foreach( array_keys( $this->data ) as $key ) {
730
			$return[ $key ] = $this->$key;
731
		}
732
		return $return;
733
	}
734
735
	/**
736
	 * Retrieves discount data as json
737
	 *
738
	 * @since 1.0.14
739
	 * @return string
740
	 */
741
	public function get_data_as_json() {
742
		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...
743
	}
744
745
	/**
746
	 * Checks if a discount can only be used once per user.
747
	 *
748
	 * @since 1.0.14
749
	 * @return bool
750
	 */
751
	public function get_is_single_use() {
752
		return (bool) apply_filters( 'wpinv_is_discount_single_use', $this->data['is_single_use'], $this->ID, $this, $this->code );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
753
	}
754
755
	/**
756
	 * Checks if a discount is recurring.
757
	 *
758
	 * @since 1.0.14
759
	 * @return bool
760
	 */
761
	public function get_is_recurring() {
762
		return (bool) apply_filters( 'wpinv_is_discount_recurring', $this->data['is_recurring'], $this->ID, $this->code, $this );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
763
	}
764
765
	/**
766
	 * Returns a discount's included items.
767
	 *
768
	 * @since 1.0.14
769
	 * @return array
770
	 */
771
	public function get_items() {
772
		return wpinv_parse_list( apply_filters( 'wpinv_get_discount_item_reqs', $this->data['items'], $this->ID, $this, $this->code ) );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
773
	}
774
775
	/**
776
	 * Returns a discount's discounted amount.
777
	 *
778
	 * @since 1.0.14
779
	 * @return float
780
	 */
781
	public function get_discounted_amount( $amount ) {
782
783
		if ( $this->type == 'flat' ) {
0 ignored issues
show
Bug Best Practice introduced by
The property type does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
784
            $amount = $amount - $this->amount;
0 ignored issues
show
Bug Best Practice introduced by
The property amount does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
785
		} else {
786
            $amount = $amount - ( $amount * ( $this->amount / 100 ) );
787
		}
788
789
		if ( $amount < 0 ) {
790
			$amount = 0;
791
		}
792
793
		return apply_filters( 'wpinv_discounted_amount', $amount, $this->ID, $this, $this->code, $this->amount );
0 ignored issues
show
Bug Best Practice introduced by
The property code does not exist on WPInv_Discount. Since you implemented __get, consider adding a @property annotation.
Loading history...
794
	}
795
	
796
}
797