Passed
Push — master ( df7500...f1dc5d )
by Brian
04:45
created

getpaid_standardize_amount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 10
rs 10
c 1
b 0
f 0
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 array; 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'] ) ) ) ) ) );
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
 * Standardizes an amount for insterting into the database.
56
 *
57
 * @param string $amount The amount to sanitize.
58
 * @return float
59
 */
60
function getpaid_standardize_amount( $amount ) {
61
62
    $amount = str_replace( wpinv_thousands_separator(), '', $amount );
63
    $amount = str_replace( wpinv_decimal_separator(), '.', $amount );
64
    if ( is_numeric( $amount ) ) {
65
        return floatval( $amount );
66
    }
67
68
    // Cast the remaining to a float.
69
    return wpinv_round_amount( preg_replace( '/[^0-9\.\-]/', '', $amount ) );
0 ignored issues
show
Bug introduced by
preg_replace('/[^0-9\.\-]/', '', $amount) of type string is incompatible with the type double expected by parameter $amount of wpinv_round_amount(). ( Ignorable by Annotation )

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

69
    return wpinv_round_amount( /** @scrutinizer ignore-type */ preg_replace( '/[^0-9\.\-]/', '', $amount ) );
Loading history...
70
71
}
72
73
/**
74
 * Standardizes an amount that has been retrieved from the database.
75
 *
76
 * @param string $amount The amount to sanitize.
77
 */
78
function getpaid_unstandardize_amount( $amount ) {
79
    return str_replace( '.', wpinv_decimal_separator(), $amount );
80
}
81
82
/**
83
 * Sanitizes an amount.
84
 * 
85
 * @param string $amount The amount to sanitize.
86
 */
87
function wpinv_sanitize_amount( $amount ) {
88
89
    if ( is_numeric( $amount ) ) {
90
        return floatval( $amount );
91
    }
92
93
    // Separate the decimals and thousands.
94
    $amount    = explode( wpinv_decimal_separator(), $amount );
95
96
    // Remove thousands.
97
    $amount[0] = str_replace( wpinv_thousands_separator(), '', $amount[0] );
98
99
    // Convert back to string.
100
    $amount = count( $amount ) > 1 ? "{$amount[0]}.{$amount[1]}" : $amount[0];
101
102
    // Cast the remaining to a float.
103
    return (float) preg_replace( '/[^0-9\.\-]/', '', $amount );
104
105
}
106
107
/**
108
 * Rounds an amount.
109
 * 
110
 * @param float $amount
111
 * @param float|string|int|null $decimals
112
 */
113
function wpinv_round_amount( $amount, $decimals = null, $use_sprintf = false ) {
114
115
    if ( $decimals === null ) {
116
        $decimals = wpinv_decimals();
117
    }
118
119
    if ( $use_sprintf ) {
120
        $amount = sprintf( "%.{$decimals}f", (float) $amount );
121
    } else {
122
        $amount = round( (float) $amount, absint( $decimals ) );
123
    }
124
125
    return apply_filters( 'wpinv_round_amount', $amount, $decimals );
126
}
127
128
/**
129
 * Get all invoice statuses.
130
 *
131
 * @since 1.0.19
132
 * @param bool $draft Whether or not to include the draft status.
133
 * @param bool $trashed Whether or not to include the trash status.
134
 * @param string|WPInv_Invoice $invoice The invoice object|post type|type
135
 * @return array
136
 */
137
function wpinv_get_invoice_statuses( $draft = false, $trashed = false, $invoice = false ) {
138
139
	$invoice_statuses = array(
140
		'wpi-pending'    => _x( 'Pending payment', 'Invoice status', 'invoicing' ),
141
        'publish'        => _x( 'Paid', 'Invoice status', 'invoicing' ),
142
        'wpi-processing' => _x( 'Processing', 'Invoice status', 'invoicing' ),
143
		'wpi-onhold'     => _x( 'On hold', 'Invoice status', 'invoicing' ),
144
		'wpi-cancelled'  => _x( 'Cancelled', 'Invoice status', 'invoicing' ),
145
		'wpi-refunded'   => _x( 'Refunded', 'Invoice status', 'invoicing' ),
146
        'wpi-failed'     => _x( 'Failed', 'Invoice status', 'invoicing' ),
147
        'wpi-renewal'    => _x( 'Renewal Payment', 'Invoice status', 'invoicing' ),
148
    );
149
150
    if ( $draft ) {
151
        $invoice_statuses['draft'] = __( 'Draft', 'invoicing' );
152
    }
153
154
    if ( $trashed ) {
155
        $invoice_statuses['trash'] = __( 'Trash', 'invoicing' );
156
    }
157
158
    if ( $invoice instanceof WPInv_Invoice ) {
159
        $invoice = $invoice->get_post_type();
160
    }
161
162
	return apply_filters( 'wpinv_statuses', $invoice_statuses, $invoice );
163
}
164
165
/**
166
 * Returns the formated invoice status.
167
 * 
168
 * @param string $status The raw status
169
 * @param string|WPInv_Invoice $invoice The invoice object|post type|type
170
 */
171
function wpinv_status_nicename( $status, $invoice = false ) {
172
    $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

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

430
        $new_hex_component = dechex( /** @scrutinizer ignore-type */ $new_decimal );
Loading history...
431
        if ( strlen( $new_hex_component ) < 2 ) {
432
            $new_hex_component = "0" . $new_hex_component;
433
        }
434
        $color .= $new_hex_component;
435
    }
436
437
    return $color;
438
}
439
440
function wpinv_hex_lighter( $color, $factor = 30 ) {
441
    $base  = wpinv_rgb_from_hex( $color );
442
    $color = '#';
443
444
    foreach ( $base as $k => $v ) {
445
        $amount      = 255 - $v;
446
        $amount      = $amount / 100;
447
        $amount      = round( $amount * $factor );
448
        $new_decimal = $v + $amount;
449
450
        $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 $num 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

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