PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.1
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
mainwp / class / class-mainwp-database-schema-checker.php

class-mainwp-database-schema-checker.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.2.1, at class/class-mainwp-database-schema-checker.php

771 lines 24.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Database Health Check Tool.
4 *
5 * @package MainWP/Dashboard
6 */
7
8 namespace MainWP\Dashboard;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 /**
16 * Class MainWP_Database_Schema_Checker
17 *
18 * @package MainWP\Dashboard
19 */
20 class MainWP_Database_Schema_Checker { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
21
22 /**
23 * Private static variable to hold the single instance of the class.
24 *
25 * @static
26 *
27 * @var mixed Default null
28 */
29 private static $instance = null;
30
31
32 /**
33 * Cache the detected issues.
34 *
35 * @static
36 * @var array issues.
37 */
38 public static $cache_detected_issues;
39
40
41 /**
42 * Cache the extensions db tables info.
43 *
44 * @static
45 * @var array issues.
46 */
47 public static $cache_extensions_loaded_info;
48
49 /**
50 * Cache the extensions db tables detected issues.
51 *
52 * @static
53 * @var array issues.
54 */
55 public static $cache_extensions_detected_issues;
56
57
58 /**
59 * MainWP_Database_Schema_Checker constructor.
60 *
61 * Run each time the class is called.
62 * Add action to generate tabletop.
63 */
64 public function __construct() {
65 // Constructor method.
66 }
67
68 /**
69 * Create public static instance.
70 *
71 * @static
72 *
73 * @return instance.
74 */
75 public static function get_instance() {
76 if ( null === static::$instance ) {
77 static::$instance = new self();
78 }
79 return static::$instance;
80 }
81
82 /**
83 * Initialize data before running database checks.
84 *
85 * @return void.
86 */
87 private static function init_data() {
88
89 if ( ! is_array( static::$cache_extensions_detected_issues ) ) {
90 static::$cache_extensions_detected_issues = array();
91 }
92
93 /**
94 * Filter to load extension information.
95 *
96 * @since 6.1.3.
97 */
98 static::$cache_extensions_loaded_info = apply_filters(
99 'mainwp_extensions_loaded_info',
100 array()
101 );
102
103 if ( ! empty( static::$cache_extensions_loaded_info ) && is_array( static::$cache_extensions_loaded_info ) ) {
104 foreach ( static::$cache_extensions_loaded_info as $ext_slug => $ext_info ) {
105 if ( ! empty( $ext_info['tables'] ) && is_array( $ext_info['tables'] ) ) {
106 static::$cache_extensions_detected_issues[ $ext_slug ]['tables_db_info'] = array_fill_keys( $ext_info['tables'], false );
107 }
108 }
109 }
110 }
111
112 /**
113 * Get array of database information.
114 *
115 * @return array
116 */
117 public static function get_database_info() { // phpcs:ignore -- NOSONAR - complex function.
118 global $wpdb;
119
120 static::init_data();
121
122 $tables = array();
123 $database_size = array();
124
125 if ( defined( 'DB_NAME' ) ) {
126 $database_table_information = $wpdb->get_results( // phpcs:ignore -- NOSONAR - custom query.
127 $wpdb->prepare(
128 "SELECT
129 table_name AS 'name',
130 engine AS 'engine',
131 round( ( data_length / 1024 / 1024 ), 2 ) 'data',
132 round( ( index_length / 1024 / 1024 ), 2 ) 'index'
133 FROM information_schema.TABLES
134 WHERE table_schema = %s
135 ORDER BY name ASC;",
136 DB_NAME
137 )
138 );
139
140 // WC Core tables to check existence of.
141 $core_tables = MainWP_DB::instance()->get_core_tables();
142
143 $tables = array(
144 'core' => array_fill_keys( $core_tables, false ),
145 'other' => array(),
146 );
147
148 $database_size = array(
149 'data' => 0,
150 'index' => 0,
151 );
152
153 $site_tables_prefix = $wpdb->get_blog_prefix( get_current_blog_id() );
154
155 $mainwp_tables_prefix = $site_tables_prefix . 'mainwp';
156
157 foreach ( $database_table_information as $table ) {
158
159 // to filter mainwp tables.
160 if ( 0 !== strpos( $table->name, $mainwp_tables_prefix ) ) {
161 continue;
162 }
163
164 $table_type = in_array( $table->name, $core_tables, true ) ? 'core' : 'other';
165
166 $tables[ $table_type ][ $table->name ] = array(
167 'data' => $table->data,
168 'index' => $table->index,
169 'engine' => $table->engine,
170 );
171
172 $database_size['data'] += $table->data;
173 $database_size['index'] += $table->index;
174 }
175 }
176
177 // Return database info.
178 return array(
179 'mainwp_database_version' => get_site_option( 'mainwp_db_version' ),
180 'database_prefix' => $wpdb->prefix,
181 'database_size' => $database_size,
182 'database_tables' => $tables,
183 );
184 }
185
186
187 /**
188 * Get array of database privileges.
189 *
190 * @return array
191 */
192 public static function get_database_privileges() {
193 global $wpdb;
194
195 $database_name = $wpdb->dbname;
196 $required_privs = array( 'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP', 'INDEX' );
197 $grants = $wpdb->get_col( 'SHOW GRANTS' ); // phpcs:ignore -- NOSONAR - requires custom query.
198 $granted_privileges = array();
199
200 foreach ( $grants as $grant ) {
201
202 if (
203 ! preg_match(
204 '/GRANT\s+(.+?)\s+ON\s+(.+?)\s+TO\s+/i',
205 $grant,
206 $matches
207 )
208 ) {
209 continue;
210 }
211
212 $privileges = strtoupper( trim( $matches[1] ) );
213 $scope = trim( $matches[2] );
214
215 // Global privileges.
216 if ( '*.*' === $scope ) {
217 if ( false !== strpos( $privileges, 'ALL PRIVILEGES' ) ) {
218 $granted_privileges = $required_privs;
219 break;
220 }
221
222 $granted_privileges = array_merge(
223 $granted_privileges,
224 array_map( 'trim', explode( ',', $privileges ) )
225 );
226
227 continue;
228 }
229
230 // Database-specific privileges.
231 if ( preg_match( '/^`([^`]+)`\.\*$/', $scope, $db_matches ) ) {
232
233 $grant_db = strtr(
234 $db_matches[1],
235 array(
236 '\_' => '_',
237 '\%' => '%',
238 )
239 );
240
241 if ( $grant_db !== $database_name ) {
242 continue;
243 }
244
245 if ( false !== strpos( $privileges, 'ALL PRIVILEGES' ) ) {
246 $granted_privileges = $required_privs;
247 break;
248 }
249
250 $granted_privileges = array_merge(
251 $granted_privileges,
252 array_map( 'trim', explode( ',', $privileges ) )
253 );
254 }
255 }
256
257 $granted_privileges = array_unique( $granted_privileges );
258
259 return array(
260 'database_grants' => $granted_privileges,
261 'database_missing_privileges' => array_diff(
262 $required_privs,
263 $granted_privileges
264 ),
265 );
266 }
267
268 /**
269 * Detect missing tables.
270 *
271 * @param string $table_name Table name.
272 *
273 * @return array Missing tables.
274 */
275 public static function detect_missing_tables( $tables ) { // phpcs:ignore -- NOSONAR - complex function.
276
277 if ( ! is_array( $tables ) ) {
278 $tables = array();
279 }
280
281 $missing_tables = array(
282 'core' => array(),
283 'extensions' => array(),
284 );
285 // check mainwp tables conditional with other tables.
286 if ( isset( $tables['core'] ) && is_array( $tables['core'] ) ) {
287
288 foreach ( $tables['core'] as $tbl_name => $info ) {
289 if ( empty( $info ) ) {
290 $missing_tables['core'][] = esc_html( $tbl_name );
291 }
292 }
293
294 if ( ! empty( $tables['other'] ) && is_array( $tables['other'] ) ) {
295 static::detect_missing_other_tables( $tables['other'] );
296 }
297
298 if ( ! empty( static::$cache_extensions_detected_issues ) && is_array( static::$cache_extensions_detected_issues ) ) {
299 foreach ( static::$cache_extensions_detected_issues as $ext_slug => $tables_info ) {
300 if ( ! empty( $tables_info['tables_db_info'] ) && is_array( $tables_info['tables_db_info'] ) ) {
301 foreach ( $tables_info['tables_db_info'] as $tbl_name => $info ) {
302 if ( empty( $info ) ) {
303
304 if ( ! isset( $missing_tables['extensions'][ $ext_slug ] ) ) {
305 $missing_tables['extensions'][ $ext_slug ] = array();
306 }
307
308 $missing_tables['extensions'][ $ext_slug ][] = esc_html( $tbl_name );
309 }
310 }
311 }
312 }
313 }
314 }
315
316 return $missing_tables;
317 }
318
319 /**
320 * Checking other tables.
321 *
322 * @param string $table_name Table name.
323 *
324 * @return void
325 */
326 protected static function detect_missing_other_tables( $tables ) { // phpcs:ignore -- NOSONAR - complex function.
327
328 if ( ! is_array( static::$cache_extensions_loaded_info ) ) {
329 static::$cache_extensions_loaded_info = array();
330 }
331
332 foreach ( $tables as $table_name => $info ) {
333 static::detect_missing_extensions_tables( $table_name, $info );
334 }
335 }
336
337 /**
338 * Checking missing extensions tables.
339 *
340 * @param string $table_name Table name.
341 * @param array $info Table info.
342 *
343 * @return void
344 */
345 protected static function detect_missing_extensions_tables( $table_name, $info ) { // phpcs:ignore -- NOSONAR - complex function.
346
347 if ( false === strpos( $table_name, 'mainwp_' ) ) {
348 return;
349 }
350
351 if ( is_array( static::$cache_extensions_detected_issues ) && ! empty( static::$cache_extensions_detected_issues ) ) {
352 foreach ( static::$cache_extensions_detected_issues as $ext_slug => $ext_info ) {
353 if ( is_array( $ext_info ) && isset( $ext_info['tables_db_info'][ $table_name ] ) ) {
354 static::$cache_extensions_detected_issues[ $ext_slug ]['tables_db_info'][ $table_name ] = $info;
355 }
356 }
357 }
358 }
359
360
361 /**
362 * Method prepare_db_and_tables_issues().
363 *
364 * @param array $missing_tables Detected Missing tables.
365 *
366 * @return void
367 */
368 public static function prepare_db_and_tables_issues( $missing_tables ) {
369
370 if ( ! is_array( $missing_tables ) ) {
371 $missing_tables = array();
372 }
373
374 $db_issues_details = '';
375
376 if ( ! empty( $missing_tables['core'] ) ) {
377 $db_issues_details = sprintf(
378 '<strong>' . esc_html__( 'Detected missing core tables', 'mainwp' ) . '</strong>: %1$s.',
379 esc_html( implode( ', ', $missing_tables['core'] ) )
380 );
381 }
382
383 if ( ! empty( $missing_tables['extensions'] ) ) {
384 foreach ( $missing_tables['extensions'] as $ext_slug => $tbls ) {
385 $title = is_array( static::$cache_extensions_loaded_info ) && isset( static::$cache_extensions_loaded_info[ $ext_slug ]['title'] ) ? static::$cache_extensions_loaded_info[ $ext_slug ]['title'] : esc_html__( 'other', 'mainwp' );
386 $title = MainWP_Extensions_Handler::polish_string_name( $title );
387
388 $db_issues_details .= sprintf(
389 ' <strong>%1$s</strong>: %2$s.',
390 esc_html( $title ),
391 esc_html( implode( ', ', $tbls ) )
392 );
393 }
394 }
395
396 if ( is_array( static::$cache_extensions_detected_issues ) ) {
397 if ( ! empty( static::$cache_extensions_detected_issues['found_missing_columns'] ) ) {
398 $found_count = array_sum(
399 array_map( 'count', static::$cache_extensions_detected_issues['found_missing_columns'] )
400 );
401
402 $db_issues_details .= sprintf(
403 ' <strong>' . esc_html__( 'Found %1$s missing columns in tables.', 'mainwp' ) . '</strong>',
404 $found_count
405 );
406 }
407
408 if ( ! empty( static::$cache_extensions_detected_issues['found_invalid_columns'] ) && is_array( static::$cache_extensions_detected_issues['found_invalid_columns'] ) ) {
409
410 $invalid_count = 0;
411
412 foreach ( static::$cache_extensions_detected_issues['found_invalid_columns'] as $invalid_cols ) {
413 $invalid_count += count( $invalid_cols );
414 }
415
416 $db_issues_details .= sprintf(
417 ' <strong>' . esc_html__( 'Found %1$s invalid columns definitions in tables.', 'mainwp' ) . '</strong>',
418 $invalid_count
419 );
420 }
421 }
422
423 if ( ! empty( $db_issues_details ) ) {
424 static::$cache_detected_issues[] = array(
425 'severity' => 'error',
426 'title' => esc_html__( 'MainWP datatable Issues', 'mainwp' ),
427 'detail_html' => $db_issues_details,
428 'anchor' => 'mainwp-system-report-mainwp-database-table',
429 'kb_url' => 'https://docs.mainwp.com/troubleshooting/resolve-system-requirement-issues/',
430 );
431 }
432 }
433
434 /**
435 * Get detected database issues.
436 *
437 * Used by get_primary_issues().
438 *
439 * @return array Detected database issues.
440 */
441 public static function get_detected_db_issues() {
442 return static::$cache_detected_issues;
443 }
444
445
446 /**
447 * Method detect_table_columns_issues().
448 *
449 * @param string $table_name Table name.
450 * @param string $type Type Table: core or other.
451 *
452 * @return array Missing and Invalid columns.
453 */
454 public static function detect_table_columns_issues( $table_name, $type ) { //phpcs:ignore -- NOSONAR - complex function.
455
456 global $wpdb;
457
458 if ( ! is_string( $table_name ) || empty( $table_name ) ) {
459 return array();
460 }
461
462 $table_name = esc_sql( $table_name );
463
464 $actual_columns = $wpdb->get_col( // phpcs:ignore -- NOSONAR - custom query.
465 "SHOW COLUMNS FROM `{$table_name}`", // phpcs:ignore -- NOSONAR - escaped table name.
466 0
467 );
468
469 $missing = array();
470 $invalid_cols = array();
471 $expected_cols = array();
472 $expected_schemas = array();
473
474 if ( 'core' === $type ) {
475 $core_tbls_schema = static::get_core_db_schema_info();
476 if ( is_array( $core_tbls_schema ) && isset( $core_tbls_schema[ $table_name ] ) && is_array( $core_tbls_schema[ $table_name ] ) && isset( $core_tbls_schema[ $table_name ]['columns'] ) && is_array( $core_tbls_schema[ $table_name ]['columns'] ) ) {
477 $expected_cols = array_keys( $core_tbls_schema[ $table_name ]['columns'] );
478 $expected_schemas = $core_tbls_schema[ $table_name ]['columns'];
479 }
480 } elseif ( ! empty( $actual_columns ) && ! empty( static::$cache_extensions_loaded_info ) && is_array( static::$cache_extensions_loaded_info ) ) {
481 foreach ( static::$cache_extensions_loaded_info as $ext_info ) {
482 if ( ! empty( $ext_info['schema'] ) && is_array( $ext_info['schema'] ) && isset( $ext_info['schema'][ $table_name ] ) && is_array( $ext_info['schema'][ $table_name ] ) && isset( $ext_info['schema'][ $table_name ]['columns'] ) && is_array( $ext_info['schema'][ $table_name ]['columns'] ) ) {
483 $expected_cols = array_keys( $ext_info['schema'][ $table_name ]['columns'] );
484 $expected_schemas = $ext_info['schema'][ $table_name ]['columns'];
485 break;
486 }
487 }
488 }
489
490 if ( is_array( $expected_cols ) && ! empty( $expected_cols ) ) {
491 $missing = array_diff(
492 $expected_cols,
493 $actual_columns
494 );
495 }
496
497 if ( ! empty( $missing ) ) {
498 if ( ! isset( static::$cache_extensions_detected_issues['found_missing_columns'] ) ) {
499 static::$cache_extensions_detected_issues['found_missing_columns'] = array();
500 }
501 if ( ! isset( static::$cache_extensions_detected_issues['found_missing_columns'][ $table_name ] ) ) {
502 static::$cache_extensions_detected_issues['found_missing_columns'][ $table_name ] = array();
503 }
504 static::$cache_extensions_detected_issues['found_missing_columns'][ $table_name ] = $missing;
505 }
506
507 if ( is_array( $expected_schemas ) && ! empty( $expected_schemas ) ) {
508
509 $actual_schemas = $wpdb->get_results(
510 "SHOW FULL COLUMNS FROM `{$table_name}`", // phpcs:ignore -- NOSONAR - escaped table name.
511 ARRAY_A
512 );
513
514 $schemas_by_column = array_column(
515 $actual_schemas,
516 null,
517 'Field'
518 );
519
520 $invalid_cols = static::detect_invalid_column_definitions( $table_name, $expected_schemas, $schemas_by_column );
521
522 if ( ! empty( $invalid_cols ) ) {
523 if ( ! isset( static::$cache_extensions_detected_issues['found_invalid_columns'] ) ) {
524 static::$cache_extensions_detected_issues['found_invalid_columns'] = array();
525 }
526 if ( ! isset( static::$cache_extensions_detected_issues['found_invalid_columns'][ $table_name ] ) ) {
527 static::$cache_extensions_detected_issues['found_invalid_columns'][ $table_name ] = array();
528 }
529 static::$cache_extensions_detected_issues['found_invalid_columns'][ $table_name ] = $invalid_cols;
530 }
531 }
532
533 return array(
534 'missing_columns' => $missing,
535 'invalid_columns' => $invalid_cols,
536
537 );
538 }
539
540
541 /**
542 * Check for incorrect column definitions in an extension table.
543 *
544 * @param string $table_name Table name information.
545 * @param array $schema Table schema information.
546 * @param array $actual_cols_schemas Actual columns information.
547 *
548 * @return array $issues Column definitions issues.
549 */
550 protected static function detect_invalid_column_definitions( $table_name, $columns, $actual_cols_schemas ) { // phpcs:ignore -- NOSONAR - complex function.
551
552 $issues = array();
553 foreach ( $columns as $column_name => $expected_definition ) {
554
555 if ( ! isset( $actual_cols_schemas[ $column_name ] ) ) {
556 continue; // already handled by missing column check.
557 }
558
559 $actual = static::get_column_definition( $actual_cols_schemas[ $column_name ] );
560
561 $expected = static::normalize_definition( $expected_definition );
562
563 $actual = static::normalize_definition( $actual );
564
565 if ( $expected !== $actual ) {
566
567 $issues[] = array(
568 'table_name' => $table_name,
569 'column' => $column_name,
570 'expected' => $expected,
571 'actual' => $actual,
572 );
573 }
574 }
575 return $issues;
576 }
577
578
579 /**
580 * Normalize a column definition for comparison.
581 *
582 * Explicit and implicit nullable forms, including DEFAULT NULL, compare
583 * as the same definition MySQL reports when the default is omitted.
584 *
585 * @param string $definition Column definition.
586 *
587 * @return string
588 */
589 protected static function normalize_definition( $definition ) {
590
591 $definition = strtoupper( trim( $definition ) );
592
593 // Normalize whitespace.
594 $definition = preg_replace( '/\s+/', ' ', $definition );
595
596 // Normalize spaces inside type declarations.
597 $definition = preg_replace(
598 '/\(\s*(\d+)\s*,\s*(\d+)\s*\)/',
599 '($1,$2)',
600 $definition
601 );
602
603 // Normalize repeated quotes.
604 $definition = str_replace( "''", '""', $definition );
605 $definition = preg_replace(
606 '/DEFAULT\s+["\']{2,}/',
607 'DEFAULT ""',
608 $definition
609 );
610
611 // Remove integer display widths.
612 $definition = preg_replace(
613 '/\b(TINYINT|SMALLINT|MEDIUMINT|INT|BIGINT)\(\d+\)/',
614 '$1',
615 $definition
616 );
617
618 // Normalize NULL handling.
619 // MySQL reports an explicit NULL default as no default at all.
620 // Remove that default before the nullable marker so both forms compare equal.
621 // Quoted literals are skipped, including escapes and doubled quotes, so a
622 // default value that contains this text is left intact.
623 $quoted = '/(?:\'(?:\'\'|\\\\.|[^\'\\\\])*\'|"(?:\\\\.|""|[^"\\\\])*")(*SKIP)(*F)|';
624
625 $definition = preg_replace(
626 $quoted . '\s+DEFAULT\s+NULL\b/',
627 '',
628 $definition
629 );
630
631 $definition = preg_replace(
632 $quoted . '(?<!NOT)\s+NULL\b/',
633 '',
634 $definition
635 );
636
637 // Normalize quoted numeric defaults.
638 $definition = preg_replace(
639 "/DEFAULT\s+'(-?\d+(?:\.\d+)?)'/",
640 'DEFAULT $1',
641 $definition
642 );
643
644 // Normalize decimal/float zero defaults.
645 $definition = preg_replace(
646 '/DEFAULT\s+0+\.0+\b/',
647 'DEFAULT 0',
648 $definition
649 );
650
651 if ( preg_match( '/^(TEXT|MEDIUMTEXT|LONGTEXT)\b/', $definition ) ) {
652 $definition = preg_replace(
653 '/\s+DEFAULT\s+(""|\'\')$/',
654 '',
655 $definition
656 );
657 }
658
659 return trim( $definition );
660 }
661
662
663 /**
664 * Normalize a column default value.
665 *
666 * @param mixed $default_definition Default value.
667 *
668 * @return string|null
669 */
670 private static function normalize_default( $default_definition ) {
671
672 if ( null === $default_definition ) {
673 return null;
674 }
675
676 $normalized = strtoupper( trim( $default_definition ) );
677
678 if ( in_array( $normalized, array( 'CURRENT_TIMESTAMP', 'CURRENT_TIMESTAMP()' ), true ) ) {
679 return 'CURRENT_TIMESTAMP';
680 }
681
682 if ( is_numeric( $default_definition ) ) {
683 return $default_definition;
684 }
685
686 return "'" . $default_definition . "'";
687 }
688
689
690 /**
691 * Method get_column_definition().
692 *
693 * @param array $column DB column values.
694 *
695 * @return string definition.
696 */
697 protected static function get_column_definition( $column ) {
698 $definition = strtoupper( $column['Type'] );
699
700 if ( 'NO' === $column['Null'] ) {
701 $definition .= ' NOT NULL';
702 }
703
704 $default = self::normalize_default( $column['Default'] );
705
706 if ( null !== $default ) {
707 $definition .= ' DEFAULT ' . $default;
708 }
709
710 if ( false !== stripos( $column['Extra'], 'auto_increment' ) ) {
711 $definition .= ' AUTO_INCREMENT';
712 }
713
714 return $definition;
715 }
716
717
718 /**
719 * Method get_core_db_schema_info().
720 *
721 * @return array Core schema info.
722 */
723 public static function get_core_db_schema_info() {
724
725 static $core_schema = null;
726
727 if ( null !== $core_schema ) {
728 return $core_schema;
729 }
730
731 $core_schema = include MAINWP_PLUGIN_DIR . 'includes/core-db-schema-info.php'; //phpcs:ignore -- NOSONAR - custom requires.
732
733 if ( ! is_array( $core_schema ) ) {
734 $core_schema = array();
735 }
736
737 $core_schema = apply_filters( 'mainwp_database_core_tables_schema', $core_schema );
738
739 if ( is_array( $core_schema ) ) {
740 global $wpdb;
741
742 $prefix = $wpdb->prefix . 'mainwp_';
743
744 $result = array();
745
746 foreach ( $core_schema as $key => $value ) {
747 $result[ $prefix . $key ] = $value;
748 }
749 $core_schema = $result;
750 }
751
752 if ( ! is_array( $core_schema ) ) {
753 $core_schema = array();
754 }
755
756 return $core_schema;
757 }
758
759 /**
760 * Method get_loaded_extension_title().
761 *
762 * @param string $ext_slug Extension slug.
763 *
764 * @return string Extension info title.
765 */
766 public static function get_loaded_extension_title( $ext_slug ) {
767 $title = is_array( static::$cache_extensions_loaded_info ) && isset( static::$cache_extensions_loaded_info[ $ext_slug ]['title'] ) ? static::$cache_extensions_loaded_info[ $ext_slug ]['title'] : esc_html__( 'other', 'mainwp' );
768 return MainWP_Extensions_Handler::polish_string_name( $title );
769 }
770 }
771