PluginProbe
Parse.ly / 3.23.4
Parse.ly v3.23.4
3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 3.20.3 All 105 releases
wp-parsely / src / Utils / class-utils.php

class-utils.php in Parse.ly 3.23.4, at src/Utils/class-utils.php

502 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Utils Class.
4 *
5 * To enforce typing on commonly used functions.
6 *
7 * @package Parsely
8 * @since 3.7.0
9 * @since 3.17.0 Refactored to a class.
10 */
11
12 declare(strict_types=1);
13
14 namespace Parsely\Utils;
15
16 use WP_Post;
17 use WP_Error;
18
19 use const Parsely\PARSELY_CACHE_GROUP;
20 use const Parsely\PARSELY_FILE;
21
22 /**
23 * Utils Class.
24 *
25 * @since 3.17.0
26 *
27 * @phpstan-type ItmParams array{
28 * campaign: string,
29 * source?: string,
30 * medium?: string,
31 * content?: string,
32 * term?: string,
33 * }
34 */
35 class Utils {
36 const DATE_UTC_FORMAT = 'Y-m-d';
37 const WP_DATE_TIME_FORMAT = 'Y-m-d H:i:s';
38
39 /**
40 * Gets UTC Date.
41 *
42 * @since 3.7.0
43 *
44 * @param int $days Number of days before or after the current date.
45 *
46 * @return string
47 */
48 public static function get_utc_date_format( int $days = 0 ): string {
49 if ( 0 === $days ) {
50 return gmdate( self::DATE_UTC_FORMAT );
51 }
52
53 return gmdate( self::DATE_UTC_FORMAT, (int) strtotime( "{$days} days" ) );
54 }
55
56 /**
57 * Gets default category.
58 *
59 * @since 3.7.0
60 *
61 * @return int
62 */
63 public static function get_default_category(): int {
64 /**
65 * Variable.
66 *
67 * @var string
68 */
69 $default_category = get_option( 'default_category' );
70 return (int) $default_category;
71 }
72
73 /**
74 * Gets option `page_for_posts`.
75 *
76 * @since 3.7.0
77 *
78 * @param bool $default_value Default Value.
79 *
80 * @return int|WP_Post
81 */
82 public static function get_page_for_posts( bool $default_value = false ) {
83 /**
84 * Variable.
85 *
86 * @var int|WP_Post
87 */
88 return get_option( 'page_for_posts', $default_value );
89 }
90
91 /**
92 * Gets option `page_on_front`.
93 *
94 * @since 3.7.0
95 *
96 * @return bool
97 */
98 public static function get_page_on_front(): bool {
99 /**
100 * Variable.
101 *
102 * @var bool
103 */
104 return (bool) get_option( 'page_on_front' );
105 }
106
107 /**
108 * Gets 'string' query variable from WP_Query class.
109 *
110 * @since 3.7.0
111 *
112 * @param string $key Variable key to retrieve.
113 *
114 * @return string
115 */
116 public static function get_string_query_var( string $key ): string {
117 /**
118 * Variable.
119 *
120 * @var string
121 */
122 return get_query_var( $key );
123 }
124
125 /**
126 * Gets site date format.
127 *
128 * @since 3.7.0
129 *
130 * @return string
131 */
132 public static function get_date_format(): string {
133 /**
134 * Variable.
135 *
136 * @var string
137 */
138 return get_option( 'date_format' );
139 }
140
141 /**
142 * Gets site time format.
143 *
144 * @since 3.7.0
145 *
146 * @return string
147 */
148 public static function get_time_format(): string {
149 /**
150 * Variable.
151 *
152 * @var string
153 */
154 return get_option( 'time_format' );
155 }
156
157 /**
158 * Returns the current time's timestamp.
159 *
160 * @since 3.12.0
161 *
162 * @return string
163 */
164 public static function get_timestamp(): string {
165 $timestamp = round( microtime( true ) * 1000 );
166 return number_format( $timestamp, 0, '', '' );
167 }
168
169 /**
170 * Gets number in formatted form i.e. express bigger numbers in form of
171 * thousands (k), millions (M), billions (B).
172 *
173 * Note: This function is not made to process float numbers, and it is a PHP
174 * port of our formatToImpreciseNumber() TypeScript function.
175 *
176 * Example:
177 * - Represent 10000 as 10K.
178 *
179 * @since 3.7.0
180 *
181 * @param string $value The number to process. It can be formatted.
182 * @param int $fraction_digits The number of desired fraction digits.
183 * @param string $glue A string to put between the number and unit.
184 *
185 * @return string The number formatted as an imprecise number.
186 */
187 public static function get_formatted_number( string $value, int $fraction_digits = 1, string $glue = '' ): string {
188 $number = (int) preg_replace( '/\D/', '', $value );
189
190 if ( $number < 1000 ) {
191 return $value;
192 } elseif ( $number < 10000 ) {
193 $fraction_digits = 1;
194 }
195
196 $unit_names = array(
197 '1000' => 'k',
198 '1000000' => 'M',
199 '1000000000' => 'B',
200 '1000000000000' => 'T',
201 '1000000000000000' => 'Q',
202 );
203 $current_number = $number;
204 $current_number_as_string = (string) $number;
205 $unit = '';
206 $previous_number = 0;
207
208 foreach ( $unit_names as $thousands => $suffix ) {
209 $thousands_int = (int) preg_replace( '/\D/', '', (string) $thousands );
210
211 if ( $number >= $thousands_int ) {
212 $current_number = $number / $thousands_int;
213 $precision = $fraction_digits;
214
215 // For over 10 units, we reduce the precision to 1 fraction digit.
216 $modulo = (int) fmod( $current_number, 1 );
217 if ( 0 !== $previous_number && $modulo > 1 / $previous_number ) {
218 $precision = $current_number > 10 ? 1 : 2;
219 }
220
221 // Precision override, where we want to show 2 fraction digits.
222 $zeroes = floatval( number_format( $current_number, 2 ) ) === floatval( number_format( $current_number, 0 ) );
223 $precision = $zeroes ? 0 : $precision;
224 $current_number_as_string = number_format( $current_number, $precision, '.', '' );
225 $unit = $suffix;
226 }
227
228 $previous_number = $current_number;
229 }
230
231 return $current_number_as_string . $glue . $unit;
232 }
233
234 /**
235 * Gets time in formatted form.
236 *
237 * Example:
238 * - Input `1000` (seconds) and Output `16:40` which represents "16 minutes, 40 seconds”
239 *
240 * @since 3.7.0
241 *
242 * @param float $seconds Time in seconds to be formatted.
243 *
244 * @return string
245 */
246 public static function get_formatted_time( $seconds ): string {
247 $seconds = round( $seconds );
248 $hours = floor( $seconds / 3600 );
249
250 if ( $hours >= 1 ) {
251 $seconds = $seconds - ( $hours * 3600 );
252 $minutes = floor( $seconds / 60 );
253 $seconds = round( $seconds % 60 );
254
255 return esc_html(
256 sprintf(
257 /* translators: 1: Number of hours 2: Number of minutes 3: Number of seconds */
258 __( '%1$d:%2$02d:%3$02d', 'wp-parsely' ),
259 $hours,
260 $minutes,
261 $seconds
262 )
263 );
264 }
265
266 $minutes = floor( $seconds / 60 );
267 $seconds = round( $seconds % 60 );
268
269 if ( $minutes >= 1 ) {
270 return esc_html(
271 sprintf(
272 /* translators: 1: Number of minutes 2: Number of seconds */
273 __( '%1$d:%2$02d', 'wp-parsely' ),
274 $minutes,
275 $seconds
276 )
277 );
278 }
279
280 return esc_html(
281 sprintf(
282 /* translators: 1: Number of seconds */
283 __( '%1$ds', 'wp-parsely' ),
284 round( $seconds )
285 )
286 );
287 }
288
289 /**
290 * Returns the passed float as a time duration in m:ss format.
291 *
292 * Examples:
293 * - $time of 1.005 yields '1:00'.
294 * - $time of 1.5 yields '1:30'.
295 * - $time of 1.999 yields '2:00'.
296 *
297 * @since 3.6.0
298 *
299 * @param float $time The time as a float number.
300 *
301 * @return string The resulting formatted time duration.
302 */
303 public static function get_formatted_duration( float $time ): string {
304 $minutes = absint( $time );
305 $seconds = absint( round( fmod( $time, 1 ) * 60 ) );
306
307 if ( 60 === $seconds ) {
308 ++$minutes;
309 $seconds = 0;
310 }
311
312 return sprintf( '%d:%02d', $minutes, $seconds );
313 }
314
315 /**
316 * Converts to associate array.
317 *
318 * @since 3.7.0
319 *
320 * @param mixed $obj Input object.
321 *
322 * @return array<string, mixed>|WP_Error
323 */
324 public static function convert_to_associative_array( $obj ) {
325 $encoded = wp_json_encode( $obj );
326 if ( false === $encoded ) {
327 return new WP_Error( 'parsely_encoding_failed', __( 'Unable to encode API response for associative array', 'wp-parsely' ) );
328 }
329
330 /**
331 * Variable.
332 *
333 * @var array<string, mixed>
334 */
335 return json_decode( $encoded, true );
336 }
337
338 /**
339 * Converts a string to a positive integer, removing any non-numeric
340 * characters.
341 *
342 * @param string $value The string to be converted to an integer.
343 * @return int The integer resulting from the conversion.
344 */
345 public static function convert_to_positive_integer( string $value ): int {
346 return (int) preg_replace( '/\D/', '', $value );
347 }
348
349 /**
350 * Converts endpoint to filter key by replacing `/` with `_`.
351 *
352 * @param string $endpoint Route of the endpoint.
353 *
354 * @since 3.7.0
355 *
356 * @return string
357 */
358 public static function convert_endpoint_to_filter_key( string $endpoint ): string {
359 return trim( str_replace( array( '-', '/' ), '_', $endpoint ), '_' );
360 }
361
362 /**
363 * Gets content of asset file.
364 *
365 * @param string $path Path of the asset file.
366 *
367 * @since 3.8.0
368 *
369 * @return Asset_Info
370 */
371 public static function get_asset_info( string $path ) {
372 return require plugin_dir_path( PARSELY_FILE ) . $path;
373 }
374
375 /**
376 * Checks if a string starts with a specific substring.
377 *
378 * This function uses the built-in PHP function `str_starts_with` if it's available (PHP 8.0 and later).
379 * If the function is not available (PHP versions prior to 8.0), it uses the `strpos` function as a fallback.
380 *
381 * @since 3.13.0
382 *
383 * @param string $haystack The string to search in.
384 * @param string $needle The substring to search for at the start of $haystack.
385 * @return bool Returns true if $haystack starts with $needle, false otherwise.
386 */
387 public static function str_starts_with( string $haystack, string $needle ): bool {
388 if ( function_exists( '\str_starts_with' ) ) {
389 return \str_starts_with( $haystack, $needle );
390 }
391 return 0 === strpos( $haystack, $needle );
392 }
393
394 /**
395 * Checks if HTTPS is supported for the site.
396 *
397 * This function checks if the WordPress function 'wp_is_using_https' exists and uses it to determine if
398 * HTTPS is supported.
399 * If the function does not exist, it checks if the home URL scheme is HTTPS.
400 * If neither of the above conditions are met, it checks if the site URL option scheme is HTTPS.
401 *
402 * @since 3.14.1
403 *
404 * @return bool Returns true if HTTPS is supported, false otherwise.
405 */
406 public static function parsely_is_https_supported(): bool {
407 if ( function_exists( 'wp_is_using_https' ) ) {
408 return wp_is_using_https();
409 }
410
411 if ( 'https' === wp_parse_url( home_url(), PHP_URL_SCHEME ) ) {
412 return true;
413 }
414
415 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
416 $site_url = apply_filters( 'site_url', get_option( 'siteurl' ), '', null, null );
417 return 'https' === wp_parse_url( $site_url, PHP_URL_SCHEME );
418 }
419
420 /**
421 * Returns the post ID for the passed URL.
422 *
423 * @since 3.16.0
424 * @since 3.18.0 Moved from `Models/class-smart-link.php`.
425 *
426 * @param string $url The URL to get the post ID for.
427 * @return int The post ID of the URL, 0 if not found.
428 */
429 public static function get_post_id_by_url( string $url ): int {
430 $cache_key = sprintf( 'url-to-postid-%s', hash( 'sha256', $url ) );
431 $cache = wp_cache_get( $cache_key, PARSELY_CACHE_GROUP );
432
433 if ( false !== $cache && is_numeric( $cache ) ) {
434 return (int) $cache;
435 }
436
437 if ( function_exists( 'wpcom_vip_url_to_postid' ) ) {
438 $post_id = wpcom_vip_url_to_postid( $url );
439 } else {
440 // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.url_to_postid_url_to_postid
441 $post_id = url_to_postid( $url );
442 wp_cache_set( $cache_key, $post_id, PARSELY_CACHE_GROUP, WEEK_IN_SECONDS );
443 }
444
445 // A post ID was found, return it.
446 if ( 0 !== $post_id ) {
447 return $post_id;
448 }
449
450 // No post ID was found, try to find it from the slug.
451 $clean_url = preg_replace( '/\?.*$/', '', $url ); // Remove the query string from the URL.
452 if ( null === $clean_url ) {
453 $clean_url = $url;
454 }
455 $post_slug = basename( $clean_url );
456
457 $public_post_types = get_post_types(
458 array(
459 'public' => true,
460 'show_in_rest' => true,
461 )
462 );
463 $post = get_page_by_path( $post_slug, OBJECT, array_keys( $public_post_types ) );
464
465 if ( null !== $post ) {
466 wp_cache_set( $cache_key, $post->ID, PARSELY_CACHE_GROUP, WEEK_IN_SECONDS );
467 return $post->ID;
468 }
469
470 return 0;
471 }
472
473 /**
474 * Appends ITM parameters to a URL.
475 *
476 * @since 3.19.0
477 *
478 * @param string $url The URL to append the ITM parameters to.
479 * @param ItmParams $params The ITM parameters to append.
480 * @return string The URL with the ITM parameters appended.
481 */
482 public static function append_itm_params( string $url, $params ): string {
483 // Convert the params array to the correct format.
484 $mapping = array(
485 'campaign' => 'itm_campaign',
486 'source' => 'itm_source',
487 'medium' => 'itm_medium',
488 'content' => 'itm_content',
489 'term' => 'itm_term',
490 );
491
492 $itm_params = array();
493 foreach ( $params as $key => $value ) {
494 if ( array_key_exists( $key, $mapping ) ) {
495 $itm_params[ $mapping[ $key ] ] = $value;
496 }
497 }
498
499 return add_query_arg( $itm_params, $url );
500 }
501 }
502