PluginProbe ʕ •ᴥ•ʔ
Advanced Database Cleaner – Optimize & Clean Database to Speed Up Site Performance / 4.2.0
Advanced Database Cleaner – Optimize & Clean Database to Speed Up Site Performance v4.2.0
4.2.0 trunk 1.0.0 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.5 1.3.6 1.3.7 2.0.0 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.1.0 4.1.1
advanced-database-cleaner / includes / utils / class-adbc-common-utils.php
advanced-database-cleaner / includes / utils Last commit date
validator 3 days ago class-adbc-common-utils.php 4 months ago class-adbc-files.php 7 months ago class-adbc-logging.php 3 months ago class-adbc-notifications.php 3 days ago class-adbc-rest.php 3 days ago
class-adbc-common-utils.php
625 lines
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) )
5 exit;
6
7 /**
8 * ADBC common utils class.
9 *
10 * This class provides common utils functions.
11 */
12 class ADBC_Common_Utils {
13
14 /**
15 * Format bytes.
16 *
17 * @param int $bytes The number of bytes.
18 * @param int $precision The number of decimal places to include in the formatted string.
19 *
20 * @return string Formatted bytes.
21 */
22 public static function format_bytes( $bytes, $precision = 2 ) {
23
24 $absolute_bytes = abs( $bytes );
25 $formatted_value = $bytes;
26 $size_unit = "B";
27
28 if ( $absolute_bytes >= 1024 ** 3 ) {
29 $formatted_value = $bytes / 1024 ** 3;
30 $size_unit = "GB";
31 } else if ( $absolute_bytes >= 1024 ** 2 ) {
32 $formatted_value = $bytes / 1024 ** 2;
33 $size_unit = "MB";
34 } else if ( $absolute_bytes >= 1024 ) {
35 $formatted_value = $bytes / 1024;
36 $size_unit = "KB";
37 }
38
39 // Truncate to specified decimals without rounding up
40 $multiplier = pow( 10, $precision );
41 $formatted_value = floor( $formatted_value * $multiplier ) / $multiplier;
42
43 return "$formatted_value $size_unit";
44
45 }
46
47 /**
48 * Convert size to bytes.
49 *
50 * @param string $size The size to convert.
51 * @param string $unit The unit of the size (e.g., KB, MB, GB).
52 *
53 * @return int Size in bytes.
54 */
55 public static function convert_size_to_bytes( $size, $unit ) {
56
57 $size = intval( $size );
58
59 switch ( strtoupper( $unit ) ) {
60 case 'KB':
61 return $size * 1024;
62 case 'MB':
63 return $size * 1024 * 1024;
64 case 'GB':
65 return $size * 1024 * 1024 * 1024;
66 default:
67 return $size;
68 }
69
70 }
71
72 /**
73 * Convert the post_max_size php setting size format to bytes.
74 *
75 * @param string $from The post_max_size value (e.g., 8M, 16G).
76 *
77 * @return int The post_max_size value in bytes.
78 */
79 public static function convert_post_max_size_to_bytes( $from ) {
80
81 if ( ! is_string( $from ) || $from === '' ) {
82 return 0;
83 }
84
85 $trimmed = trim( $from );
86 $last = strtoupper( substr( $trimmed, -1 ) );
87
88 // If last char is a unit, remove it and parse the number
89 if ( in_array( $last, array( 'K', 'M', 'G' ), true ) ) {
90 $number = (float) substr( $trimmed, 0, -1 );
91 } else {
92 // No unit, treat the whole string as a number
93 return (float) $trimmed;
94 }
95
96 switch ( $last ) {
97 case 'K':
98 return $number * 1024;
99 case 'M':
100 return $number * 1024 * 1024;
101 case 'G':
102 return $number * 1024 * 1024 * 1024;
103 }
104
105 return 0;
106 }
107
108
109 /**
110 * Truncate a string to a specific length.
111 *
112 * @param string $string The string to truncate.
113 * @param int $max_length The maximum length of the string.
114 * @param string $append The string to append to the truncated string.
115 *
116 * @return string The truncated string.
117 */
118 public static function truncate_string( $string, $max_length = 100, $append = '...' ) {
119
120 $truncated_string = $string;
121
122 if ( strlen( $string ) > $max_length ) {
123 $truncated_string = substr( $string, 0, $max_length ) . $append;
124 }
125
126 return $truncated_string;
127
128 }
129
130 /**
131 * Format a date string to a friendly, localized format.
132 *
133 * @param string $date_string The date string to format.
134 * @param string $date_format The format of the input date string (PHP DateTime format).
135 *
136 * @return string The formatted date string.
137 */
138 public static function format_date_friendly( $date_string, $date_format = 'd/m/Y' ) {
139
140 $date = DateTime::createFromFormat( $date_format, $date_string );
141
142 // Invalid date: keep original behavior.
143 if ( ! $date ) {
144 return $date_string;
145 }
146
147 $timestamp = $date->getTimestamp();
148
149 // Translators can adjust the display format.
150 // Example output: "December 10, 2025".
151 $friendly_format = _x(
152 'F j, Y',
153 'Friendly date format (e.g. December 10, 2025)',
154 'advanced-database-cleaner'
155 );
156
157 if ( function_exists( 'wp_date' ) ) {
158 return wp_date( $friendly_format, $timestamp );
159 }
160
161 // Fallback for very old WordPress versions.
162 return date_i18n( $friendly_format, $timestamp );
163 }
164
165 /**
166 * Format a timestamp to a friendly, localized format.
167 *
168 * @param int|string|null $timestamp The timestamp to format.
169 *
170 * @return string The formatted date string, or empty string if timestamp is invalid.
171 */
172 public static function format_timestamp_friendly( $timestamp ) {
173
174 // Handle empty or invalid timestamps.
175 if ( empty( $timestamp ) || ! is_numeric( $timestamp ) ) {
176 return '';
177 }
178
179 $timestamp = (int) $timestamp;
180
181 // Validate timestamp range (reasonable Unix timestamp range).
182 if ( $timestamp < 0 || $timestamp > 2147483647 ) {
183 return '';
184 }
185
186 // Translators can adjust the display format.
187 // Example output: "December 10, 2025 3:45 PM".
188 $friendly_format = _x(
189 'F j, Y g:i A',
190 'Friendly date and time format (e.g. December 10, 2025 3:45 PM)',
191 'advanced-database-cleaner'
192 );
193
194 if ( function_exists( 'wp_date' ) ) {
195 return wp_date( $friendly_format, $timestamp );
196 }
197
198 // Fallback for very old WordPress versions.
199 return date_i18n( $friendly_format, $timestamp );
200 }
201
202 /**
203 * Detect the type of a value.
204 *
205 * @param mixed $value The value to detect.
206 *
207 * @return string The detected type.
208 */
209 public static function get_value_type( $value ) {
210
211 /* 1. Empty string after CAST or manual entry */
212 if ( $value === null || $value === '' ) {
213 return 'empty_string';
214 }
215
216 /* 2. Boolean-flavoured strings */
217 $lc = strtolower( trim( $value ) );
218 if ( in_array( $lc, [ 'true', 'false' ], true ) ) {
219 return 'boolean_string';
220 }
221
222 /* 3. Numeric strings */
223 if ( is_numeric( $value ) ) {
224 return strpos( $value, '.' ) !== false ? 'float_string' : 'integer_string';
225 }
226
227 /* 4. Serialized PHP payloads */
228 if ( is_serialized( $value ) ) {
229 return 'serialized_data';
230 }
231
232 /* 5. JSON blobs */
233 $t = trim( $value );
234 if (
235 ( $t[0] === '{' && substr( $t, -1 ) === '}' ) ||
236 ( $t[0] === '[' && substr( $t, -1 ) === ']' )
237 ) {
238 if ( mb_check_encoding( $t, 'UTF-8' ) ) {
239 $decoded = json_decode( $t );
240 if ( json_last_error() === JSON_ERROR_NONE ) {
241 return is_array( $decoded ) ? 'json_array' : 'json_object';
242 }
243 }
244 }
245
246 /* 6. Fallback */
247 return 'string';
248 }
249
250 /**
251 * Safely unserialize a string that is known to contain a serialized *array*.
252 * – Rejects objects and references by passing `allowed_classes => false`.
253 * – Returns false on failure or when the payload is not a serialized array.
254 *
255 * @param string $str The serialized value.
256 * @return array|false Decoded array, or false on error.
257 */
258 public static function safe_unserialize_array( $str ) {
259
260 // Quick sanity check
261 if ( ! is_serialized( $str ) )
262 return false;
263
264 // Use PHP's allowed_classes flag (>=7.0) to block objects
265 $decoded = @unserialize( $str, [ 'allowed_classes' => false ] );
266
267 return is_array( $decoded ) ? $decoded : false;
268 }
269
270 /**
271 * Recursively decode any JSON-or-serialized blobs found inside a value.
272 *
273 * Rules applied depth-first:
274 * 1. If the value is a JSON string → json_decode() (assoc array).
275 * 2. Else if the value is a PHP-serialized string → unserialize().
276 * 3. Arrays / objects are walked recursively.
277 * 4. All other scalars are returned untouched.
278 *
279 * @param mixed $data The value to examine.
280 * @return mixed Fully decoded structure.
281 */
282 public static function deep_decode( $data ) {
283
284 // 1. Handle scalar strings: try JSON, then serialized.
285 if ( is_string( $data ) ) {
286
287 $trim = trim( $data );
288
289 /* JSON test: quick wrapper check before json_decode() */
290 if (
291 $trim !== '' &&
292 (
293 ( $trim[0] === '{' && substr( $trim, -1 ) === '}' ) ||
294 ( $trim[0] === '[' && substr( $trim, -1 ) === ']' )
295 )
296 ) {
297 $decoded = json_decode( $trim, true );
298 if ( json_last_error() === JSON_ERROR_NONE ) {
299 return self::deep_decode( $decoded );
300 }
301 }
302
303 /* Try PHP-serialized (arrays only, objects disabled) */
304 if ( is_serialized( $data ) ) {
305
306 $decoded = @unserialize( $data, [ 'allowed_classes' => false ] );
307
308 // Accept only arrays; everything else is left as-is
309 if ( is_array( $decoded ) ) {
310 return self::deep_decode( $decoded );
311 }
312 }
313
314 return $data; // plain string
315 }
316
317 // 2. Recurse through arrays
318 if ( is_array( $data ) ) {
319 foreach ( $data as $k => $v ) {
320 $data[ $k ] = self::deep_decode( $v );
321 }
322 return $data;
323 }
324
325 // 4. Objects, Integers, floats, booleans, null → return as-is
326 return $data;
327 }
328
329 /**
330 * Parse a date string into a DateTime object based on a specified format.
331 * This function attempts to create a DateTime object from the given date string
332 * using the specified format. If the date string is not valid according to the format,
333 * it returns null.
334 *
335 * @param string $date
336 * @param string $format
337 *
338 * @return bool|DateTime|null
339 */
340 public static function parse_date( $date, string $format ): ?DateTime {
341 $obj = DateTime::createFromFormat( $format, $date );
342 $err = DateTime::getLastErrors() ?: [ 'warning_count' => 0, 'error_count' => 0 ];
343
344 return ( $obj && 0 === $err['warning_count'] && 0 === $err['error_count'] )
345 ? $obj
346 : null;
347 }
348
349 /**
350 * Get the added custom ADBC schedule frequencies.
351 *
352 * @return array<string>
353 */
354 public static function get_adbc_schedule_frequencies() {
355
356 $all_frequencies = wp_get_schedules();
357 $adbc_frequencies = [];
358 foreach ( $all_frequencies as $key => $value ) {
359 if ( strpos( $key, 'adbc_' ) === 0 ) {
360 $adbc_frequencies[ $key ] = $value;
361 }
362 }
363
364 return array_keys( $adbc_frequencies );
365
366 }
367
368 /**
369 * Check if a new free version (>= 4.0.0) exists.
370 *
371 * @return bool True if a new free version exists, false otherwise.
372 */
373 public static function is_new_free_version_installed() {
374
375 // Ensure plugin functions are loaded
376 if ( ! function_exists( 'get_plugins' ) ) {
377 require_once ABSPATH . 'wp-admin/includes/plugin.php';
378 }
379
380 $plugins = get_plugins();
381
382 $free_slug = 'advanced-database-cleaner/advanced-db-cleaner.php';
383
384 if ( isset( $plugins[ $free_slug ] ) ) {
385 $free_version = $plugins[ $free_slug ]['Version'];
386
387 // Compare version
388 if ( version_compare( $free_version, '4.0.0', '>=' ) ) {
389 return true;
390 }
391 }
392
393 return false;
394
395 }
396
397 /**
398 * Get the last n days ending today.
399 *
400 * @param int $n The number of days to get.
401 *
402 * @return array An array of date strings in 'Y-m-d' format.
403 */
404 public static function last_n_days_ending_today( $n ) {
405
406 $n = max( 1, (int) $n );
407 $today = new DateTime( 'today' );
408 $start = clone $today;
409 $start->modify( '-' . ( $n - 1 ) . ' days' );
410 $out = [];
411 $cursor = clone $start;
412
413 while ( $cursor <= $today ) {
414 $out[] = $cursor->format( 'Y-m-d' );
415 $cursor->modify( '+1 day' );
416 }
417
418 return $out;
419
420 }
421
422 /**
423 * Check if the old free version exists.
424 *
425 * @return bool True if the old free version exists, false otherwise.
426 */
427 public static function is_old_free_exists() {
428
429 // Ensure plugin functions are loaded
430 if ( ! function_exists( 'get_plugins' ) ) {
431 require_once ABSPATH . 'wp-admin/includes/plugin.php';
432 }
433
434 $plugins = get_plugins();
435
436 $free_slug = 'advanced-database-cleaner/advanced-db-cleaner.php';
437
438 if ( isset( $plugins[ $free_slug ] ) ) {
439 $free_version = $plugins[ $free_slug ]['Version'];
440
441 // Compare version
442 if ( version_compare( $free_version, '4.0.0', '<' ) ) {
443 return true;
444 }
445
446 }
447
448 return false;
449
450 }
451
452 /**
453 * Check if a new free version (>= 4.0.0) exists.
454 *
455 * @return bool True if a new free version exists, false otherwise.
456 */
457 public static function is_new_free_exists() {
458
459 // Ensure plugin functions are loaded
460 if ( ! function_exists( 'get_plugins' ) ) {
461 require_once ABSPATH . 'wp-admin/includes/plugin.php';
462 }
463
464 $plugins = get_plugins();
465
466 $free_slug = 'advanced-database-cleaner/advanced-db-cleaner.php';
467
468 if ( isset( $plugins[ $free_slug ] ) ) {
469 $free_version = $plugins[ $free_slug ]['Version'];
470
471 // Compare version
472 if ( version_compare( $free_version, '4.0.0', '>=' ) ) {
473 return true;
474 }
475 }
476
477 return false;
478
479 }
480
481 /**
482 * Check if the pro version is installed.
483 *
484 * @return bool True if the pro version is installed, false otherwise.
485 */
486 public static function is_old_pro_exists() {
487
488 // Ensure plugin functions are loaded
489 if ( ! function_exists( 'get_plugins' ) ) {
490 require_once ABSPATH . 'wp-admin/includes/plugin.php';
491 }
492
493 $plugins = get_plugins();
494
495 $pro_slug = 'advanced-database-cleaner-pro/advanced-db-cleaner.php';
496 if ( isset( $plugins[ $pro_slug ] ) ) {
497 $pro_version = $plugins[ $pro_slug ]['Version'];
498
499 // Compare version
500 if ( version_compare( $pro_version, '4.0.0', '<' ) ) {
501 return true;
502 }
503
504 }
505
506 return false;
507
508 }
509
510 public static function is_old_pro_data_exists() {
511
512 // We consider the old pro data exists if both the security folder and the settings options are not empty, as these are the main components of the old pro version data.
513 $security_folder = get_option( 'aDBc_security_folder_code' );
514 $settings = get_option( 'aDBc_settings' );
515 return ! empty( $security_folder ) && ! empty( $settings );
516
517 }
518
519 /**
520 * Check if the premium version is installed.
521 *
522 * @return bool True if the premium version is installed, false otherwise.
523 */
524 public static function is_premium_exists() {
525
526 // Ensure plugin functions are loaded
527 if ( ! function_exists( 'get_plugins' ) ) {
528 require_once ABSPATH . 'wp-admin/includes/plugin.php';
529 }
530
531 $plugins = get_plugins();
532
533 $premium_slug = 'advanced-database-cleaner-premium/advanced-db-cleaner.php';
534 if ( isset( $plugins[ $premium_slug ] ) ) {
535 return true;
536 }
537
538 return false;
539
540 }
541
542 /**
543 * Executes the old plugin version deactivation cleaning.
544 *
545 * @return void
546 */
547 public static function old_plugin_version_deactivation_cleaning() {
548
549 // Unschedule the optimization and cleaning schedules
550 wp_unschedule_hook( 'aDBc_optimize_scheduler' );
551 wp_unschedule_hook( 'aDBc_clean_scheduler' );
552
553 // Deactivate all cleaning tasks at once
554 $cleaning_tasks = get_option( 'aDBc_clean_schedule' );
555 if ( is_array( $cleaning_tasks ) && ! empty( $cleaning_tasks ) ) {
556 foreach ( $cleaning_tasks as $task_name => $task_info ) {
557 $cleaning_tasks[ $task_name ]['active'] = 0;
558 }
559 update_option( 'aDBc_clean_schedule', $cleaning_tasks, false );
560 }
561
562 // Deactivate all optimization tasks at once
563 $optimize_schedules = get_option( 'aDBc_optimize_schedule' );
564 if ( is_array( $optimize_schedules ) && ! empty( $optimize_schedules ) ) {
565 foreach ( $optimize_schedules as $task_name => $task_info ) {
566 $optimize_schedules[ $task_name ]['active'] = 0;
567 }
568 update_option( 'aDBc_optimize_schedule', $optimize_schedules, false );
569 }
570
571 }
572
573 /**
574 * Mask a license key for display (e.g., show first and last 4 characters only)
575 *
576 * @param string|null $key The license key.
577 * @return string Masked license key.
578 */
579 public static function mask_license_key( $key ) {
580
581 if ( empty( $key ) )
582 return '';
583
584 $key = (string) $key;
585 $length = strlen( $key );
586
587 // Fully mask very short keys
588 if ( $length <= 8 ) {
589 return str_repeat( '*', $length );
590 }
591
592 $first = substr( $key, 0, 4 );
593 $last = substr( $key, -4 );
594 $middle_length = max( 0, $length - 8 );
595 $middle = str_repeat( '*', $middle_length );
596
597 return $first . $middle . $last;
598 }
599
600 /**
601 * Strip the WordPress transient prefix from a transient name.
602 *
603 * Remove the `_site_transient_` or `_transient_` prefix from the given
604 * transient key and return the normalized key. If no prefix is found,
605 * return the key as-is.
606 *
607 * @param string $transient_name Raw transient name.
608 * @return string Transient name without its prefix.
609 */
610 public static function strip_transient_prefix( $transient_name ) {
611
612 // Check `_site_transient_` first (longest and contains "_transient_")
613 if ( strpos( $transient_name, '_site_transient_' ) === 0 )
614 return substr( $transient_name, strlen( '_site_transient_' ) );
615
616 // Check `_transient_`
617 if ( strpos( $transient_name, '_transient_' ) === 0 )
618 return substr( $transient_name, strlen( '_transient_' ) );
619
620 // No prefix found, return unchanged
621 return $transient_name;
622
623 }
624
625 }