Passed
Push — master ( ece56d...190d73 )
by Brian
09:21 queued 04:12
created

getpaid_array_merge_if_empty()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 4
c 1
b 0
f 1
dl 0
loc 11
rs 10
cc 4
nc 3
nop 2
1
<?php
2
/**
3
 * Contains helper functions.
4
 *
5
 * @since 1.0.0
6
 * @package Invoicing
7
 */
8
 
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Are we supporting item quantities?
13
 */
14
function wpinv_item_quantities_enabled() {
15
    return true;
16
}
17
18
/**
19
 * Returns the user's ip address.
20
 */
21
function wpinv_get_ip() {
22
23
    if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) {
24
        return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) );
0 ignored issues
show
Bug introduced by
It seems like wp_unslash($_SERVER['HTTP_X_REAL_IP']) can also be of type string[]; however, parameter $str of sanitize_text_field() does only seem to accept 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

24
        return sanitize_text_field( /** @scrutinizer ignore-type */ wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) );
Loading history...
25
    }
26
27
    if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
28
        // Proxy servers can send through this header like this: X-Forwarded-For: client1, proxy1, proxy2
29
        // Make sure we always only send through the first IP in the list which should always be the client IP.
30
        return (string) rest_is_ip_address( trim( current( preg_split( '/,/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) );
0 ignored issues
show
Bug introduced by
It seems like preg_split('/,/', saniti...TP_X_FORWARDED_FOR']))) can also be of type false; however, parameter $array of current() 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

30
        return (string) rest_is_ip_address( trim( current( /** @scrutinizer ignore-type */ preg_split( '/,/', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ) ) ) );
Loading history...
31
    }
32
33
    if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
34
        return sanitize_text_field( wp_unslash( $_SERVER['HTTP_CLIENT_IP'] ) );
35
    }
36
37
    if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
38
        return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
39
    }
40
41
    return '';
42
}
43
44
function wpinv_get_user_agent() {
45
    if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
46
        $user_agent = sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] );
47
    } else {
48
        $user_agent = '';
49
    }
50
51
    return apply_filters( 'wpinv_get_user_agent', $user_agent );
52
}
53
54
/**
55
 * Sanitizes an amount.
56
 * 
57
 * @param string $amount The amount to sanitize.
58
 */
59
function wpinv_sanitize_amount( $amount ) {
60
61
    // Format decimals.
62
    $amount = str_replace( wpinv_decimal_separator(), '.', $amount );
63
64
    // Remove thousands.
65
    $amount = str_replace( wpinv_thousands_separator(), '', $amount );
66
67
    // Cast the remaining to a float.
68
    return (float) preg_replace( '/[^0-9\.\-]/', '', $amount );
69
70
}
71
72
function wpinv_round_amount( $amount, $decimals = NULL ) {
73
    if ( $decimals === NULL ) {
74
        $decimals = wpinv_decimals();
75
    }
76
    
77
    $amount = round( (double)$amount, wpinv_currency_decimal_filter( absint( $decimals ) ) );
78
79
    return apply_filters( 'wpinv_round_amount', $amount, $decimals );
80
}
81
82
/**
83
 * Get all invoice statuses.
84
 *
85
 * @since 1.0.19
86
 * @param bool $draft Whether or not to include the draft status.
87
 * @param bool $trashed Whether or not to include the trash status.
88
 * @param string|WPInv_Invoice $invoice The invoice object|post type|type
89
 * @return array
90
 */
91
function wpinv_get_invoice_statuses( $draft = false, $trashed = false, $invoice = false ) {
92
93
	$invoice_statuses = array(
94
		'wpi-pending'    => _x( 'Pending payment', 'Invoice status', 'invoicing' ),
95
        'publish'        => _x( 'Paid', 'Invoice status', 'invoicing' ),
96
        'wpi-processing' => _x( 'Processing', 'Invoice status', 'invoicing' ),
97
		'wpi-onhold'     => _x( 'On hold', 'Invoice status', 'invoicing' ),
98
		'wpi-cancelled'  => _x( 'Cancelled', 'Invoice status', 'invoicing' ),
99
		'wpi-refunded'   => _x( 'Refunded', 'Invoice status', 'invoicing' ),
100
        'wpi-failed'     => _x( 'Failed', 'Invoice status', 'invoicing' ),
101
        'wpi-renewal'    => _x( 'Renewal Payment', 'Invoice status', 'invoicing' ),
102
    );
103
104
    if ( $draft ) {
105
        $invoice_statuses['draft'] = __( 'Draft', 'invoicing' );
106
    }
107
108
    if ( $trashed ) {
109
        $invoice_statuses['trash'] = __( 'Trash', 'invoicing' );
110
    }
111
112
    if ( $invoice instanceof WPInv_Invoice ) {
113
        $invoice = $invoice->get_post_type();
114
    }
115
116
	return apply_filters( 'wpinv_statuses', $invoice_statuses, $invoice );
117
}
118
119
/**
120
 * Returns the formated invoice status.
121
 * 
122
 * @param string $status The raw status
123
 * @param string|WPInv_Invoice $invoice The invoice object|post type|type
124
 */
125
function wpinv_status_nicename( $status, $invoice = false ) {
126
    $statuses = wpinv_get_invoice_statuses( true, true, $invoice );
0 ignored issues
show
Bug introduced by
It seems like $invoice can also be of type false; however, parameter $invoice of wpinv_get_invoice_statuses() does only seem to accept WPInv_Invoice|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

126
    $statuses = wpinv_get_invoice_statuses( true, true, /** @scrutinizer ignore-type */ $invoice );
Loading history...
127
    $status   = isset( $statuses[$status] ) ? $statuses[$status] : $status;
128
129
    return sanitize_text_field( $status );
130
}
131
132
/**
133
 * Retrieves the default currency code.
134
 * 
135
 * @param string $current
136
 */
137
function wpinv_get_currency( $current = '' ) {
138
139
    if ( empty( $current ) ) {
140
        $current = apply_filters( 'wpinv_currency', wpinv_get_option( 'currency', 'USD' ) );
141
    }
142
143
    return trim( strtoupper( $current ) );
144
}
145
146
/**
147
 * Given a currency, it returns a currency symbol.
148
 * 
149
 * @param string|null $currency The currency code. Defaults to the default currency.
150
 */
151
function wpinv_currency_symbol( $currency = null ) {
152
153
    // Prepare the currency.
154
    $currency = empty( $currency ) ? wpinv_get_currency() : wpinv_clean( $currency );
155
156
    // Fetch all symbols.
157
    $symbols = wpinv_get_currency_symbols();
158
159
    // Fetch this currencies symbol.
160
    $currency_symbol = isset( $symbols[$currency] ) ? $symbols[$currency] : $currency;
161
162
    // Filter the symbol.
163
    return apply_filters( 'wpinv_currency_symbol', $currency_symbol, $currency );
164
}
165
166
function wpinv_currency_position() {
167
    $position = wpinv_get_option( 'currency_position', 'left' );
168
    
169
    return apply_filters( 'wpinv_currency_position', $position );
170
}
171
172
/**
173
 * Returns the thousands separator for a currency.
174
 * 
175
 * @param $string|null $current
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string|null at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string|null.
Loading history...
176
 */
177
function wpinv_thousands_separator( $current = null ) {
178
179
    if ( null == $current ) {
180
        $current = wpinv_get_option( 'thousands_separator', '.' );
181
    }
182
183
    return trim( $current );
184
}
185
186
/**
187
 * Returns the decimal separator for a currency.
188
 * 
189
 * @param $string|null $current
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string|null at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string|null.
Loading history...
190
 */
191
function wpinv_decimal_separator( $current = null ) {
192
193
    if ( null == $current ) {
194
        $current = wpinv_get_option( 'decimal_separator', '.' );
195
    }
196
    
197
    return trim( $current );
198
}
199
200
/**
201
 * Returns the number of decimals to use.
202
 * 
203
 * @param $string|null $current
0 ignored issues
show
Documentation Bug introduced by
The doc comment $string|null at position 0 could not be parsed: Unknown type name '$string' at position 0 in $string|null.
Loading history...
204
 */
205
function wpinv_decimals( $current = null ) {
206
207
    if ( null == $current ) {
208
        $current = wpinv_get_option( 'decimals', 2 );
209
    }
210
    
211
    return absint( $current );
212
}
213
214
/**
215
 * Retrieves a list of all supported currencies.
216
 */
217
function wpinv_get_currencies() {
218
    return apply_filters( 'wpinv_currencies', wpinv_get_data( 'currencies' ) );
219
}
220
221
/**
222
 * Retrieves a list of all currency symbols.
223
 */
224
function wpinv_get_currency_symbols() {
225
    return apply_filters( 'wpinv_currency_symbols', wpinv_get_data( 'currency-symbols' ) );
226
}
227
228
/**
229
 * Get the price format depending on the currency position.
230
 *
231
 * @return string
232
 */
233
function getpaid_get_price_format() {
234
	$currency_pos = wpinv_currency_position();
235
	$format       = '%1$s%2$s';
236
237
	switch ( $currency_pos ) {
238
		case 'left':
239
			$format = '%1$s%2$s';
240
			break;
241
		case 'right':
242
			$format = '%2$s%1$s';
243
			break;
244
		case 'left_space':
245
			$format = '%1$s&nbsp;%2$s';
246
			break;
247
		case 'right_space':
248
			$format = '%2$s&nbsp;%1$s';
249
			break;
250
	}
251
252
	return apply_filters( 'getpaid_price_format', $format, $currency_pos );
253
}
254
255
/**
256
 * Format the amount with a currency symbol.
257
 *
258
 * @param  float  $amount Raw price.
259
 * @param  string $currency Currency.
260
 * @return string
261
 */
262
function wpinv_price( $amount = 0, $currency = '' ) {
263
264
    // Backwards compatibility.
265
    $amount             = floatval( wpinv_sanitize_amount( $amount ) );
266
267
    // Prepare variables.
268
    $currency           = wpinv_get_currency( $currency );
269
    $amount             = (float) $amount;
270
    $unformatted_amount = $amount;
271
    $negative           = $amount < 0;
272
    $amount             = apply_filters( 'getpaid_raw_amount', floatval( $negative ? $amount * -1 : $amount ) );
273
    $amount             = wpinv_format_amount( $amount );
274
275
    // Format the amount.
276
    $format             = getpaid_get_price_format();
277
    $formatted_amount   = ( $negative ? '-' : '' ) . sprintf( $format, '<span class="getpaid-currency__symbol">' . wpinv_currency_symbol( $currency ) . '</span>', $amount );
278
279
    // Filter the formatting.
280
    return apply_filters( 'wpinv_price', $formatted_amount, $amount, $currency, $unformatted_amount );
281
}
282
283
/**
284
 * Format an amount with separators.
285
 *
286
 * @param  float    $amount Raw amount.
287
 * @param  null|int $decimals Number of decimals to use.
288
 * @param  bool     $calculate Whether or not to apply separators.
289
 * @return string
290
 */
291
function wpinv_format_amount( $amount, $decimals = null, $calculate = false ) {
292
    $thousands_sep = wpinv_thousands_separator();
293
    $decimal_sep   = wpinv_decimal_separator();
294
    $decimals      = wpinv_decimals( $decimals );
295
296
    // Format decimals.
297
    $amount = str_replace( $decimal_sep, '.', $amount );
298
299
    // Remove thousands.
300
    $amount = str_replace( $thousands_sep, '', $amount );
301
302
    // Cast the remaining to a float.
303
    $amount = floatval( $amount );
304
305
    if ( $calculate ) {
306
        return $amount;
307
    }
308
309
    // Fomart the amount.
310
    return number_format( $amount, $decimals, $decimal_sep, $thousands_sep );
311
}
312
313
function wpinv_sanitize_key( $key ) {
314
    $raw_key = $key;
315
    $key = preg_replace( '/[^a-zA-Z0-9_\-\.\:\/]/', '', $key );
316
317
    return apply_filters( 'wpinv_sanitize_key', $key, $raw_key );
318
}
319
320
/**
321
 * Returns a file extesion.
322
 * 
323
 * @param $str the file whose extension should be retrieved.
324
 */
325
function wpinv_get_file_extension( $str ) {
326
    $filetype = wp_check_filetype( $str );
327
    return $filetype['ext'];
328
}
329
330
/**
331
 * Checks if a given string is an image URL.
332
 * 
333
 * @param string $string
334
 */
335
function wpinv_string_is_image_url( $string ) {
336
    $extension = strtolower( wpinv_get_file_extension( $string ) );
337
    return in_array( $extension, array( 'jpeg', 'jpg', 'png', 'gif', 'ico' ), true );
338
}
339
340
/**
341
 * Returns the current URL.
342
 */
343
function wpinv_get_current_page_url() {
344
    return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
345
}
346
347
/**
348
 * Define a constant if it is not already defined.
349
 *
350
 * @since 1.0.19
351
 * @param string $name  Constant name.
352
 * @param mixed  $value Value.
353
 */
354
function getpaid_maybe_define_constant( $name, $value ) {
355
	if ( ! defined( $name ) ) {
356
		define( $name, $value );
357
	}
358
}
359
360
function wpinv_get_php_arg_separator_output() {
361
	return ini_get( 'arg_separator.output' );
362
}
363
364
function wpinv_rgb_from_hex( $color ) {
365
    $color = str_replace( '#', '', $color );
366
367
    // Convert shorthand colors to full format, e.g. "FFF" -> "FFFFFF"
368
    $color = preg_replace( '~^(.)(.)(.)$~', '$1$1$2$2$3$3', $color );
369
    if ( empty( $color ) ) {
370
        return NULL;
371
    }
372
373
    $color = str_split( $color );
374
375
    $rgb      = array();
376
    $rgb['R'] = hexdec( $color[0] . $color[1] );
377
    $rgb['G'] = hexdec( $color[2] . $color[3] );
378
    $rgb['B'] = hexdec( $color[4] . $color[5] );
379
380
    return $rgb;
381
}
382
383
function wpinv_hex_darker( $color, $factor = 30 ) {
384
    $base  = wpinv_rgb_from_hex( $color );
385
    $color = '#';
386
387
    foreach ( $base as $k => $v ) {
388
        $amount      = $v / 100;
389
        $amount      = round( $amount * $factor );
390
        $new_decimal = $v - $amount;
391
392
        $new_hex_component = dechex( $new_decimal );
0 ignored issues
show
Bug introduced by
$new_decimal of type double is incompatible with the type integer expected by parameter $number of dechex(). ( Ignorable by Annotation )

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

392
        $new_hex_component = dechex( /** @scrutinizer ignore-type */ $new_decimal );
Loading history...
393
        if ( strlen( $new_hex_component ) < 2 ) {
394
            $new_hex_component = "0" . $new_hex_component;
395
        }
396
        $color .= $new_hex_component;
397
    }
398
399
    return $color;
400
}
401
402
function wpinv_hex_lighter( $color, $factor = 30 ) {
403
    $base  = wpinv_rgb_from_hex( $color );
404
    $color = '#';
405
406
    foreach ( $base as $k => $v ) {
407
        $amount      = 255 - $v;
408
        $amount      = $amount / 100;
409
        $amount      = round( $amount * $factor );
410
        $new_decimal = $v + $amount;
411
412
        $new_hex_component = dechex( $new_decimal );
0 ignored issues
show
Bug introduced by
$new_decimal of type double is incompatible with the type integer expected by parameter $number of dechex(). ( Ignorable by Annotation )

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

412
        $new_hex_component = dechex( /** @scrutinizer ignore-type */ $new_decimal );
Loading history...
413
        if ( strlen( $new_hex_component ) < 2 ) {
414
            $new_hex_component = "0" . $new_hex_component;
415
        }
416
        $color .= $new_hex_component;
417
    }
418
419
    return $color;
420
}
421
422
function wpinv_light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) {
423
    $hex = str_replace( '#', '', $color );
424
425
    $c_r = hexdec( substr( $hex, 0, 2 ) );
426
    $c_g = hexdec( substr( $hex, 2, 2 ) );
427
    $c_b = hexdec( substr( $hex, 4, 2 ) );
428
429
    $brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
430
431
    return $brightness > 155 ? $dark : $light;
432
}
433
434
function wpinv_format_hex( $hex ) {
435
    $hex = trim( str_replace( '#', '', $hex ) );
436
437
    if ( strlen( $hex ) == 3 ) {
438
        $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
439
    }
440
441
    return $hex ? '#' . $hex : null;
442
}
443
444
/**
445
 * Get truncated string with specified width.
446
 *
447
 * @since 1.0.0
448
 *
449
 * @param string $str The string being decoded.
450
 * @param int $start The start position offset. Number of characters from the beginning of string.
451
 *                      For negative value, number of characters from the end of the string.
452
 * @param int $width The width of the desired trim. Negative widths count from the end of the string.
453
 * @param string $trimmaker A string that is added to the end of string when string is truncated. Ex: "...".
454
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
455
 * @return string
456
 */
457
function wpinv_utf8_strimwidth( $str, $start, $width, $trimmaker = '', $encoding = 'UTF-8' ) {
458
    if ( function_exists( 'mb_strimwidth' ) ) {
459
        return mb_strimwidth( $str, $start, $width, $trimmaker, $encoding );
460
    }
461
    
462
    return wpinv_utf8_substr( $str, $start, $width, $encoding ) . $trimmaker;
463
}
464
465
/**
466
 * Get the string length.
467
 *
468
 * @since 1.0.0
469
 *
470
 * @param string $str The string being checked for length. 
471
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
472
 * @return int Returns the number of characters in string.
473
 */
474
function wpinv_utf8_strlen( $str, $encoding = 'UTF-8' ) {
475
    if ( function_exists( 'mb_strlen' ) ) {
476
        return mb_strlen( $str, $encoding );
477
    }
478
        
479
    return strlen( $str );
480
}
481
482
function wpinv_utf8_strtolower( $str, $encoding = 'UTF-8' ) {
483
    if ( function_exists( 'mb_strtolower' ) ) {
484
        return mb_strtolower( $str, $encoding );
485
    }
486
    
487
    return strtolower( $str );
488
}
489
490
function wpinv_utf8_strtoupper( $str, $encoding = 'UTF-8' ) {
491
    if ( function_exists( 'mb_strtoupper' ) ) {
492
        return mb_strtoupper( $str, $encoding );
493
    }
494
    
495
    return strtoupper( $str );
496
}
497
498
/**
499
 * Find position of first occurrence of string in a string
500
 *
501
 * @since 1.0.0
502
 *
503
 * @param string $str The string being checked.
504
 * @param string $find The string to find in input string.
505
 * @param int $offset The search offset. Default "0". A negative offset counts from the end of the string.
506
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
507
 * @return int Returns the position of the first occurrence of search in the string.
508
 */
509
function wpinv_utf8_strpos( $str, $find, $offset = 0, $encoding = 'UTF-8' ) {
510
    if ( function_exists( 'mb_strpos' ) ) {
511
        return mb_strpos( $str, $find, $offset, $encoding );
512
    }
513
        
514
    return strpos( $str, $find, $offset );
515
}
516
517
/**
518
 * Find position of last occurrence of a string in a string.
519
 *
520
 * @since 1.0.0
521
 *
522
 * @param string $str The string being checked, for the last occurrence of search.
523
 * @param string $find The string to find in input string.
524
 * @param int $offset Specifies begin searching an arbitrary number of characters into the string.
525
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
526
 * @return int Returns the position of the last occurrence of search.
527
 */
528
function wpinv_utf8_strrpos( $str, $find, $offset = 0, $encoding = 'UTF-8' ) {
529
    if ( function_exists( 'mb_strrpos' ) ) {
530
        return mb_strrpos( $str, $find, $offset, $encoding );
531
    }
532
        
533
    return strrpos( $str, $find, $offset );
534
}
535
536
/**
537
 * Get the part of string.
538
 *
539
 * @since 1.0.0
540
 *
541
 * @param string $str The string to extract the substring from.
542
 * @param int $start If start is non-negative, the returned string will start at the entered position in string, counting from zero.
543
 *                      If start is negative, the returned string will start at the entered position from the end of string. 
544
 * @param int|null $length Maximum number of characters to use from string.
545
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
546
 * @return string
547
 */
548
function wpinv_utf8_substr( $str, $start, $length = null, $encoding = 'UTF-8' ) {
549
    if ( function_exists( 'mb_substr' ) ) {
550
        if ( $length === null ) {
551
            return mb_substr( $str, $start, wpinv_utf8_strlen( $str, $encoding ), $encoding );
552
        } else {
553
            return mb_substr( $str, $start, $length, $encoding );
554
        }
555
    }
556
        
557
    return substr( $str, $start, $length );
558
}
559
560
/**
561
 * Get the width of string.
562
 *
563
 * @since 1.0.0
564
 *
565
 * @param string $str The string being decoded.
566
 * @param string $encoding The encoding parameter is the character encoding. Default "UTF-8".
567
 * @return string The width of string.
568
 */
569
function wpinv_utf8_strwidth( $str, $encoding = 'UTF-8' ) {
570
    if ( function_exists( 'mb_strwidth' ) ) {
571
        return mb_strwidth( $str, $encoding );
572
    }
573
    
574
    return wpinv_utf8_strlen( $str, $encoding );
575
}
576
577
function wpinv_utf8_ucfirst( $str, $lower_str_end = false, $encoding = 'UTF-8' ) {
578
    if ( function_exists( 'mb_strlen' ) ) {
579
        $first_letter = wpinv_utf8_strtoupper( wpinv_utf8_substr( $str, 0, 1, $encoding ), $encoding );
580
        $str_end = "";
581
        
582
        if ( $lower_str_end ) {
583
            $str_end = wpinv_utf8_strtolower( wpinv_utf8_substr( $str, 1, wpinv_utf8_strlen( $str, $encoding ), $encoding ), $encoding );
584
        } else {
585
            $str_end = wpinv_utf8_substr( $str, 1, wpinv_utf8_strlen( $str, $encoding ), $encoding );
586
        }
587
588
        return $first_letter . $str_end;
589
    }
590
    
591
    return ucfirst( $str );
592
}
593
594
function wpinv_utf8_ucwords( $str, $encoding = 'UTF-8' ) {
595
    if ( function_exists( 'mb_convert_case' ) ) {
596
        return mb_convert_case( $str, MB_CASE_TITLE, $encoding );
597
    }
598
    
599
    return ucwords( $str );
600
}
601
602
function wpinv_period_in_days( $period, $unit ) {
603
    $period = absint( $period );
604
    
605
    if ( $period > 0 ) {
606
        if ( in_array( strtolower( $unit ), array( 'w', 'week', 'weeks' ) ) ) {
607
            $period = $period * 7;
608
        } else if ( in_array( strtolower( $unit ), array( 'm', 'month', 'months' ) ) ) {
609
            $period = $period * 30;
610
        } else if ( in_array( strtolower( $unit ), array( 'y', 'year', 'years' ) ) ) {
611
            $period = $period * 365;
612
        }
613
    }
614
    
615
    return $period;
616
}
617
618
function wpinv_cal_days_in_month( $calendar, $month, $year ) {
619
    if ( function_exists( 'cal_days_in_month' ) ) {
620
        return cal_days_in_month( $calendar, $month, $year );
621
    }
622
623
    // Fallback in case the calendar extension is not loaded in PHP
624
    // Only supports Gregorian calendar
625
    return date( 't', mktime( 0, 0, 0, $month, 1, $year ) );
626
}
627
628
/**
629
 * Display a help tip for settings.
630
 *
631
 * @param  string $tip Help tip text
632
 * @param  bool $allow_html Allow sanitized HTML if true or escape
633
 *
634
 * @return string
635
 */
636
function wpi_help_tip( $tip, $allow_html = false ) {
637
638
    if ( $allow_html ) {
639
        $tip = wpi_sanitize_tooltip( $tip );
640
    } else {
641
        $tip = esc_attr( $tip );
642
    }
643
644
    return '<span class="wpi-help-tip dashicons dashicons-editor-help" title="' . $tip . '"></span>';
645
}
646
647
/**
648
 * Sanitize a string destined to be a tooltip.
649
 *
650
 * Tooltips are encoded with htmlspecialchars to prevent XSS. Should not be used in conjunction with esc_attr()
651
 *
652
 * @param string $var
653
 * @return string
654
 */
655
function wpi_sanitize_tooltip( $var ) {
656
    return wp_kses( html_entity_decode( $var ), array(
657
        'br'     => array(),
658
        'em'     => array(),
659
        'strong' => array(),
660
        'b'      => array(),
661
        'small'  => array(),
662
        'span'   => array(),
663
        'ul'     => array(),
664
        'li'     => array(),
665
        'ol'     => array(),
666
        'p'      => array(),
667
    ) );
668
}
669
670
/**
671
 * Get all WPI screen ids.
672
 *
673
 * @return array
674
 */
675
function wpinv_get_screen_ids() {
676
677
    $screen_id = sanitize_title( __( 'Invoicing', 'invoicing' ) );
678
679
    $screen_ids = array(
680
        'toplevel_page_' . $screen_id,
681
        'wpi_invoice',
682
        'wpi_item',
683
        'wpi_quote',
684
        'wpi_discount',
685
        'edit-wpi_invoice',
686
        'edit-wpi_item',
687
        'edit-wpi_discount',
688
        'edit-wpi_quote',
689
        'invoicing_page_wpinv-settings',
690
        'invoicing_page_wpinv-subscriptions',
691
        'invoicing_page_wpinv-reports',
692
        'invoicing_page_wpi-addons',
693
    );
694
695
    return apply_filters( 'wpinv_screen_ids', $screen_ids );
696
}
697
698
/**
699
 * Cleans up an array, comma- or space-separated list of scalar values.
700
 *
701
 * @since 1.0.13
702
 *
703
 * @param array|string $list List of values.
704
 * @return array Sanitized array of values.
705
 */
706
function wpinv_parse_list( $list ) {
707
708
    if ( empty( $list ) ) {
709
        $list = array();
710
    }
711
712
	if ( ! is_array( $list ) ) {
713
		return preg_split( '/[\s,]+/', $list, -1, PREG_SPLIT_NO_EMPTY );
0 ignored issues
show
Bug Best Practice introduced by
The expression return preg_split('/[\s,...1, PREG_SPLIT_NO_EMPTY) could also return false which is incompatible with the documented return type array. 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...
714
	}
715
716
	return $list;
717
}
718
719
/**
720
 * Fetches data stored on disk.
721
 *
722
 * @since 1.0.14
723
 *
724
 * @param string $key Type of data to fetch.
725
 * @return mixed Fetched data.
726
 */
727
function wpinv_get_data( $key ) {
728
729
    // Try fetching it from the cache.
730
    $data = wp_cache_get( "wpinv-data-$key", 'wpinv' );
731
    if( $data ) {
732
        return $data;
733
    }
734
735
    $data = apply_filters( "wpinv_get_$key", include WPINV_PLUGIN_DIR . "includes/data/$key.php" );
736
	wp_cache_set( "wpinv-data-$key", $data, 'wpinv' );
737
738
	return $data;
739
}
740
741
/**
742
 * (Maybe) Adds an empty option to an array of options.
743
 *
744
 * @since 1.0.14
745
 *
746
 * @param array $options
747
 * @param bool $first_empty Whether or not the first item in the list should be empty
748
 * @return mixed Fetched data.
749
 */
750
function wpinv_maybe_add_empty_option( $options, $first_empty ) {
751
752
    if ( ! empty( $options ) && $first_empty ) {
753
        return array_merge( array( '' => '' ), $options );
754
    }
755
    return $options;
756
757
}
758
759
/**
760
 * Clean variables using sanitize_text_field.
761
 *
762
 * @param mixed $var Data to sanitize.
763
 * @return string|array
764
 */
765
function wpinv_clean( $var ) {
766
767
	if ( is_array( $var ) ) {
768
		return array_map( 'wpinv_clean', $var );
769
    }
770
771
    if ( is_object( $var ) ) {
772
		$object_vars = get_object_vars( $var );
773
		foreach ( $object_vars as $property_name => $property_value ) {
774
			$var->$property_name = wpinv_clean( $property_value );
775
        }
776
        return $var;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $var returns the type object which is incompatible with the documented return type array|string.
Loading history...
777
	}
778
    
779
    return is_string( $var ) ? sanitize_text_field( $var ) : $var;
780
}
781
782
/**
783
 * Converts a price string into an options array.
784
 *
785
 * @param string $str Data to convert.
786
 * @return string|array
787
 */
788
function getpaid_convert_price_string_to_options( $str ) {
789
790
	$raw_options = array_map( 'trim', explode( ',', $str ) );
791
    $options     = array();
792
793
    foreach ( $raw_options as $option ) {
794
795
        if ( '' == $option ) {
796
            continue;
797
        }
798
799
        $option = array_map( 'trim', explode( '|', $option ) );
800
801
        $price = null;
802
        $label = null;
803
804
        if ( isset( $option[0] ) && '' !=  $option[0] ) {
805
            $label  = $option[0];
806
        }
807
808
        if ( isset( $option[1] ) && '' !=  $option[1] ) {
809
            $price = $option[1];
810
        }
811
812
        if ( ! isset( $price ) ) {
813
            $price = $label;
814
        }
815
816
        if ( ! isset( $price ) || ! is_numeric( $price ) ) {
817
            continue;
818
        }
819
820
        if ( ! isset( $label ) ) {
821
            $label = $price;
822
        }
823
824
        $options[ $price ] = $label;
825
    }
826
827
    return $options;
828
}
829
830
/**
831
 * Returns the help tip.
832
 */
833
function getpaid_get_help_tip( $tip, $additional_classes = '' ) {
834
    $additional_classes = sanitize_html_class( $additional_classes );
835
    $tip                = esc_attr__( $tip );
836
    return "<span class='wpi-help-tip dashicons dashicons-editor-help $additional_classes' title='$tip'></span>";
837
}
838
839
/**
840
 * Formats a date
841
 */
842
function getpaid_format_date( $date ) {
843
844
    if ( empty( $date ) || $date == '0000-00-00 00:00:00' ) {
845
        return '';
846
    }
847
848
    return date_i18n( getpaid_date_format(), strtotime( $date ) );
849
850
}
851
852
/**
853
 * Formats a date into the website's date setting.
854
 *
855
 * @return string
856
 */
857
function getpaid_format_date_value( $date, $default = "&mdash;" ) {
858
    $date = getpaid_format_date( $date );
859
    return empty( $date ) ? $default : $date;
860
}
861
862
/**
863
 * Get the date format used all over the plugin.
864
 *
865
 * @return string
866
 */
867
function getpaid_date_format() {
868
	return apply_filters( 'getpaid_date_format', get_option( 'date_format' ) );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('ge..._option('date_format')) 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...
869
}
870
871
/**
872
 * Get the time format used all over the plugin.
873
 *
874
 * @return string
875
 */
876
function getpaid_time_format() {
877
	return apply_filters( 'getpaid_time_format', get_option( 'time_format' ) );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('ge..._option('time_format')) 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...
878
}
879
880
/**
881
 * Limit length of a string.
882
 *
883
 * @param  string  $string string to limit.
884
 * @param  integer $limit Limit size in characters.
885
 * @return string
886
 */
887
function getpaid_limit_length( $string, $limit ) {
888
    $str_limit = $limit - 3;
889
890
	if ( function_exists( 'mb_strimwidth' ) ) {
891
		if ( mb_strlen( $string ) > $limit ) {
892
			$string = mb_strimwidth( $string, 0, $str_limit ) . '...';
893
		}
894
	} else {
895
		if ( strlen( $string ) > $limit ) {
896
			$string = substr( $string, 0, $str_limit ) . '...';
897
		}
898
	}
899
    return $string;
900
901
}
902
903
/**
904
 * Returns the REST API handler.
905
 * 
906
 * @return WPInv_API
907
 * @since 1.0.19
908
 */
909
function getpaid_api() {
910
    return getpaid()->get( 'api' );
911
}
912
913
/**
914
 * Returns the post types object.
915
 * 
916
 * @return GetPaid_Post_Types
917
 * @since 1.0.19
918
 */
919
function getpaid_post_types() {
920
    return getpaid()->get( 'post_types' );
921
}
922
923
/**
924
 * Returns the session handler.
925
 * 
926
 * @return WPInv_Session_Handler
927
 * @since 1.0.19
928
 */
929
function getpaid_session() {
930
    return getpaid()->get( 'session' );
931
}
932
933
/**
934
 * Returns the notes handler.
935
 * 
936
 * @return WPInv_Notes
937
 * @since 1.0.19
938
 */
939
function getpaid_notes() {
940
    return getpaid()->get( 'notes' );
941
}
942
943
/**
944
 * Returns the main admin class.
945
 * 
946
 * @return GetPaid_Admin
947
 */
948
function getpaid_admin() {
949
    return getpaid()->get( 'admin' );
950
}
951
952
/**
953
 * Retrieves a URL to an authenticated action
954
 *
955
 * @param string $action
956
 * @param string $base the base url
957
 * @return string
958
 */
959
function getpaid_get_authenticated_action_url( $action, $base = false ) {
960
    return wp_nonce_url( add_query_arg( 'getpaid-action', $action, $base ), 'getpaid-nonce', 'getpaid-nonce' );
961
}
962
963
/**
964
 * Returns a post type label.
965
 *
966
 * @return string
967
 */
968
function getpaid_get_post_type_label( $post_type, $plural = true ) {
969
970
    $post_type = get_post_type_object( $post_type );
971
972
    if ( ! is_object( $post_type ) ) {
973
        return null;
974
    }
975
976
    return $plural ? $post_type->labels->name : $post_type->labels->singular_name;
977
978
}
979
980
/**
981
 * Retrieves an array
982
 *
983
 * @return mixed|null
984
 */
985
function getpaid_get_array_field( $array, $key, $secondary_key = null ) {
986
987
    if ( ! is_array( $array ) ) {
988
        return null;
989
    }
990
991
    if ( ! empty( $secondary_key ) ) {
992
        $array = isset( $array[ $secondary_key ] ) ? $array[ $secondary_key ] : array();
993
        return getpaid_get_array_field( $array, $key );
0 ignored issues
show
Bug introduced by
Are you sure the usage of getpaid_get_array_field($array, $key) is correct as it 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...
994
    }
995
996
    return isset( $array[ $key ] ) ? $array[ $key ] : null;
997
998
}
999
1000
/**
1001
 * Merges an empty array
1002
 *
1003
 * @return array
1004
 */
1005
function getpaid_array_merge_if_empty( $args, $defaults ) {
1006
1007
    foreach ( $defaults as $key => $value ) {
1008
1009
        if ( array_key_exists( $key, $args ) && empty( $args[ $key ] ) ) {
1010
            $args[ $key ] = $value;
1011
        }
1012
1013
    }
1014
1015
    return $args;
1016
1017
}
1018