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.3 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 All 156 releases
← All changes | class/class-mainwp-install.php +249 -35 6.0.11 → 6.2.1 View file →
@@ -30,9 +30,9 @@
30 30 * Private variable to hold the database version info.
31 31 *
32 32 * @var string DB version info.
33 33 */
34 - protected $mainwp_db_version = '9.0.1.4'; // NOSONAR - no IP.
34 + protected $mainwp_db_version = '9.0.2.5'; // NOSONAR - no IP. 9.0.2.4 drops the stray unique index on backup progress task_id. Bumped for MWP-1566 sibling-dir chmod migration (MWP-1558 follow-up). Original 9.0.2.0 bump for MWP-1557/1558.
35 35
36 36 /**
37 37 * Protected variable to hold the database option name.
38 38 *
@@ -40,8 +40,18 @@
40 40 */
41 41 protected $option_db_key = 'mainwp_db_version';
42 42
43 43 /**
44 + * Network-scoped like mainwp_db_version, so every blog on a multisite sees the pending repair.
45 + */
46 + const BACKUP_PROGRESS_INDEX_REPAIR_PENDING = 'mainwp_backup_progress_index_repair_pending';
47 +
48 + /**
49 + * Network-scoped like mainwp_db_version, so every blog on a multisite sees the pending repair.
50 + */
51 + const WP_GROUP_MISSING_PRIMARY_KEY_REPAIR_PENDING = 'mainwp_wp_group_missing_primary_key_repair_pending';
52 +
53 + /**
44 54 * Private static variable to hold the single instance of the class.
45 55 *
46 56 * @static
47 57 *
@@ -98,8 +108,14 @@
98 108 $currentVersion = false;
99 109 }
100 110
101 111 if ( $currentVersion === $this->mainwp_db_version ) {
112 + // The index repair keeps its own marker so a failed DROP is retried at
113 + // most hourly, without holding the DB version back and re-running every
114 + // older migration on each load.
115 + $this->maybe_retry_backup_progress_index_repair();
116 +
117 + $this->maybe_retry_repair_wp_group_primary_key();
102 118 return;
103 119 }
104 120
105 121 $this->pre_update_tables();
@@ -280,14 +296,12 @@
280 296 wp_group_id int(11) NOT NULL auto_increment,
281 297 wpid int(11) NOT NULL,
282 298 groupid int(11) NOT NULL,
283 299 KEY idx_wpid (wpid),
284 - KEY idx_groupid (groupid)';
285 - if ( empty( $currentVersion ) || version_compare( $currentVersion, '8.57', '<=' ) ) {
286 - $tbl .= ',
287 - PRIMARY KEY (wp_group_id) ';
288 - }
289 - $tbl .= ') ' . $charset_collate;
300 + KEY idx_groupid (groupid),
301 + PRIMARY KEY (wp_group_id)
302 + ) ' . $charset_collate;
303 +
290 304 $sql[] = $tbl;
291 305
292 306 $tbl = 'CREATE TABLE ' . $this->table_name( 'lookup_item_objects' ) . ' (
293 307 lookup_id bigint(20) unsigned NOT NULL auto_increment,
@@ -304,9 +318,9 @@
304 318 }
305 319 $tbl .= ') ' . $charset_collate . ';';
306 320 $sql[] = $tbl;
307 321
308 - $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_backup_progress' ) . " (
322 + $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_backup_progress' ) . " (
309 323 task_id int(11) NOT NULL,
310 324 wp_id int(11) NOT NULL,
311 325 dtsFetched int(11) NOT NULL DEFAULT 0,
312 326 fetchResult text NOT NULL DEFAULT '',
@@ -318,12 +332,8 @@
318 332 attempts int(11) NOT NULL DEFAULT 0,
319 333 last_error text NOT NULL DEFAULT '',
320 334 pid int(11) NOT NULL DEFAULT 0,
321 335 KEY idx_task_id (task_id)";
322 - if ( empty( $currentVersion ) || version_compare( $currentVersion, '8.53', '<=' ) ) {
323 - $tbl .= ',
324 - UNIQUE (task_id)';
325 - }
326 336 $tbl .= ') ' . $charset_collate;
327 337 $sql[] = $tbl;
328 338
329 339 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_backup' ) . ' (
@@ -367,9 +377,9 @@
367 377 user_id bigint(20) unsigned NOT NULL,
368 378 description varchar(200) NULL,
369 379 permissions varchar(10) NOT NULL,
370 380 consumer_key char(64) NOT NULL,
371 - consumer_secret char(43) NOT NULL,
381 + consumer_secret varchar(255) NOT NULL,
372 382 nonces longtext NULL,
373 383 truncated_key char(7) NOT NULL,
374 384 key_pass char(64) NOT NULL DEFAULT "",
375 385 key_type tinyint(1) NOT NULL DEFAULT 0,
@@ -374,10 +384,9 @@
374 384 key_pass char(64) NOT NULL DEFAULT "",
375 385 key_type tinyint(1) NOT NULL DEFAULT 0,
376 386 `enabled` tinyint(1) DEFAULT 0,
377 387 last_access datetime NULL default null,
378 - KEY consumer_key (consumer_key),
379 - KEY consumer_secret (consumer_secret)';
388 + KEY consumer_key (consumer_key)';
380 389 if ( empty( $currentVersion ) || version_compare( $currentVersion, '9.0.0.9', '<=' ) ) { // NOSONAR - none IP.
381 390 $tbl .= ',
382 391 PRIMARY KEY (key_id) ';
383 392 }
@@ -494,12 +503,12 @@
494 503 $sslColumns = array(
495 504 'nossl',
496 505 'nosslkey',
497 506 );
498 - $wp_table = esc_sql( $this->table_name( 'wp' ) );
507 + $wp_table = esc_sql( $this->table_name( 'wp' ) );
499 508 foreach ( $sslColumns as $col ) {
500 509 $col = esc_sql( $col );
501 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
510 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
502 511 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$col}" );
503 512 }
504 513 }
505 514
@@ -516,17 +525,17 @@
516 525 );
517 526
518 527 foreach ( $rankColumns as $rankColumn ) {
519 528 $rankColumn = esc_sql( $rankColumn );
520 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
529 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
521 530 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$rankColumn}" );
522 531 }
523 532
524 - $syncColumns = array( 'uptodate' );
533 + $syncColumns = array( 'uptodate' );
525 534 $wp_sync_table = esc_sql( $this->table_name( 'wp_sync' ) );
526 535 foreach ( $syncColumns as $column ) {
527 536 $column = esc_sql( $column );
528 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
537 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
529 538 $this->wpdb->query( "ALTER TABLE {$wp_sync_table} DROP COLUMN {$column}" );
530 539 }
531 540 }
532 541
@@ -534,16 +543,16 @@
534 543 if ( version_compare( $currentVersion, '8.35', '<' ) ) {
535 544 $delColumns = array( 'offline_checks' );
536 545 foreach ( $delColumns as $column ) {
537 546 $column = esc_sql( $column );
538 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
547 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
539 548 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$column}" );
540 549 }
541 - $delColumns = array( 'heatMap' );
550 + $delColumns = array( 'heatMap' );
542 551 $users_table = esc_sql( $this->table_name( 'users' ) );
543 552 foreach ( $delColumns as $column ) {
544 553 $column = esc_sql( $column );
545 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
554 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
546 555 $this->wpdb->query( "ALTER TABLE {$users_table} DROP COLUMN {$column}" );
547 556 }
548 557 }
549 558
@@ -551,9 +560,9 @@
551 560 if ( version_compare( $currentVersion, '8.42', '<' ) ) {
552 561 $delColumns = array( 'offlineChecksOnlineNotification' );
553 562 foreach ( $delColumns as $column ) {
554 563 $column = esc_sql( $column );
555 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
564 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
556 565 $this->wpdb->query( "ALTER TABLE {$users_table} DROP COLUMN {$column}" );
557 566 }
558 567 }
559 568
@@ -558,20 +567,29 @@
558 567 }
559 568
560 569 // fix missing PRIMARY keys.
561 570 if ( version_compare( $currentVersion, '8.53', '<=' ) ) {
562 - $wp_options_table = esc_sql( $this->table_name( 'wp_options' ) );
571 + $wp_options_table = esc_sql( $this->table_name( 'wp_options' ) );
563 572 $wp_settings_backup_table = esc_sql( $this->table_name( 'wp_settings_backup' ) );
564 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
573 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
565 574 $this->wpdb->query( "ALTER TABLE {$wp_options_table} ADD opt_id int NOT NULL AUTO_INCREMENT PRIMARY KEY" );
566 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
575 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
567 576 $this->wpdb->query( "ALTER TABLE {$wp_settings_backup_table} ADD set_id int NOT NULL AUTO_INCREMENT PRIMARY KEY" );
568 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
577 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
569 578 $this->wpdb->query( "ALTER TABLE {$wp_sync_table} ADD sync_id int NOT NULL AUTO_INCREMENT PRIMARY KEY" );
570 579 }
571 580
572 581 $this->update_optimize_indexes_55( $currentVersion );
573 582
583 + // dbDelta never drops an index, so the stray unique key needs an explicit migration.
584 + if ( version_compare( $currentVersion, '9.0.2.4', '<' ) ) { // NOSONAR - no ip.
585 + $this->repair_backup_progress_index();
586 + }
587 +
588 + if ( ! empty( $currentVersion ) && version_compare( $currentVersion, '9.0.2.5', '<' ) ) { // NOSONAR - no ip.
589 + $this->repair_wp_group_primary_key();
590 + }
591 +
574 592 $this->wpdb->suppress_errors( $suppress );
575 593 MainWP_DB_Client::instance()->check_to_updates_reports_data_861( $currentVersion );
576 594 }
577 595
@@ -591,11 +609,207 @@
591 609 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp' ) . ' ADD INDEX idx_userid (userid)' ); //phpcs:ignore -- ok.
592 610 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp' ) . ' ADD INDEX idx_client_id (client_id)' ); //phpcs:ignore -- ok.
593 611 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp' ) . ' ADD INDEX idx_url (url(191))' ); //phpcs:ignore -- ok.
594 612 }
613 + // MWP-1540: widen consumer_secret so wp_hash_password output (variable length,
614 + // typically 34 chars for $P$ but up to ~150 for argon2) fits without truncation,
615 + // and drop the unused KEY consumer_secret index (lookups are by HMAC'd consumer_key,
616 + // never by consumer_secret). dbDelta does not reliably MODIFY existing column types
617 + // or DROP indexes, so both changes are applied explicitly here.
618 + if ( ! empty( $current_ver ) && version_compare( $current_ver, '9.0.1.5', '<' ) ) { // NOSONAR - no ip.
619 + $api_keys_table = $this->table_name( 'api_keys' );
620 + // Drop the unused index FIRST so the column type change is unambiguous.
621 + $existing_indexes = $this->wpdb->get_col( "SHOW INDEX FROM {$api_keys_table} WHERE Key_name = 'consumer_secret'", 2 ); // phpcs:ignore -- table name is internal.
622 + if ( ! empty( $existing_indexes ) ) {
623 + $this->wpdb->query( "ALTER TABLE {$api_keys_table} DROP INDEX consumer_secret" ); // phpcs:ignore -- table name is internal.
624 + }
625 + $this->wpdb->query( "ALTER TABLE {$api_keys_table} MODIFY COLUMN consumer_secret varchar(255) NOT NULL" ); // phpcs:ignore -- table name is internal.
626 +
627 + // Confirm the MODIFY actually took effect before we trust this migration.
628 + // WP 6.5+ produces bcrypt hashes around 60 characters, so a silent failure
629 + // would leave the column at char(43) and truncate every freshly hashed
630 + // secret on insert, quietly breaking auth on any newly created key. On a
631 + // width mismatch we set a flag option that the dashboard surfaces as an
632 + // admin notice; mainwp_notice_wp_mail_failed in class-mainwp-system.php
633 + // uses the same shape.
634 + $col_def = $this->wpdb->get_row( "SHOW COLUMNS FROM {$api_keys_table} LIKE 'consumer_secret'" ); // phpcs:ignore -- table name is internal.
635 + if ( empty( $col_def ) || false === stripos( (string) $col_def->Type, 'varchar(255)' ) ) {
636 + update_option( 'mainwp_notice_consumer_secret_migration_failed', current_time( 'mysql' ) );
637 + } else {
638 + delete_option( 'mainwp_notice_consumer_secret_migration_failed' );
639 + }
640 + }
595 641 }
596 642
597 643 /**
644 + * Retry a pending backup progress index repair, no more than once an hour.
645 + *
646 + * A DB user without ALTER never clears the marker, so an unthrottled retry
647 + * would run two SHOW INDEX plus a failing ALTER on every single request.
648 + *
649 + * @return bool True when a repair attempt was made.
650 + */
651 + public function maybe_retry_repair_wp_group_primary_key() {
652 + $pending = (int) get_site_option( self::WP_GROUP_MISSING_PRIMARY_KEY_REPAIR_PENDING );
653 + if ( empty( $pending ) || time() - $pending < HOUR_IN_SECONDS ) {
654 + return false;
655 + }
656 +
657 + $suppress = $this->wpdb->suppress_errors();
658 + $this->repair_wp_group_primary_key();
659 + $this->wpdb->suppress_errors( $suppress );
660 +
661 + return true;
662 + }
663 +
664 + /**
665 + * Repair the wp_group table primary key for legacy installations.
666 + *
667 + * To fix MWP-1794.
668 + *
669 + * @return void
670 + */
671 + private function repair_wp_group_primary_key() {
672 + $wp_group_table = esc_sql( $this->table_name( 'wp_group' ) );
673 +
674 + $column_exists = $this->wpdb->get_var(
675 + "SHOW COLUMNS FROM {$wp_group_table} LIKE 'wp_group_id'" // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped and query contains no user input.
676 + );
677 +
678 + if ( empty( $column_exists ) ) {
679 + $this->wpdb->query(
680 + "ALTER TABLE {$wp_group_table}
681 + ADD COLUMN wp_group_id int(11) NOT NULL auto_increment PRIMARY KEY FIRST" // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped and query contains no user input.
682 + );
683 + } else {
684 + $primary_key = $this->wpdb->get_var(
685 + "SHOW INDEX FROM {$wp_group_table}
686 + WHERE Key_name = 'PRIMARY'
687 + AND Column_name = 'wp_group_id'" // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped and query contains no user input.
688 + );
689 +
690 + if ( empty( $primary_key ) ) {
691 + $this->wpdb->query(
692 + "ALTER TABLE {$wp_group_table}
693 + ADD PRIMARY KEY (wp_group_id)" // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped and query contains no user input.
694 + );
695 + }
696 + }
697 +
698 + $primary_key = $this->wpdb->get_var(
699 + "SHOW INDEX FROM {$wp_group_table}
700 + WHERE Key_name = 'PRIMARY'
701 + AND Column_name = 'wp_group_id'" // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped and query contains no user input.
702 + );
703 +
704 + if ( ! empty( $primary_key ) ) {
705 + delete_site_option( self::WP_GROUP_MISSING_PRIMARY_KEY_REPAIR_PENDING );
706 + } else {
707 + // A timestamp, not a formatted date: the retry throttle compares it against time().
708 + update_site_option( self::WP_GROUP_MISSING_PRIMARY_KEY_REPAIR_PENDING, time() );
709 + }
710 + }
711 +
712 + /**
713 + * Retry a pending backup progress index repair, no more than once an hour.
714 + *
715 + * A DB user without ALTER never clears the marker, so an unthrottled retry
716 + * would run two SHOW INDEX plus a failing ALTER on every single request.
717 + *
718 + * @return bool True when a repair attempt was made.
719 + */
720 + public function maybe_retry_backup_progress_index_repair() {
721 + $pending = (int) get_site_option( self::BACKUP_PROGRESS_INDEX_REPAIR_PENDING );
722 + if ( empty( $pending ) || time() - $pending < HOUR_IN_SECONDS ) {
723 + return false;
724 + }
725 +
726 + $this->repair_backup_progress_index();
727 +
728 + return true;
729 + }
730 +
731 + /**
732 + * Run the backup progress index repair and remember whether it still needs a retry.
733 + *
734 + * @return bool True when the repair is confirmed complete.
735 + */
736 + public function repair_backup_progress_index() {
737 + // The retry from install() runs outside post_update()'s suppression; a
738 + // persistently failing ALTER must not log or print on every request.
739 + $suppress = $this->wpdb->suppress_errors();
740 + $repaired = $this->drop_backup_progress_unique_index();
741 + $this->wpdb->suppress_errors( $suppress );
742 + if ( $repaired ) {
743 + delete_site_option( self::BACKUP_PROGRESS_INDEX_REPAIR_PENDING );
744 + } else {
745 + // A timestamp, not a formatted date: the retry throttle compares it against time().
746 + update_site_option( self::BACKUP_PROGRESS_INDEX_REPAIR_PENDING, time() );
747 + }
748 + return $repaired;
749 + }
750 +
751 + /**
752 + * Drop a stray unique index on the backup progress task_id column.
753 + *
754 + * Fresh installs made between DB 8.53 and the schema fix created
755 + * wp_backup_progress with UNIQUE (task_id). Progress rows are per task and
756 + * per site, so a task covering two or more sites could never insert its
757 + * second row. dbDelta never drops an index, so the stray key has to go here.
758 + *
759 + * @return bool True when no such key remains, false when one survived a failed DROP.
760 + */
761 + public function drop_backup_progress_unique_index() {
762 + $table = $this->table_name( 'wp_backup_progress' );
763 + $keys = $this->find_backup_progress_task_id_unique_keys();
764 + if ( null === $keys ) {
765 + return false;
766 + }
767 +
768 + foreach ( $keys as $key_name ) {
769 + $this->wpdb->query( "ALTER TABLE {$table} DROP INDEX `{$key_name}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DDL statement; table name is a hardcoded internal identifier, key name comes from SHOW INDEX and is allowlisted below.
770 + }
771 +
772 + // suppress_errors() hides a failed DROP, so the table is the only source of truth.
773 + return array() === $this->find_backup_progress_task_id_unique_keys();
774 + }
775 +
776 + /**
777 + * Names of the unique keys on the backup progress table that cover task_id alone.
778 + *
779 + * A composite unique key over task_id and wp_id is legitimate and is left out.
780 + *
781 + * @return array|null Key names, or null when the lookup itself failed and nothing can be concluded.
782 + */
783 + protected function find_backup_progress_task_id_unique_keys() {
784 + $table = $this->table_name( 'wp_backup_progress' );
785 +
786 + // suppress_errors() is on in post_update(), so a failed SHOW INDEX looks like an empty index list; last_error is the only tell.
787 + $this->wpdb->last_error = '';
788 + $indexes = $this->wpdb->get_results( "SHOW INDEX FROM {$table} WHERE Non_unique = 0", ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DDL introspection; table name is a hardcoded internal identifier, no user input involved.
789 + if ( '' !== $this->wpdb->last_error ) {
790 + return null;
791 + }
792 +
793 + $key_columns = array();
794 + foreach ( (array) $indexes as $index ) {
795 + if ( 'PRIMARY' === $index['Key_name'] ) {
796 + continue;
797 + }
798 + $key_columns[ $index['Key_name'] ][] = $index['Column_name'];
799 + }
800 +
801 + $keys = array();
802 + foreach ( $key_columns as $key_name => $columns ) {
803 + if ( array( 'task_id' ) === $columns && preg_match( '/^[A-Za-z0-9_]+$/', $key_name ) ) {
804 + $keys[] = $key_name;
805 + }
806 + }
807 +
808 + return $keys;
809 + }
810 +
811 + /**
598 812 * Method pre_update_tables()
599 813 *
600 814 * Handle pre update tables.
601 815 *
@@ -612,9 +826,9 @@
612 826 $suppress = $this->wpdb->suppress_errors();
613 827
614 828 if ( version_compare( $currentVersion, '8.98', '<=' ) ) {
615 829 $wp_table = esc_sql( $this->table_name( 'wp' ) );
616 - $existing_columns = $this->wpdb->get_col( "SHOW COLUMNS FROM {$wp_table}", 0 ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DDL introspection; table name is a hardcoded internal identifier escaped via esc_sql(), no user input involved.
830 + $existing_columns = $this->wpdb->get_col( "SHOW COLUMNS FROM {$wp_table}", 0 ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DDL introspection; table name is a hardcoded internal identifier escaped via esc_sql(), no user input involved.
617 831 foreach ( array( 'backups', 'note_lastupdate', 'pages' ) as $column ) {
618 832 if ( in_array( $column, $existing_columns, true ) ) {
619 833 $this->wpdb->query( 'ALTER TABLE ' . $wp_table . ' DROP COLUMN ' . esc_sql( $column ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DDL statement; table and column names are hardcoded internal identifiers escaped via esc_sql(), no user input involved.
620 834 }
@@ -649,13 +863,13 @@
649 863 'dtsSyncStart',
650 864 'dtsAutomaticSync',
651 865 'dtsAutomaticSyncStart',
652 866 );
653 - $wp_table = esc_sql( $this->table_name( 'wp' ) );
867 + $wp_table = esc_sql( $this->table_name( 'wp' ) );
654 868 $wp_sync_table = esc_sql( $this->table_name( 'wp_sync' ) );
655 869 foreach ( $wpSyncColumns as $wpSyncColumn ) {
656 870 $wpSyncColumn = esc_sql( $wpSyncColumn );
657 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
871 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
658 872 $rslts = $this->wpdb->get_results( "SELECT id,{$wpSyncColumn} FROM {$wp_table}", ARRAY_A );
659 873 if ( empty( $rslts ) ) {
660 874 continue;
661 875 }
@@ -660,9 +874,9 @@
660 874 continue;
661 875 }
662 876
663 877 foreach ( $rslts as $rslt ) {
664 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql and query uses proper prepare.
878 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql and query uses proper prepare.
665 879 $exists = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT wpid FROM {$wp_sync_table} WHERE wpid = %d", $rslt['id'] ), ARRAY_A );
666 880 if ( empty( $exists ) ) {
667 881 $this->wpdb->insert(
668 882 $this->table_name( 'wp_sync' ),
@@ -676,9 +890,9 @@
676 890 }
677 891 }
678 892
679 893 $suppress = $this->wpdb->suppress_errors();
680 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
894 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
681 895 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$wpSyncColumn}" );
682 896 $this->wpdb->suppress_errors( $suppress );
683 897 }
684 898
@@ -692,9 +906,9 @@
692 906 'recent_pages',
693 907 );
694 908 foreach ( $optionColumns as $optionColumn ) {
695 909 $optionColumn = esc_sql( $optionColumn );
696 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
910 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
697 911 $rslts = $this->wpdb->get_results( "SELECT id,{$optionColumn} FROM {$wp_table}", ARRAY_A );
698 912 if ( empty( $rslts ) ) {
699 913 continue;
700 914 }
@@ -703,9 +917,9 @@
703 917 static::update_website_option( (object) $rslt, $optionColumn, $rslt[ $optionColumn ] );
704 918 }
705 919
706 920 $suppress = $this->wpdb->suppress_errors();
707 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
921 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
708 922 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$optionColumn}" );
709 923 $this->wpdb->suppress_errors( $suppress );
710 924 }
711 925 }