PluginProbe
Packeta / 1.6.4
Packeta v1.6.4
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 1.6.4, at src/Packetery/Module/WpdbAdapter.php

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