PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.2.6
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.2.6
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
← All changes | includes/Core/Migration.php +8 -108 4.9.24.2.6 View file →
@@ -1,12 +1,8 @@
1 1 <?php
2 -// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange -- one-shot data/schema migrations; caching unwanted.
2 +
3 3 namespace WPDeveloper\BetterDocs\Core;
4 4
5 -if ( ! defined( 'ABSPATH' ) ) {
6 - exit;
7 -}
8 -
9 5 use WP_Query;
10 6 use WPDeveloper\BetterDocs\Utils\Base;
11 7 use WPDeveloper\BetterDocs\Utils\Database;
12 8 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
@@ -35,10 +31,8 @@
35 31 }
36 32 }
37 33
38 34 $this->search_migration();
39 - $this->fix_search_table_collation();
40 - $this->backfill_search_keyword_hash();
41 35
42 36 /**
43 37 * Settings Migration
44 38 */
@@ -55,9 +49,9 @@
55 49 $args = [
56 50 'post_type' => 'docs',
57 51 'post_status' => 'publish',
58 52 'posts_per_page' => -1,
59 - 'suppress_filters' => true, // phpcs:ignore WordPressVIPMinimum.Hooks.PreGetPosts.PreGetPosts,WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- migration must run on raw posts without language filters.
53 + 'suppress_filters' => true,
60 54 's' => $key
61 55 ];
62 56
63 57 $loop = new WP_Query( $args );
@@ -68,15 +62,14 @@
68 62 $count = 0;
69 63 $not_found_count = $value;
70 64 }
71 65
72 - // Use BINARY comparison to avoid collation mismatch errors
73 - $keyword = $wpdb->get_var(
66 + $keyword = $wpdb->get_var(
74 67 $wpdb->prepare(
75 68 "
76 69 SELECT keyword
77 70 FROM {$wpdb->prefix}betterdocs_search_keyword
78 - WHERE BINARY keyword = %s",
71 + WHERE keyword = %s",
79 72 $key
80 73 )
81 74 );
82 75
@@ -83,13 +76,12 @@
83 76 if ( $keyword == null ) {
84 77 $insert = $wpdb->query(
85 78 $wpdb->prepare(
86 79 "INSERT INTO {$wpdb->prefix}betterdocs_search_keyword
87 - ( keyword, keyword_hash )
88 - VALUES ( %s, %s )",
80 + ( keyword )
81 + VALUES ( %s )",
89 82 [
90 - $key,
91 - md5( $key )
83 + $key
92 84 ]
93 85 )
94 86 );
95 87
@@ -102,9 +94,9 @@
102 94 [
103 95 $wpdb->insert_id,
104 96 $count,
105 97 $not_found_count,
106 - gmdate( 'Y-m-d' )
98 + date( 'Y-m-d' )
107 99 ]
108 100 )
109 101 );
110 102 }
@@ -112,99 +104,7 @@
112 104 }
113 105
114 106 $this->database->save( 'betterdocs_search_data_migration', '1.0' );
115 107 }
116 - }
117 - }
118 -
119 - /**
120 - * Fix collation issues in search tables
121 - * Converts tables to proper UTF-8 charset and collation to prevent
122 - * "Illegal mix of collations" errors when searching with non-Latin characters
123 - *
124 - * @since 1.0.2
125 - * @return void
126 - */
127 - public function fix_search_table_collation() {
128 - global $wpdb;
129 -
130 - // Check if migration already ran
131 - if ( $this->database->get( 'betterdocs_search_collation_fixed', false ) ) {
132 - return;
133 - }
134 -
135 - // Get the WordPress default charset and collation; restrict to a safe identifier
136 - // alphabet because they're interpolated into ALTER TABLE statements below.
137 - $charset = preg_replace( '/[^A-Za-z0-9_]/', '', $wpdb->charset ? $wpdb->charset : 'utf8mb4' );
138 - $collate = preg_replace( '/[^A-Za-z0-9_]/', '', $wpdb->collate ? $wpdb->collate : 'utf8mb4_unicode_520_ci' );
139 -
140 - // Fix search_keyword table
141 - $search_keyword_table = $wpdb->prefix . 'betterdocs_search_keyword';
142 - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange -- one-shot collation migration; identifiers come from $wpdb only.
143 - if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $search_keyword_table ) ) === $search_keyword_table ) {
144 - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,PluginCheck.Security.DirectDB.UnescapedDBParameter
145 - $wpdb->query( "ALTER TABLE `{$search_keyword_table}` CONVERT TO CHARACTER SET {$charset} COLLATE {$collate}" );
146 - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,PluginCheck.Security.DirectDB.UnescapedDBParameter
147 - $wpdb->query( "ALTER TABLE `{$search_keyword_table}` MODIFY keyword TEXT CHARACTER SET {$charset} COLLATE {$collate} NOT NULL" );
148 - }
149 -
150 - // Fix search_log table
151 - $search_log_table = $wpdb->prefix . 'betterdocs_search_log';
152 - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
153 - if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $search_log_table ) ) === $search_log_table ) {
154 - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,PluginCheck.Security.DirectDB.UnescapedDBParameter
155 - $wpdb->query( "ALTER TABLE `{$search_log_table}` CONVERT TO CHARACTER SET {$charset} COLLATE {$collate}" );
156 - }
157 -
158 - // Mark migration as complete
159 - $this->database->save( 'betterdocs_search_collation_fixed', '1.0' );
160 - }
161 -
162 - /**
163 - * Populate keyword_hash for rows that predate the column.
164 - *
165 - * The column is added by dbDelta with an empty default; until it holds the
166 - * hash, insert_search_keyword() cannot find those rows by index and would
167 - * insert duplicates. Runs in bounded batches so a large keyword table does
168 - * not stall the request that triggers the upgrade — anything left over is
169 - * picked up on the next admin load.
170 - *
171 - * @since 1.0.3
172 - * @return void
173 - */
174 - public function backfill_search_keyword_hash() {
175 - global $wpdb;
176 -
177 - if ( $this->database->get( 'betterdocs_search_keyword_hash_filled', false ) ) {
178 - return;
179 - }
180 -
181 - $table = $wpdb->prefix . 'betterdocs_search_keyword';
182 -
183 - // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- one-shot schema backfill; identifier comes from $wpdb.
184 - if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) {
185 - return;
186 - }
187 -
188 - // Bail if the column never made it (dbDelta failed) — retry next load.
189 - if ( ! $wpdb->get_results( "SHOW COLUMNS FROM `{$table}` LIKE 'keyword_hash'" ) ) {
190 - return;
191 - }
192 -
193 - $batches = 0;
194 - do {
195 - $updated = $wpdb->query(
196 - "UPDATE `{$table}` SET keyword_hash = MD5( keyword ) WHERE keyword_hash = '' LIMIT 2000"
197 - );
198 - $batches++;
199 - } while ( $updated > 0 && $batches < 25 );
200 -
201 - // Only finish once nothing is left, so a table larger than 50k keywords
202 - // resumes instead of being marked done half-filled.
203 - $remaining = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}` WHERE keyword_hash = ''" );
204 - // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
205 -
206 - if ( 0 === $remaining ) {
207 - $this->database->save( 'betterdocs_search_keyword_hash_filled', '1.0.3' );
208 108 }
209 109 }
210 110 }