Passed
Pull Request — master (#68)
by Kiran
04:07
created

wpinv-item-functions.php ➔ wpinv_update_item()   F

Complexity

Conditions 27
Paths 1128

Size

Total Lines 72
Code Lines 44

Duplication

Lines 12
Ratio 16.67 %

Importance

Changes 0
Metric Value
cc 27
eloc 44
nc 1128
nop 2
dl 12
loc 72
rs 2.6039
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
// Exit if accessed directly
3
if ( ! defined( 'ABSPATH' ) ) exit;
4
5
function wpinv_get_item_by( $field = '', $value = '', $type = '' ) {
6
    if( empty( $field ) || empty( $value ) ) {
7
        return false;
8
    }
9
    
10
    $posts = array();
0 ignored issues
show
Unused Code introduced by
$posts is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
11
12
    switch( strtolower( $field ) ) {
13
        case 'id':
14
            $item = new WPInv_Item( $value );
0 ignored issues
show
Documentation introduced by
$value is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
15
16
            if ( !empty( $item ) && $item->post_type == 'wpi_item' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
17
                return $item;
18
            }
19
            return false;
20
21
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
22
23
        case 'slug':
24
        case 'name':
25
            $posts = get_posts( array(
26
                'post_type'      => 'wpi_item',
27
                'name'           => $value,
28
                'posts_per_page' => 1,
29
                'post_status'    => 'any'
30
            ) );
31
32
            break;
33
        case 'custom_id':
34
            if ( empty( $value ) || empty( $type ) ) {
35
                return false;
36
            }
37
            
38
            $meta_query = array();
39
            $meta_query[] = array(
40
                'key'   => '_wpinv_type',
41
                'value' => $type,
42
            );
43
            $meta_query[] = array(
44
                'key'   => '_wpinv_custom_id',
45
                'value' => $value,
46
            );
47
            
48
            $args = array(
49
                'post_type'      => 'wpi_item',
50
                'posts_per_page' => 1,
51
                'post_status'    => 'any',
52
                'orderby'        => 'ID',
53
                'order'          => 'ASC',
54
                'meta_query'     => array( $meta_query )
55
            );
56
            
57
            $posts = get_posts( $args );
58
59
            break;
60
61
        default:
62
            return false;
63
    }
64
    
65
    if ( !empty( $posts[0] ) ) {
66
        $item = new WPInv_Item( $posts[0]->ID );
67
68
        if ( !empty( $item ) && $item->post_type == 'wpi_item' ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
69
            return $item;
70
        }
71
    }
72
73
    return false;
74
}
75
76
function wpinv_get_item( $item = 0 ) {
77
    if ( is_numeric( $item ) ) {
78
        $item = get_post( $item );
79
        if ( ! $item || 'wpi_item' !== $item->post_type )
80
            return null;
81
        return $item;
82
    }
83
84
    $args = array(
85
        'post_type'   => 'wpi_item',
86
        'name'        => $item,
87
        'numberposts' => 1
88
    );
89
90
    $item = get_posts($args);
91
92
    if ( $item ) {
93
        return $item[0];
94
    }
95
96
    return null;
97
}
98
99
function wpinv_is_free_item( $item_id = 0 ) {
100
    if( empty( $item_id ) ) {
101
        return false;
102
    }
103
104
    $item = new WPInv_Item( $item_id );
0 ignored issues
show
Documentation introduced by
$item_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
105
    
106
    return $item->is_free();
107
}
108
109
function wpinv_item_is_editable( $item = 0 ) {
110
    if ( !empty( $item ) && is_a( $item, 'WP_Post' ) ) {
111
        $item = $item->ID;
112
    }
113
        
114
    if ( empty( $item ) ) {
115
        return true;
116
    }
117
118
    $item = new WPInv_Item( $item );
119
    
120
    return (bool) $item->is_editable();
121
}
122
123
function wpinv_get_item_price( $item_id = 0 ) {
124
    if( empty( $item_id ) ) {
125
        return false;
126
    }
127
128
    $item = new WPInv_Item( $item_id );
0 ignored issues
show
Documentation introduced by
$item_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
129
    
130
    return $item->get_price();
131
}
132
133
function wpinv_is_recurring_item( $item_id = 0 ) {
134
    if( empty( $item_id ) ) {
135
        return false;
136
    }
137
138
    $item = new WPInv_Item( $item_id );
0 ignored issues
show
Documentation introduced by
$item_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
139
    
140
    return $item->is_recurring();
141
}
142
143
function wpinv_item_price( $item_id = 0 ) {
144
    if( empty( $item_id ) ) {
145
        return false;
146
    }
147
148
    $price = wpinv_get_item_price( $item_id );
149
    $price = wpinv_price( wpinv_format_amount( $price ) );
150
    
151
    return apply_filters( 'wpinv_item_price', $price, $item_id );
152
}
153
154
function wpinv_item_show_price( $item_id = 0, $echo = true ) {
155
    if ( empty( $item_id ) ) {
156
        $item_id = get_the_ID();
157
    }
158
159
    $price = wpinv_item_price( $item_id );
160
161
    $price           = apply_filters( 'wpinv_item_price', wpinv_sanitize_amount( $price ), $item_id );
162
    $formatted_price = '<span class="wpinv_price" id="wpinv_item_' . $item_id . '">' . $price . '</span>';
163
    $formatted_price = apply_filters( 'wpinv_item_price_after_html', $formatted_price, $item_id, $price );
164
165
    if ( $echo ) {
166
        echo $formatted_price;
167
    } else {
168
        return $formatted_price;
169
    }
170
}
171
172
function wpinv_get_item_final_price( $item_id = 0, $amount_override = null ) {
173
    if ( is_null( $amount_override ) ) {
174
        $original_price = get_post_meta( $item_id, '_wpinv_price', true );
175
    } else {
176
        $original_price = $amount_override;
177
    }
178
    
179
    $price = $original_price;
180
181
    return apply_filters( 'wpinv_get_item_final_price', $price, $item_id );
182
}
183
184
function wpinv_item_custom_singular_name( $item_id ) {
185
    if( empty( $item_id ) ) {
186
        return false;
187
    }
188
189
    $item = new WPInv_Item( $item_id );
190
    
191
    return $item->get_custom_singular_name();
192
}
193
194
function wpinv_get_item_types() {
195
    $item_types = array(
196
            'custom'    => __( 'Standard', 'invoicing' ),
197
            'fee'       => __( 'Fee', 'invoicing' ),
198
        );
199
    return apply_filters( 'wpinv_get_item_types', $item_types );
200
}
201
202
function wpinv_item_types() {
203
    $item_types = wpinv_get_item_types();
204
    
205
    return ( !empty( $item_types ) ? array_keys( $item_types ) : array() );
206
}
207
208
function wpinv_get_item_type( $item_id ) {
209
    if( empty( $item_id ) ) {
210
        return false;
211
    }
212
213
    $item = new WPInv_Item( $item_id );
214
    
215
    return $item->get_type();
216
}
217
218
function wpinv_item_type( $item_id ) {
219
    $item_types = wpinv_get_item_types();
220
    
221
    $item_type = wpinv_get_item_type( $item_id );
222
    
223
    if ( empty( $item_type ) ) {
224
        $item_type = '-';
225
    }
226
    
227
    $item_type = isset( $item_types[$item_type] ) ? $item_types[$item_type] : __( $item_type, 'invoicing' );
228
229
    return apply_filters( 'wpinv_item_type', $item_type, $item_id );
230
}
231
232
function wpinv_record_item_in_log( $item_id = 0, $file_id, $user_info, $ip, $invoice_id ) {
233
    global $wpinv_logs;
234
    
235
    if ( empty( $wpinv_logs ) ) {
236
        return false;
237
    }
238
239
    $log_data = array(
240
        'post_parent'	=> $item_id,
241
        'log_type'		=> 'wpi_item'
242
    );
243
244
    $user_id = isset( $user_info['user_id'] ) ? $user_info['user_id'] : (int) -1;
245
246
    $log_meta = array(
247
        'user_info'	=> $user_info,
248
        'user_id'	=> $user_id,
249
        'file_id'	=> (int)$file_id,
250
        'ip'		=> $ip,
251
        'invoice_id'=> $invoice_id,
252
    );
253
254
    $wpinv_logs->insert_log( $log_data, $log_meta );
255
}
256
257
function wpinv_remove_item_logs_on_delete( $item_id = 0 ) {
258
    if ( 'wpi_item' !== get_post_type( $item_id ) )
259
        return;
260
261
    global $wpinv_logs;
262
    
263
    if ( empty( $wpinv_logs ) ) {
264
        return false;
265
    }
266
267
    // Remove all log entries related to this item
268
    $wpinv_logs->delete_logs( $item_id );
269
}
270
add_action( 'delete_post', 'wpinv_remove_item_logs_on_delete' );
271
272
function wpinv_get_random_item( $post_ids = true ) {
273
    wpinv_get_random_items( 1, $post_ids );
274
}
275
276
function wpinv_get_random_items( $num = 3, $post_ids = true ) {
277
    if ( $post_ids ) {
278
        $args = array( 'post_type' => 'wpi_item', 'orderby' => 'rand', 'post_count' => $num, 'fields' => 'ids' );
279
    } else {
280
        $args = array( 'post_type' => 'wpi_item', 'orderby' => 'rand', 'post_count' => $num );
281
    }
282
    
283
    $args  = apply_filters( 'wpinv_get_random_items', $args );
284
    
285
    return get_posts( $args );
286
}
287
288
function wpinv_get_item_token( $url = '' ) {
289
    $args    = array();
290
    $hash    = apply_filters( 'wpinv_get_url_token_algorithm', 'sha256' );
291
    $secret  = apply_filters( 'wpinv_get_url_token_secret', hash( $hash, wp_salt() ) );
292
293
    $parts   = parse_url( $url );
294
    $options = array();
295
296
    if ( isset( $parts['query'] ) ) {
297
        wp_parse_str( $parts['query'], $query_args );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
298
299
        if ( ! empty( $query_args['o'] ) ) {
300
            $options = explode( ':', rawurldecode( $query_args['o'] ) );
301
302
            if ( in_array( 'ip', $options ) ) {
303
                $args['ip'] = wpinv_get_ip();
304
            }
305
306
            if ( in_array( 'ua', $options ) ) {
307
                $ua = wpinv_get_user_agent();
308
                $args['user_agent'] = rawurlencode( $ua );
309
            }
310
        }
311
    }
312
313
    $args = apply_filters( 'wpinv_get_url_token_args', $args, $url, $options );
314
315
    $args['secret'] = $secret;
316
    $args['token']  = false;
317
318
    $url   = add_query_arg( $args, $url );
319
    $parts = parse_url( $url );
320
321
    if ( ! isset( $parts['path'] ) ) {
322
        $parts['path'] = '';
323
    }
324
325
    $token = md5( $parts['path'] . '?' . $parts['query'] );
326
327
    return $token;
328
}
329
330
function wpinv_validate_url_token( $url = '' ) {
331
    $ret   = false;
332
    $parts = parse_url( $url );
333
334
    if ( isset( $parts['query'] ) ) {
335
        wp_parse_str( $parts['query'], $query_args );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
336
337
        $allowed = apply_filters( 'wpinv_url_token_allowed_params', array(
338
            'item',
339
            'ttl',
340
            'token'
341
        ) );
342
343
        $remove = array();
344
345
        foreach( $query_args as $key => $value ) {
346
            if( false === in_array( $key, $allowed ) ) {
347
                $remove[] = $key;
348
            }
349
        }
350
351
        if( ! empty( $remove ) ) {
352
            $url = remove_query_arg( $remove, $url );
353
        }
354
355
        if ( isset( $query_args['ttl'] ) && current_time( 'timestamp' ) > $query_args['ttl'] ) {
356
            wp_die( apply_filters( 'wpinv_item_link_expired_text', __( 'Sorry but your item link has expired.', 'invoicing' ) ), __( 'Error', 'invoicing' ), array( 'response' => 403 ) );
357
        }
358
359
        if ( isset( $query_args['token'] ) && $query_args['token'] == wpinv_get_item_token( $url ) ) {
360
            $ret = true;
361
        }
362
363
    }
364
365
    return apply_filters( 'wpinv_validate_url_token', $ret, $url, $query_args );
366
}
367
368
function wpinv_item_in_cart( $item_id = 0, $options = array() ) {
369
    $cart_items = wpinv_get_cart_contents();
370
371
    $ret = false;
372
373
    if ( is_array( $cart_items ) ) {
374
        foreach ( $cart_items as $item ) {
375
            if ( $item['id'] == $item_id ) {
376
                $ret = true;
377
                break;
378
            }
379
        }
380
    }
381
382
    return (bool) apply_filters( 'wpinv_item_in_cart', $ret, $item_id, $options );
383
}
384
385
function wpinv_get_cart_item_tax( $item_id = 0, $subtotal = '', $options = array() ) {
386
    $tax = 0;
387
    if ( ! wpinv_item_is_tax_exclusive( $item_id ) ) {
388
        $country = !empty( $_POST['country'] ) ? $_POST['country'] : false;
389
        $state   = isset( $_POST['state'] ) ? $_POST['state'] : '';
390
391
        $tax = wpinv_calculate_tax( $subtotal, $country, $state, $item_id );
392
    }
393
394
    return apply_filters( 'wpinv_get_cart_item_tax', $tax, $item_id, $subtotal, $options );
395
}
396
397
function wpinv_cart_item_price( $item ) {
398
    $use_taxes  = wpinv_use_taxes();
0 ignored issues
show
Unused Code introduced by
$use_taxes is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
399
    $item_id    = isset( $item['id'] ) ? $item['id'] : 0;
400
    $price      = isset( $item['item_price'] ) ? wpinv_round_amount( $item['item_price'] ) : 0;
401
    $options    = isset( $item['options'] ) ? $item['options'] : array();
402
    $price_id   = isset( $options['price_id'] ) ? $options['price_id'] : false;
403
    $tax        = wpinv_price( wpinv_format_amount( $item['tax'] ) );
404
    
405
    if ( !wpinv_is_free_item( $item_id, $price_id ) && !wpinv_item_is_tax_exclusive( $item_id ) ) {
0 ignored issues
show
Unused Code introduced by
The call to wpinv_is_free_item() has too many arguments starting with $price_id.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
406
        if ( wpinv_prices_show_tax_on_checkout() && !wpinv_prices_include_tax() ) {
407
            $price += $tax;
408
        }
409
        
410
        if( !wpinv_prices_show_tax_on_checkout() && wpinv_prices_include_tax() ) {
411
            $price -= $tax;
412
        }        
413
    }
414
415
    $price = wpinv_price( wpinv_format_amount( $price ) );
416
417
    return apply_filters( 'wpinv_cart_item_price_label', $price, $item );
418
}
419
420
function wpinv_cart_item_subtotal( $item ) {
421
    $subtotal   = isset( $item['subtotal'] ) ? $item['subtotal'] : 0;
422
    $subtotal   = wpinv_price( wpinv_format_amount( $subtotal ) );
423
424
    return apply_filters( 'wpinv_cart_item_subtotal_label', $subtotal, $item );
425
}
426
427
function wpinv_cart_item_tax( $item ) {
428
    $tax        = '';
429
    $tax_rate   = '';
430
    
431 View Code Duplication
    if ( isset( $item['tax'] ) && $item['tax'] > 0 && $item['subtotal'] > 0 ) {
432
        $tax      = wpinv_price( wpinv_format_amount( $item['tax'] ) );
433
        $tax_rate = !empty( $item['vat_rate'] ) ? $item['vat_rate'] : ( $item['tax'] / $item['subtotal'] ) * 100;
434
        $tax_rate = $tax_rate > 0 ? (float)wpinv_round_amount( $tax_rate, 4 ) : '';
435
        $tax_rate = $tax_rate != '' ? ' <small class="tax-rate normal small">(' . $tax_rate . '%)</small>' : '';
436
    }
437
    
438
    $tax        = $tax . $tax_rate;
439
    
440
    if ( $tax === '' ) {
441
        $tax = 0; // Zero tax
442
    }
443
444
    return apply_filters( 'wpinv_cart_item_tax_label', $tax, $item );
445
}
446
447
function wpinv_get_cart_item_price( $item_id = 0, $cart_item = array(), $options = array(), $remove_tax_from_inclusive = false ) {
448
    $price = 0;
449
    
450
    // Set custom price
451
    if ( isset( $cart_item['custom_price'] ) && $cart_item['custom_price'] !== '' ) {
452
        $price = $cart_item['custom_price'];
453
    } else {
454
        $variable_prices = wpinv_has_variable_prices( $item_id );
455
456
        if ( $variable_prices ) {
457
            $prices = wpinv_get_variable_prices( $item_id );
458
459
            if ( $prices ) {
460
                if( ! empty( $options ) ) {
461
                    $price = isset( $prices[ $options['price_id'] ] ) ? $prices[ $options['price_id'] ]['amount'] : false;
462
                } else {
463
                    $price = false;
464
                }
465
            }
466
        }
467
468
        if( ! $variable_prices || false === $price ) {
469
            // Get the standard Item price if not using variable prices
470
            $price = wpinv_get_item_price( $item_id );
471
        }
472
    }
473
474
    if ( $remove_tax_from_inclusive && wpinv_prices_include_tax() ) {
475
        $price -= wpinv_get_cart_item_tax( $item_id, $price, $options );
476
    }
477
478
    return apply_filters( 'wpinv_cart_item_price', $price, $item_id, $cart_item, $options, $remove_tax_from_inclusive );
479
}
480
481
function wpinv_get_cart_item_price_id( $item = array() ) {
482
    if( isset( $item['item_number'] ) ) {
483
        $price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null;
484
    } else {
485
        $price_id = isset( $item['options']['price_id'] ) ? $item['options']['price_id'] : null;
486
    }
487
    return $price_id;
488
}
489
490
function wpinv_get_cart_item_price_name( $item = array() ) {
491
    $price_id = (int)wpinv_get_cart_item_price_id( $item );
492
    $prices   = wpinv_get_variable_prices( $item['id'] );
493
    $name     = ! empty( $prices[ $price_id ] ) ? $prices[ $price_id ]['name'] : '';
494
    return apply_filters( 'wpinv_get_cart_item_price_name', $name, $item['id'], $price_id, $item );
495
}
496
497
function wpinv_get_cart_item_name( $item = array() ) {
498
    $item_title = !empty( $item['name'] ) ? $item['name'] : get_the_title( $item['id'] );
499
500
    if ( empty( $item_title ) ) {
501
        $item_title = $item['id'];
502
    }
503
504
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
505
    if ( wpinv_has_variable_prices( $item['id'] ) && false !== wpinv_get_cart_item_price_id( $item ) ) {
506
        $item_title .= ' - ' . wpinv_get_cart_item_price_name( $item );
507
    }
508
    */
509
510
    return apply_filters( 'wpinv_get_cart_item_name', $item_title, $item['id'], $item );
511
}
512
513
function wpinv_has_variable_prices( $item_id = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $item_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
514
    return false;
515
}
516
517
function wpinv_get_item_position_in_cart( $item_id = 0, $options = array() ) {
518
    $cart_items = wpinv_get_cart_contents();
519
520
    if ( !is_array( $cart_items ) ) {
521
        return false; // Empty cart
522
    } else {
523
        foreach ( $cart_items as $position => $item ) {
524
            if ( $item['id'] == $item_id ) {
525
                if ( isset( $options['price_id'] ) && isset( $item['options']['price_id'] ) ) {
526
                    if ( (int) $options['price_id'] == (int) $item['options']['price_id'] ) {
527
                        return $position;
528
                    }
529
                } else {
530
                    return $position;
531
                }
532
            }
533
        }
534
    }
535
536
    return false; // Not found
537
}
538
539
function wpinv_get_cart_item_quantity( $item ) {
540
    if ( wpinv_item_quantities_enabled() ) {
541
        $quantity = !empty( $item['quantity'] ) && (int)$item['quantity'] > 0 ? absint( $item['quantity'] ) : 1;
542
    } else {
543
        $quantity = 1;
544
    }
545
    
546
    if ( $quantity < 1 ) {
547
        $quantity = 1;
548
    }
549
    
550
    return apply_filters( 'wpinv_get_cart_item_quantity', $quantity, $item );
551
}
552
553
function wpinv_get_item_suffix( $item, $html = true ) {
554
    if ( empty( $item ) ) {
555
        return NULL;
556
    }
557
    
558
    if ( is_int( $item ) ) {
559
        $item = new WPInv_Item( $item );
0 ignored issues
show
Documentation introduced by
$item is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
560
    }
561
    
562
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
563
        return NULL;
564
    }
565
    
566
    $suffix = $item->is_recurring() ? ' <span class="wpi-suffix">' . __( '(r)', 'invoicing' ) . '</span>' : '';
567
    
568
    if ( !$html && $suffix ) {
569
        $suffix = strip_tags( $suffix );
570
    }
571
    
572
    return apply_filters( 'wpinv_get_item_suffix', $suffix, $item, $html );
573
}
574
575
function wpinv_remove_item( $item = 0, $force_delete = false ) {
576
    if ( empty( $item ) ) {
577
        return NULL;
578
    }
579
    
580
    if ( is_int( $item ) ) {
581
        $item = new WPInv_Item( $item );
0 ignored issues
show
Documentation introduced by
$item is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
582
    }
583
    
584
    if ( !( is_object( $item ) && is_a( $item, 'WPInv_Item' ) ) ) {
585
        return NULL;
586
    }
587
    
588
    do_action( 'wpinv_pre_delete_item', $item );
589
590
    wp_delete_post( $item->ID, $force_delete );
591
592
    do_action( 'wpinv_post_delete_item', $item );
593
}
594
595
function wpinv_can_delete_item( $post_id ) {
596
    $return = current_user_can( 'manage_options' ) ? true : false;
597
    
598
    if ( $return && wpinv_item_in_use( $post_id ) ) {
599
        $return = false; // Don't delete item already use in invoices.
600
    }
601
    
602
    return apply_filters( 'wpinv_can_delete_item', $return, $post_id );
603
}
604
605
function wpinv_admin_action_delete() {
606
    $screen = get_current_screen();
607
    
608
    if ( !empty( $screen->post_type ) && $screen->post_type == 'wpi_item' && !empty( $_REQUEST['post'] ) && is_array( $_REQUEST['post'] ) ) {
609
        $post_ids = array();
610
        
611
        foreach ( $_REQUEST['post'] as $post_id ) {
612
            if ( !wpinv_can_delete_item( $post_id ) ) {
613
                continue;
614
            }
615
            
616
            $post_ids[] = $post_id;
617
        }
618
        
619
        $_REQUEST['post'] = $post_ids;
620
    }
621
}
622
add_action( 'admin_action_trash', 'wpinv_admin_action_delete', -10 );
623
add_action( 'admin_action_delete', 'wpinv_admin_action_delete', -10 );
624
625
function wpinv_check_delete_item( $check, $post, $force_delete ) {
626
    if ( $post->post_type == 'wpi_item' ) {
627
        if ( $force_delete && !wpinv_can_delete_item( $post->ID ) ) {
628
            return true;
629
        }
630
    }
631
    
632
    return $check;
633
}
634
add_filter( 'pre_delete_post', 'wpinv_check_delete_item', 10, 3 );
635
636
function wpinv_item_in_use( $item_id ) {
637
    global $wpdb, $wpi_items_in_use;
638
    
639
    if ( !$item_id > 0 ) {
640
        return false;
641
    }
642
    
643
    if ( !empty( $wpi_items_in_use ) ) {
644
        if ( isset( $wpi_items_in_use[$item_id] ) ) {
645
            return $wpi_items_in_use[$item_id];
646
        }
647
    } else {
648
        $wpi_items_in_use = array();
649
    }
650
    
651
    $statuses   = array_keys( wpinv_get_invoice_statuses( true ) );
652
    
653
    $query  = "SELECT p.ID FROM " . $wpdb->posts . " AS p INNER JOIN " . $wpdb->postmeta . " AS pm ON p.ID = pm.post_id WHERE p.post_type = 'wpi_invoice' AND p.post_status IN( '" . implode( "','", $statuses ) . "' ) AND pm.meta_key = '_wpinv_item_ids' AND FIND_IN_SET( '" . (int)$item_id . "', pm.meta_value )";
654
    $in_use = $wpdb->get_var( $query ) > 0 ? true : false;
655
    
656
    $wpi_items_in_use[$item_id] = $in_use;
657
    
658
    return $in_use;
659
}
660
661
function wpinv_create_item( $args = array(), $wp_error = false, $force_update = false ) {
662
    // Set some defaults
663
    $defaults = array(
664
        'type'                 => 'custom',                                                // Optional. Item type. Default 'custom'.
665
        'title'                => '',                                                      // Required. Item title.
666
        'custom_id'            => 0,                                                       // Optional. Any integer or non numeric id. Must be unique within item type.
667
        'price'                => '0.00',                                                  // Optional. Item price. Default '0.00'.
668
        'status'               => 'pending',                                               // Optional. pending, publish
669
        'custom_name'          => '',                                                      // Optional. Plural sub title for item.
670
        'custom_singular_name' => '',                                                      // Optional. Singular sub title for item.
671
        'vat_rule'             => 'digital',                                               // Optional. digital => Digital item, physical => Physical item
672
        'editable'             => true,                                                    // Optional. Item editable from Items list page? Default true.
673
        'excerpt'              => '',                                                      // Optional. Item short description
674
        /* Recurring item fields */
675
        'is_recurring'         => 0,                                                       // Optional. 1 => Allow recurring or 0 => Don't allow recurring
676
        'recurring_period'     => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
677
        'recurring_interval'   => 0,                                                       // Optional. Integer value between 1 - 90.
678
        'recurring_limit'      => 0,                                                       // Optional. Any integer number. 0 for recurring forever until cancelled.
679
        'free_trial'           => 0,                                                       // Optional. 1 => Allow free trial or 0 => Don't free trial
680
        'trial_period'         => 'M',                                                     // Optional. D => Daily, W => Weekly, M => Monthly, Y => Yearly
681
        'trial_interval'       => 0,                                                       // Optional. Any integer number.
682
    );
683
684
    $data = wp_parse_args( $args, $defaults );
685
686
    if ( empty( $data['type'] ) ) {
687
        $data['type'] = 'custom';
688
    }
689
690
    if ( !empty( $data['custom_id'] ) ) {
691
        $item = wpinv_get_item_by( 'custom_id', $data['custom_id'], $data['type'] );
692
    } else {
693
        $item = NULL;
694
    }
695
696
    if ( !empty( $item ) ) {
697
        if ( $force_update ) {
698
            if ( empty( $args['ID'] ) ) {
699
                $args['ID'] = $item->ID;
700
            }
701
            return wpinv_update_item( $args, $wp_error );
702
        }
703
704
        return $item;
705
    }
706
707
    $meta                           = array();
708
    $meta['type']                   = $data['type'];
709
    $meta['custom_id']              = $data['custom_id'];
710
    $meta['custom_singular_name']   = $data['custom_singular_name'];
711
    $meta['custom_name']            = $data['custom_name'];
712
    $meta['price']                  = wpinv_round_amount( $data['price'] );
713
    $meta['editable']               = (int)$data['editable'];
714
    $meta['vat_rule']               = $data['vat_rule'];
715
    $meta['vat_class']              = '_standard';
716
    
717
    if ( !empty( $data['is_recurring'] ) ) {
718
        $meta['is_recurring']       = $data['is_recurring'];
719
        $meta['recurring_period']   = $data['recurring_period'];
720
        $meta['recurring_interval'] = absint( $data['recurring_interval'] );
721
        $meta['recurring_limit']    = absint( $data['recurring_limit'] );
722
        $meta['free_trial']         = $data['free_trial'];
723
        $meta['trial_period']       = $data['trial_period'];
724
        $meta['trial_interval']     = absint( $data['trial_interval'] );
725 View Code Duplication
    } else {
726
        $meta['is_recurring']       = 0;
727
        $meta['recurring_period']   = '';
728
        $meta['recurring_interval'] = '';
729
        $meta['recurring_limit']    = '';
730
        $meta['free_trial']         = 0;
731
        $meta['trial_period']       = '';
732
        $meta['trial_interval']     = '';
733
    }
734
    
735
    $post_data  = array( 
736
        'post_title'    => $data['title'],
737
        'post_excerpt'  => $data['excerpt'],
738
        'post_status'   => $data['status'],
739
        'meta'          => $meta
740
    );
741
742
    $item = new WPInv_Item();
743
    $return = $item->create( $post_data, $wp_error );
744
745
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
746
        return $item;
747
    }
748
749
    if ( $wp_error && is_wp_error( $return ) ) {
750
        return $return;
751
    }
752
    return 0;
753
}
754
755
function wpinv_update_item( $args = array(), $wp_error = false ) {
756
    $item = !empty( $args['ID'] ) ? new WPInv_Item( $args['ID'] ) : NULL;
757
758 View Code Duplication
    if ( empty( $item ) || !( !empty( $item->post_type ) && $item->post_type == 'wpi_item' ) ) {
0 ignored issues
show
Documentation introduced by
The property post_type does not exist on object<WPInv_Item>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
759
        if ( $wp_error ) {
760
            return new WP_Error( 'wpinv_invalid_item', __( 'Invalid item.', 'invoicing' ) );
761
        }
762
        return 0;
763
    }
764
    
765
    if ( !empty( $args['custom_id'] ) ) {
766
        $item_exists = wpinv_get_item_by( 'custom_id', $args['custom_id'], ( !empty( $args['type'] ) ? $args['type'] : $item->type ) );
0 ignored issues
show
Documentation introduced by
The property $type is declared private in WPInv_Item. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
767
        
768 View Code Duplication
        if ( !empty( $item_exists ) && $item_exists->ID != $args['ID'] ) {
769
            if ( $wp_error ) {
770
                return new WP_Error( 'wpinv_invalid_custom_id', __( 'Item with custom id already exists.', 'invoicing' ) );
771
            }
772
            return 0;
773
        }
774
    }
775
776
    $meta_fields = array( 'type', 'custom_id', 'custom_singular_name', 'custom_name', 'price', 'editable', 'vat_rule', 'vat_class', 'is_recurring', 'recurring_period', 'recurring_interval', 'recurring_limit', 'free_trial', 'trial_period', 'trial_interval' );
777
778
    $post_data = array();
779
    if ( isset( $args['title'] ) ) { 
780
        $post_data['post_title'] = $args['title'];
781
    }
782
    if ( isset( $args['excerpt'] ) ) { 
783
        $post_data['post_excerpt'] = $args['excerpt'];
784
    }
785
    if ( isset( $args['status'] ) ) { 
786
        $post_data['post_status'] = $args['status'];
787
    }
788
    
789
    foreach ( $meta_fields as $meta_field ) {
790
        if ( isset( $args[ $meta_field ] ) ) { 
791
            $value = $args[ $meta_field ];
792
793
            switch ( $meta_field ) {
794
                case 'price':
795
                    $value = wpinv_round_amount( $value );
796
                break;
797
                case 'recurring_interval':
798
                case 'recurring_limit':
799
                case 'trial_interval':
800
                    $value = absint( $value );
801
                break;
802
            }
803
804
            $post_data['meta'][ $meta_field ] = $value;
805
        };
806
    }
807
808
    if ( empty( $post_data ) ) {
809
        if ( $wp_error ) {
810
            return new WP_Error( 'wpinv_invalid_item_data', __( 'Invalid item data.', 'invoicing' ) );
811
        }
812
        return 0;
813
    }
814
    $post_data['ID'] = $args['ID'];
815
816
    $return = $item->update( $post_data, $wp_error );
817
818
    if ( $return && !empty( $item ) && !is_wp_error( $return ) ) {
819
        return $item;
820
    }
821
822
    if ( $wp_error && is_wp_error( $return ) ) {
823
        return $return;
824
    }
825
    return 0;
826
}