PluginProbe
Packeta / 2.1
Packeta v2.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / src / Packetery / Module / WpdbAdapter.php

WpdbAdapter.php in Packeta 2.1, at src/Packetery/Module/WpdbAdapter.php

479 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class WpdbAdapter
4 *
5 * @package Packetery
6 */
7
8 declare( strict_types=1 );
9
10 namespace Packetery\Module;
11
12 use WC_Logger;
13
14 /**
15 * Class WpdbAdapter
16 *
17 * @package Packetery
18 */
19 class WpdbAdapter {
20
21 /**
22 * Table name.
23 *
24 * @var string
25 */
26 public $packeteryCarrier;
27
28 /**
29 * Table name.
30 *
31 * @var string
32 */
33 public $packeteryOrder;
34
35 /**
36 * Table name.
37 *
38 * @var string
39 */
40 public $packeteryLog;
41
42 /**
43 * Table name.
44 *
45 * @var string
46 */
47 public $packeteryCustomsDeclaration;
48
49 /**
50 * Table name.
51 *
52 * @var string
53 */
54 public $packeteryCustomsDeclarationItem;
55
56 /**
57 * Table name.
58 *
59 * @var string
60 */
61 public $wcOrders;
62
63 /**
64 * Table name.
65 *
66 * @var string
67 */
68 public $posts;
69
70 /**
71 * Table name.
72 *
73 * @var string
74 */
75 public $options;
76
77 /**
78 * Table name.
79 *
80 * @var string
81 */
82 public $postmeta;
83
84 /**
85 * Wpdb.
86 *
87 * @var \wpdb
88 */
89 private $wpdb;
90
91 /**
92 * Constructor.
93 *
94 * @param \wpdb $wpdb Wpdb.
95 */
96 public function __construct( \wpdb $wpdb ) {
97 $this->wpdb = $wpdb;
98 }
99
100 /**
101 * @param string $query SQL query.
102 * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which
103 * correspond to an stdClass object, an associative array, or a numeric array,
104 * respectively. Default OBJECT.
105 *
106 * @return array<string, mixed>|object|null Database query result or null on failure.
107 */
108 public function get_row( string $query, string $output = OBJECT ) {
109 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
110 $result = $this->wpdb->get_row( $query, $output );
111 if ( $result === null ) {
112 $this->handleError();
113 }
114
115 return $result;
116 }
117
118 /**
119 * Prepares a SQL query for safe execution.
120 *
121 * @param string $query Query.
122 * @param mixed ...$args Arguments.
123 *
124 * @return string
125 */
126 public function prepare( string $query, ...$args ): string {
127 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
128 $result = $this->wpdb->prepare( $query, ...$args );
129 if ( $result === null ) {
130 $this->logError( 'Query to prepare is invalid. Likely due placeholder count mismatch.' );
131 }
132
133 return (string) $result;
134 }
135
136 /**
137 * Executes SQL query.
138 *
139 * @param string $query Query.
140 *
141 * @return int|bool Boolean true for CREATE, ALTER, TRUNCATE and DROP queries. Number of rows
142 * affected/selected for all other queries. Boolean false on error.
143 */
144 public function query( string $query ) {
145 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
146 $result = $this->wpdb->query( $query );
147 if ( $result === false ) {
148 $this->handleError();
149 }
150
151 return $result;
152 }
153
154 /**
155 * Helper function for insert and replace.
156 *
157 * @param string $table Table name.
158 * @param array<string, mixed> $data Data to insert (in column => value pairs).
159 * @param string[]|null $format Optional. An array of formats to be mapped to each of the value in $data.
160 * @param string $type Optional. Type of operation. Possible values include 'INSERT' or 'REPLACE'.
161 *
162 * @return int|false The number of rows affected, or false on error.
163 */
164 public function insertReplaceHelper( string $table, array $data, ?array $format = null, string $type = 'INSERT' ) {
165 $result = $this->wpdb->_insert_replace_helper( $table, $data, $format, $type );
166 if ( $result === false ) {
167 $this->handleError();
168 }
169
170 return $result;
171 }
172
173 /**
174 * Deletes a row in the table.
175 *
176 * @param string $table Table name.
177 * @param array<string, int|string> $where A named array of WHERE clauses (in column => value pairs).
178 * @param string|null $whereFormat Optional. An array of formats to be mapped to each of the values in $where.
179 *
180 * @return int|false The number of rows updated, or false on error.
181 */
182 public function delete( string $table, array $where, ?string $whereFormat = null ) {
183 $result = $this->wpdb->delete( $table, $where, $whereFormat );
184 if ( $result === false ) {
185 $this->handleError();
186 }
187
188 return $result;
189 }
190
191 /**
192 * Inserts a row into the table.
193 *
194 * @param string $table Table name.
195 * @param array<string, mixed> $data Data to insert (in column => value pairs).
196 *
197 * @return int|false The number of rows inserted, or false on error.
198 */
199 public function insert( string $table, array $data ) {
200 $result = $this->wpdb->insert( $table, $data );
201 if ( $result === false ) {
202 $this->handleError();
203 }
204
205 return $result;
206 }
207
208 /**
209 * Updates a row in the table.
210 *
211 * @param string $table Table name.
212 * @param array<string, int|float|string|null|bool> $data Data to update (in column => value pairs).
213 * @param array<string, int|string> $where A named array of WHERE clauses (in column => value pairs).
214 *
215 * @return int|false The number of rows updated, or false on error.
216 */
217 public function update( string $table, array $data, array $where ) {
218 $result = $this->wpdb->update( $table, $data, $where );
219 if ( $result === false ) {
220 $this->handleError();
221 }
222
223 return $result;
224 }
225
226 /**
227 * Gets charset collate.
228 *
229 * @return string
230 */
231 public function get_charset_collate(): string {
232 return $this->wpdb->get_charset_collate();
233 }
234
235 /**
236 * Retrieves an entire SQL result set from the database (i.e., many rows).
237 *
238 * @param string $query SQL query.
239 * @param string $output Optional. Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants.
240 *
241 * @return array|object[]|null Database query results.
242 */
243 public function get_results( string $query, string $output = OBJECT ): ?array {
244 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
245 $result = $this->wpdb->get_results( $query, $output );
246 $this->handleError();
247
248 return $result;
249 }
250
251 /**
252 * Retrieves one variable from the database.
253 *
254 * @param string $query SQL query. Defaults to null, use the result from the previous query.
255 *
256 * @return string|null Database query result (as string), or null on failure.
257 */
258 public function get_var( string $query ): ?string {
259 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
260 $result = $this->wpdb->get_var( $query );
261 if ( $result === null ) {
262 $this->handleError();
263 }
264
265 return $result;
266 }
267
268 /**
269 * Tells if packetery table is queried.
270 *
271 * @param string $query Query.
272 *
273 * @return bool
274 */
275 private function isPacketeryTableQueried( string $query ): bool {
276 return preg_match( '~\s*(FROM|JOIN|INTO|UPDATE|TABLE)\s*`?' . preg_quote( $this->getPacketeryPrefix(), '~' ) . '~i', $query ) === 1;
277 }
278
279 /**
280 * Gets packetery prefix.
281 *
282 * @return string
283 */
284 public function getPacketeryPrefix(): string {
285 return sprintf( '%spacketery_', $this->wpdb->prefix );
286 }
287
288 /**
289 * Logs wpdb error.
290 *
291 * @param string $errorMessage Error message.
292 *
293 * @return void
294 */
295 private function logError( string $errorMessage ): void {
296 /**
297 * WC logger.
298 *
299 * @var WC_Logger $wcLogger
300 */
301 $wcLogger = wc_get_logger();
302
303 $wcLogger->error( sprintf( 'wpdb: %s', $errorMessage ), [ 'source' => 'packeta' ] );
304 }
305
306 /**
307 * Handles wpdb error.
308 *
309 * @return void
310 */
311 private function handleError(): void {
312 if ( $this->getLastWpdbError() !== '' && $this->isPacketeryTableQueried( (string) $this->wpdb->last_query ) ) {
313 $this->logError( $this->getLastWpdbError() );
314 }
315 }
316
317 /**
318 * Gets last wpdb error.
319 *
320 * @return string
321 */
322 public function getLastWpdbError(): string {
323 return $this->wpdb->last_error;
324 }
325
326 /**
327 * Gets wpdb queries.
328 *
329 * @return \Generator
330 */
331 public function getWpdbQueries(): \Generator {
332 if ( $this->wpdb->queries !== null ) {
333 foreach ( $this->wpdb->queries as $queryInfo ) {
334 yield $queryInfo;
335 }
336 }
337 }
338
339 /**
340 * This method outputs a one dimensional array. If more than one column is returned by the query,
341 * only the specified column will be returned, but the entire result is cached for later use.
342 *
343 * @param string $query The query you wish to execute. Setting this parameter to null will return the specified column from the cached results of the previous query.
344 * @param int $columnOffset The desired column (0 being the first). Defaults to 0.
345 *
346 * @return array<int|float|string|null|bool> Returns an empty array if no result is found.
347 */
348 public function get_col( string $query, int $columnOffset = 0 ): array {
349 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
350 $result = $this->wpdb->get_col( $query, $columnOffset );
351 if ( $result === [] ) {
352 $this->handleError();
353 }
354
355 return $result;
356 }
357
358 /**
359 * Quote array of strings.
360 *
361 * @param string[] $input Input.
362 *
363 * @return string[]
364 */
365 private function quoteArrayOfStrings( array $input ): array {
366 return array_map(
367 function ( string $item ) {
368 return $this->prepare( '%s', $item );
369 },
370 $input
371 );
372 }
373
374 /**
375 * Prepare IN clause from array of strings.
376 *
377 * @param string[] $input Input array.
378 *
379 * @return string
380 */
381 public function prepareInClause( array $input ): string {
382 return implode( ',', $this->quoteArrayOfStrings( $input ) );
383 }
384
385 /**
386 * Wrapper for dbDelta function, logs result.
387 *
388 * @param string $createTableQuery Create table query.
389 * @param string $tableName Table name.
390 *
391 * @return bool
392 */
393 public function dbDelta( string $createTableQuery, string $tableName ): bool {
394 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
395 $result1 = dbDelta( $createTableQuery );
396 $result2 = dbDelta( $createTableQuery );
397
398 /**
399 * WC logger.
400 *
401 * @var WC_Logger $wcLogger
402 */
403 $wcLogger = wc_get_logger();
404 foreach ( $result1 as $tableOrColumn => $message ) {
405 $wcLogger->info( sprintf( 'dbDelta: %s => %s', $tableOrColumn, $message ), [ 'source' => 'packeta' ] );
406 }
407
408 $parsedResult1 = $this->parseDbdeltaOutput( $result1 );
409 $parsedResult2 = $this->parseDbdeltaOutput( $result2 );
410 // If the first command tries to create the table and so does the second, it means it failed.
411 if (
412 in_array( $tableName, $parsedResult1['created_tables'], true ) &&
413 in_array( $tableName, $parsedResult2['created_tables'], true )
414 ) {
415 return false;
416 }
417 // If the first command tries to add column and so does the second, it means it failed.
418 if ( $parsedResult1['added_columns'] !== [] && $parsedResult2['added_columns'] !== [] ) {
419 return false;
420 }
421
422 // Otherwise, we assume everything is fine, column changes errors are not safe to catch this way.
423 return true;
424 }
425
426 /**
427 * Parses the output given by dbDelta and returns information about it. Taken from DatabaseUtil 7.5.1.
428 *
429 * @param array<int|string, string> $dbdeltaOutput The output from the execution of dbDelta.
430 *
431 * An array containing a 'created_tables' and 'added_columns' key whose value is an array with the names of the tables or columns that have been (or would have been) created.
432 * @return array{created_tables: array<int<0, max>, (int|string)>, added_columns: array<int<0, max>, (int|string)>}
433 */
434 private function parseDbdeltaOutput( array $dbdeltaOutput ): array {
435 $createdTables = [];
436 $addedColumns = [];
437
438 foreach ( $dbdeltaOutput as $tableOrColumn => $result ) {
439 if ( "Created table $tableOrColumn" === $result ) {
440 $createdTables[] = $tableOrColumn;
441
442 continue;
443 }
444 if ( "Added column $tableOrColumn" === $result ) {
445 $addedColumns[] = $tableOrColumn;
446 }
447 }
448
449 return [
450 'created_tables' => $createdTables,
451 'added_columns' => $addedColumns,
452 ];
453 }
454
455 /**
456 * Gets last insert ID.
457 *
458 * @return string|null
459 */
460 public function getLastInsertId(): ?string {
461 if ( $this->wpdb->insert_id === 0 ) {
462 return null;
463 }
464
465 return (string) $this->wpdb->insert_id;
466 }
467
468 /**
469 * Wpdb esc_like method proxy.
470 *
471 * @param string $text Text to escape.
472 *
473 * @return string
474 */
475 public function escLike( string $text ): string {
476 return $this->wpdb->esc_like( $text );
477 }
478 }
479