PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.1
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.1
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / classes / class-imagify-db.php

class-imagify-db.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.3.1, at inc/classes/class-imagify-db.php

738 lines 22.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Imagify DB class. It reunites tools to work with the DB.
5 *
6 * @since 1.6.13
7 * @author Grégory Viguier
8 */
9 class Imagify_DB {
10
11 /**
12 * Class version.
13 *
14 * @var string
15 */
16 const VERSION = '1.0.1';
17
18 /**
19 * Some hosts limit the number of JOINs in SQL queries, but we need them.
20 *
21 * @since 1.6.13
22 * @access public
23 * @author Grégory Viguier
24 */
25 public static function unlimit_joins() {
26 global $wpdb;
27 static $done = false;
28
29 if ( $done ) {
30 return;
31 }
32
33 $done = true;
34 $query = 'SET SQL_BIG_SELECTS=1';
35
36 /**
37 * Filter the SQL query allowing to remove the limit on JOINs.
38 *
39 * @since 1.6.13
40 * @author Grégory Viguier
41 *
42 * @param string|bool $query The query. False to prevent any query.
43 */
44 $query = apply_filters( 'imagify_db_unlimit_joins_query', $query );
45
46 if ( $query && is_string( $query ) ) {
47 $wpdb->query( $query ); // WPCS: unprepared SQL ok.
48 }
49 }
50
51 /**
52 * Change an array of values into a comma separated list, ready to be used in a `IN ()` clause.
53 *
54 * @since 1.6.13
55 * @access public
56 * @author Grégory Viguier
57 *
58 * @param array $values An array of values.
59 * @return string A comma separated list of values.
60 */
61 public static function prepare_values_list( $values ) {
62 $values = esc_sql( (array) $values );
63 $values = array_map( [ __CLASS__, 'quote_string' ], $values );
64 return implode( ',', $values );
65 }
66
67 /**
68 * Wrap a value in quotes, unless it's an integer.
69 *
70 * @since 1.6.13
71 * @access public
72 * @author Grégory Viguier
73 *
74 * @param int|string $value A value.
75 * @return int|string
76 */
77 public static function quote_string( $value ) {
78 return is_numeric( $value ) ? $value : "'" . addcslashes( $value, "'" ) . "'";
79 }
80
81 /**
82 * First half of escaping for LIKE special characters % and _ before preparing for MySQL.
83 * Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.
84 *
85 * Example Prepared Statement:
86 * $wild = '%';
87 * $find = 'only 43% of planets';
88 * $like = $wild . $wpdb->esc_like( $find ) . $wild;
89 * $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE %s", $like );
90 *
91 * Example Escape Chain:
92 * $sql = esc_sql( $wpdb->esc_like( $input ) );
93 *
94 * @since 1.7
95 * @access public
96 * @author Grégory Viguier
97 *
98 * @param string $text The raw text to be escaped. The input typed by the user should have no extra or deleted slashes.
99 * @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare() or real_escape next.
100 */
101 public static function esc_like( $text ) {
102 global $wpdb;
103
104 if ( method_exists( $wpdb, 'esc_like' ) ) {
105 // Introduced in WP 4.0.0.
106 return $wpdb->esc_like( $text );
107 }
108
109 return addcslashes( $text, '_%\\' );
110 }
111
112 /**
113 * Get Imagify mime types, ready to be used in a `IN ()` clause.
114 *
115 * @since 1.6.13
116 * @since 1.9 Added $type parameter.
117 * @access public
118 * @author Grégory Viguier
119 *
120 * @param string $type One of 'image', 'not-image'. Any other value will return all mime types.
121 * @return string A comma separated list of mime types.
122 */
123 public static function get_mime_types( $type = null ) {
124 static $mime_types = [];
125
126 if ( empty( $type ) ) {
127 $type = 'all';
128 }
129
130 if ( ! isset( $mime_types[ $type ] ) ) {
131 $mime_types[ $type ] = self::prepare_values_list( imagify_get_mime_types( $type ) );
132 }
133
134 return $mime_types[ $type ];
135 }
136
137 /**
138 * Get post statuses related to attachments, ready to be used in a `IN ()` clause.
139 *
140 * @since 1.7
141 * @access public
142 * @author Grégory Viguier
143 *
144 * @return string A comma separated list of post statuses.
145 */
146 public static function get_post_statuses() {
147 static $statuses;
148
149 if ( ! isset( $statuses ) ) {
150 $statuses = self::prepare_values_list( imagify_get_post_statuses() );
151 }
152
153 return $statuses;
154 }
155
156 /**
157 * Get the SQL JOIN clause to use to get only attachments that have the required WP metadata.
158 * It returns an empty string if the database has no attachments without the required metadada.
159 * It also triggers Imagify_DB::unlimit_joins().
160 *
161 * @param string $id_field An ID field to match the metadata ID against in the JOIN clause.
162 * Default is the posts table `ID` field, using the `p` alias: `p.ID`.
163 * In case of "false" value or PEBKAC, fallback to the same field without alias.
164 * @param bool $matching Set to false to get a query to fetch metas NOT matching the file extensions.
165 * @param bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
166 * @param string $special_join_conditions Special conditions to apply on the join.
167 *
168 * @return string
169 * @author Grégory Viguier
170 *
171 * @since 1.7
172 * @access public
173 */
174 public static function get_required_wp_metadata_join_clause( $id_field = 'p.ID', $matching = true, $test = true, $special_join_conditions = '' ) {
175 global $wpdb;
176
177 if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
178 return '';
179 }
180
181 self::unlimit_joins();
182 $clause = '';
183
184 if ( ! $id_field || ! is_string( $id_field ) ) {
185 $id_field = "$wpdb->posts.ID";
186 }
187
188 $join = $matching ? 'INNER' : 'LEFT';
189
190 $first = true;
191
192 foreach ( self::get_required_wp_metadata_aliases() as $meta_name => $alias ) {
193 if ( $first ) {
194 $first = false;
195 $clause .= "
196 $join JOIN $wpdb->postmeta AS $alias
197 ON ( $id_field = $alias.post_id AND $alias.meta_key = '$meta_name' $special_join_conditions )";
198 continue;
199 }
200 $clause .= "
201 $join JOIN $wpdb->postmeta AS $alias
202 ON ( $id_field = $alias.post_id AND $alias.meta_key = '$meta_name' )";
203 }
204
205 return $clause;
206 }
207
208
209 /**
210 * Get the Sub query(exists) clause to use to get only attachments that have the required WP metadata.
211 * It returns an empty string if the database has no attachments without the required metadada.
212 *
213 * @param string $id_field An ID field to match the metadata ID against in the WHERE clause.
214 * Default is the posts table `ID` field, using the `p` alias: `p.ID`.
215 * @param bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
216 *
217 * @return string
218 */
219 public static function get_required_wp_metadata_exist_clause( $id_field = 'p.ID', $test = true ) {
220 global $wpdb;
221
222 if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
223 return '';
224 }
225
226 self::unlimit_joins();
227 $clause = '';
228
229 if ( ! $id_field || ! is_string( $id_field ) ) {
230 $id_field = "$wpdb->posts.ID";
231 }
232 $additional_clause = self::get_required_exist_wp_metadata_where_clause(
233 [
234 'matching' => false,
235 'test' => false,
236 ]
237 );
238
239 $first = true;
240
241 foreach ( self::get_required_wp_metadata_aliases() as $meta_name => $alias ) {
242 if ( $first ) {
243 $first = false;
244 $clause .= "
245 EXISTS(
246 SELECT 1 FROM $wpdb->postmeta AS $alias WHERE
247 $alias.post_id = $id_field AND $alias.meta_key = '$meta_name'
248 $additional_clause
249 )
250 ";
251 continue;
252 }
253
254 $clause .= "
255 OR NOT EXISTS (
256 SELECT 1 FROM $wpdb->postmeta AS $alias WHERE
257 $alias.post_id = $id_field AND $alias.meta_key = '$meta_name')";
258 }
259
260 return "AND( $clause )";
261 }
262
263 /**
264 * Get the SQL part to be used in a WHERE clause, to get only attachments that have (in)valid '_wp_attached_file' and '_wp_attachment_metadata' metadatas.
265 * It returns an empty string if the database has no attachments without the required metadada.
266 *
267 * @since 1.7
268 * @since 1.7.1.2 Use a single $arg parameter instead of 3. New $prepared parameter.
269 * @access public
270 * @author Grégory Viguier
271 *
272 * @param array $args {
273 * Optional. An array of arguments.
274 *
275 * string $aliases The aliases to use for the meta values.
276 * bool $matching Set to false to get a query to fetch invalid metas.
277 * bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
278 * bool $prepared Set to true if the query will be prepared with using $wpdb->prepare().
279 * }.
280 * @return string A query.
281 */
282 public static function get_required_wp_metadata_where_clause( $args = [] ) {
283 static $query = [];
284
285 $args = imagify_merge_intersect(
286 $args,
287 [
288 'aliases' => [],
289 'matching' => true,
290 'test' => true,
291 'prepared' => false,
292 ]
293 );
294
295 list( $aliases, $matching, $test, $prepared ) = array_values( $args );
296
297 if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
298 return '';
299 }
300
301 if ( $aliases && is_string( $aliases ) ) {
302 $aliases = [
303 '_wp_attached_file' => $aliases,
304 ];
305 } elseif ( ! is_array( $aliases ) ) {
306 $aliases = [];
307 }
308
309 $aliases = imagify_merge_intersect( $aliases, self::get_required_wp_metadata_aliases() );
310 $key = implode( '|', $aliases ) . '|' . (int) $matching;
311
312 if ( isset( $query[ $key ] ) ) {
313 return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
314 }
315
316 unset( $args['prepared'] );
317 $alias_1 = $aliases['_wp_attached_file'];
318 $alias_2 = $aliases['_wp_attachment_metadata'];
319 $extensions = self::get_extensions_where_clause( $args );
320
321 if ( $matching ) {
322 $query[ $key ] = "AND $alias_1.meta_value NOT LIKE '%://%' AND $alias_1.meta_value NOT LIKE '_:\\\\\%' AND $extensions";
323 } else {
324 $query[ $key ] = "AND ( $alias_2.meta_value IS NULL OR $alias_1.meta_value IS NULL OR $alias_1.meta_value LIKE '%://%' OR $alias_1.meta_value LIKE '_:\\\\\%' AND $extensions )";
325 }
326
327 return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
328 }
329
330 /**
331 * Get the SQL part to be used in a WHERE clause, to get only attachments that have (in)valid '_wp_attached_file' and '_wp_attachment_metadata' metadatas.
332 * It returns an empty string if the database has no attachments without the required metadada.
333 *
334 * @param array $args {
335 * Optional. An array of arguments.
336 *
337 * string $aliases The aliases to use for the meta values.
338 * bool $matching Set to false to get a query to fetch invalid metas.
339 * bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
340 * bool $prepared Set to true if the query will be prepared with using $wpdb->prepare().
341 * }.
342 * @return string A query.
343 */
344 public static function get_required_exist_wp_metadata_where_clause( $args = [] ) {
345 static $query = [];
346
347 $args = imagify_merge_intersect(
348 $args,
349 [
350 'aliases' => [],
351 'matching' => true,
352 'test' => true,
353 'prepared' => false,
354 ]
355 );
356
357 list( $aliases, $matching, $test, $prepared ) = array_values( $args );
358
359 if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
360 return '';
361 }
362
363 if ( $aliases && is_string( $aliases ) ) {
364 $aliases = [
365 '_wp_attached_file' => $aliases,
366 ];
367 } elseif ( ! is_array( $aliases ) ) {
368 $aliases = [];
369 }
370
371 $aliases = imagify_merge_intersect( $aliases, self::get_required_wp_metadata_aliases() );
372 $key = implode( '|', $aliases ) . '|' . (int) $matching;
373
374 if ( isset( $query[ $key ] ) ) {
375 return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
376 }
377
378 unset( $args['prepared'] );
379 $alias_1 = $aliases['_wp_attached_file'];
380 $extensions = self::get_extensions_where_clause( $args );
381
382 if ( $matching ) {
383 $query[ $key ] = "AND $alias_1.meta_value NOT LIKE '%://%' AND $alias_1.meta_value NOT LIKE '_:\\\\\%' OR NOT ( $extensions )";
384 } else {
385 $query[ $key ] = "AND ( $alias_1.meta_value LIKE '%://%' OR $alias_1.meta_value LIKE '_:\\\\\%' OR NOT ( $extensions ) )";
386 }
387
388 return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
389 }
390
391 /**
392 * Prepare query arguments.
393 *
394 * @param array $args {
395 * Optional. An array of arguments.
396 *
397 * string $aliases The aliases to use for the meta values.
398 * bool $matching Set to false to get a query to fetch invalid metas.
399 * bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
400 * bool $prepared Set to true if the query will be prepared with using $wpdb->prepare().
401 * }.
402 *
403 * @return array
404 */
405 private function prepare_query_args( $args ) {
406 return imagify_merge_intersect(
407 $args,
408 [
409 'aliases' => [],
410 'matching' => true,
411 'test' => true,
412 'prepared' => false,
413 ]
414 );
415 }
416
417 /**
418 * Generate query.
419 *
420 * @param bool $matching Matching.
421 * @param string $alias Query alias.
422 * @param string $regex Query Regex.
423 *
424 * @return string
425 */
426 private function generate_query( $matching, $alias, $regex ) {
427 if ( $matching ) {
428 return "REVERSE (LOWER( $alias.meta_value )) REGEXP '$regex'";
429 }
430
431 return "REVERSE (LOWER( $alias.meta_value )) NOT REGEXP '$regex'";
432 }
433
434 /**
435 * Get the SQL part to be used in a WHERE clause, to get only attachments that have a valid file extensions.
436 * It returns an empty string if the database has no attachments without the required metadada.
437 *
438 * @since 1.7
439 * @since 1.7.1.2 Use a single $arg parameter instead of 3. New $prepared parameter.
440 * @access public
441 * @author Grégory Viguier
442 *
443 * @param array $args {
444 * Optional. An array of arguments.
445 *
446 * string $alias The alias to use for the meta value.
447 * bool $matching Set to false to get a query to fetch metas NOT matching the file extensions.
448 * bool $test Test if the site has attachments without required metadata before returning the query. False to bypass the test and get the query anyway.
449 * bool $prepared Set to true if the query will be prepared with using $wpdb->prepare().
450 * }.
451 * @return string A query.
452 */
453 public static function get_extensions_where_clause( $args = false ) {
454 static $extensions;
455 static $query = [];
456
457 $instance = new self();
458
459 $args = $instance->prepare_query_args( $args );
460
461 list( $alias, $matching, $test, $prepared ) = array_values( $args );
462
463 if ( $test && ! imagify_has_attachments_without_required_metadata() ) {
464 return '';
465 }
466
467 if ( ! isset( $extensions ) ) {
468 $extensions = array_keys( imagify_get_mime_types() );
469 $extensions = implode( '|', $extensions );
470 $extensions = explode( '|', $extensions );
471 $extensions = array_map(
472 function ( $ex ) {
473 return strrev( $ex );
474 },
475 $extensions
476 );
477 }
478
479 if ( ! $alias ) {
480 $alias = self::get_required_wp_metadata_aliases();
481 $alias = $alias['_wp_attached_file'];
482 }
483
484 $key = $alias . '|' . (int) $matching;
485
486 if ( isset( $query[ $key ] ) ) {
487 return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
488 }
489
490 $regex = '^' . implode( '\..*|^', $extensions ) . '\..*';
491
492 $query[ $key ] = $instance->generate_query( $matching, $alias, $regex );
493
494 return $prepared ? str_replace( '%', '%%', $query[ $key ] ) : $query[ $key ];
495 }
496
497 /**
498 * Get the aliases used for the metas in self::get_required_wp_metadata_join_clause(), self::get_required_wp_metadata_where_clause(), and self::get_extensions_where_clause().
499 *
500 * @since 1.7
501 * @access public
502 * @author Grégory Viguier
503 *
504 * @return array An array with the meta name as key and its alias as value.
505 */
506 public static function get_required_wp_metadata_aliases() {
507 return [
508 '_wp_attached_file' => 'imrwpmt1',
509 '_wp_attachment_metadata' => 'imrwpmt2',
510 ];
511 }
512
513 /**
514 * Combine two arrays with some specific keys.
515 * We use this function to combine the result of 2 SQL queries.
516 *
517 * @since 1.6.13
518 * @access public
519 * @author Grégory Viguier
520 *
521 * @param array $keys An array of keys.
522 * @param array $values An array of arrays like array( 'id' => id, 'value' => value ).
523 * @param int $keep_keys_order Set to true to return an array ordered like $keys instead of $values.
524 * @return array The combined arrays.
525 */
526 public static function combine_query_results( $keys, $values, $keep_keys_order = false ) {
527 if ( ! $keys || ! $values ) {
528 return [];
529 }
530
531 $result = [];
532 $keys = array_flip( $keys );
533
534 foreach ( $values as $v ) {
535 if ( isset( $keys[ $v['id'] ] ) ) {
536 $result[ $v['id'] ] = $v['value'];
537 }
538 }
539
540 if ( $keep_keys_order ) {
541 $keys = array_intersect_key( $keys, $result );
542 return array_replace( $keys, $result );
543 }
544
545 return $result;
546 }
547
548 /**
549 * A helper to retrieve all values from one or several post metas, given a list of post IDs.
550 * The $wpdb cache is flushed to save memory.
551 *
552 * @since 1.6.13
553 * @access public
554 * @author Grégory Viguier
555 *
556 * @param array $metas An array of meta names like:
557 * array(
558 * 'key1' => 'meta_name_1',
559 * 'key2' => 'meta_name_2',
560 * 'key3' => 'meta_name_3',
561 * )
562 * If a key contains 'data', the results will be unserialized.
563 * @param array $ids An array of post IDs.
564 * @return array An array of arrays of results like:
565 * array(
566 * 'key1' => array( post_id_1 => 'result_1', post_id_2 => 'result_2', post_id_3 => 'result_3' ),
567 * 'key2' => array( post_id_1 => 'result_4', post_id_3 => 'result_5' ),
568 * 'key3' => array( post_id_1 => 'result_6', post_id_2 => 'result_7' ),
569 * )
570 */
571 public static function get_metas( $metas, $ids ) {
572 global $wpdb;
573
574 if ( ! $ids ) {
575 return array_fill_keys( array_keys( $metas ), [] );
576 }
577
578 $sql_ids = implode( ',', $ids );
579
580 foreach ( $metas as $result_name => $meta_name ) {
581 $metas[ $result_name ] = $wpdb->get_results( // WPCS: unprepared SQL ok.
582 "SELECT pm.post_id as id, pm.meta_value as value
583 FROM $wpdb->postmeta as pm
584 WHERE pm.meta_key = '$meta_name'
585 AND pm.post_id IN ( $sql_ids )
586 ORDER BY pm.post_id DESC",
587 ARRAY_A
588 );
589
590 $wpdb->flush();
591 $metas[ $result_name ] = self::combine_query_results( $ids, $metas[ $result_name ], true );
592
593 if ( strpos( $result_name, 'data' ) !== false ) {
594 $metas[ $result_name ] = array_map( 'maybe_unserialize', $metas[ $result_name ] );
595 }
596 }
597
598 return $metas;
599 }
600
601 /**
602 * Create/Upgrade the table in the database.
603 *
604 * @since 1.7
605 * @access public
606 * @author Grégory Viguier
607 *
608 * @param string $table_name The (prefixed) table name.
609 * @param string $schema_query Query representing the table schema.
610 * @return bool True on success. False otherwise.
611 */
612 public static function create_table( $table_name, $schema_query ) {
613 global $wpdb;
614
615 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
616
617 $wpdb->hide_errors();
618
619 $schema_query = trim( $schema_query );
620 $charset_collate = $wpdb->get_charset_collate();
621
622 dbDelta( "CREATE TABLE $table_name ($schema_query) $charset_collate;" );
623
624 return empty( $wpdb->last_error ) && self::table_exists( $table_name );
625 }
626
627 /**
628 * Tell if the given table exists.
629 *
630 * @since 1.7
631 * @access public
632 * @author Grégory Viguier
633 *
634 * @param string $table_name Full name of the table (with DB prefix).
635 * @return bool
636 */
637 public static function table_exists( $table_name ) {
638 global $wpdb;
639
640 $escaped_table = self::esc_like( $table_name );
641 $result = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $escaped_table ) );
642
643 return $result === $table_name;
644 }
645
646 /**
647 * Cache transients used for optimization process locks.
648 *
649 * @since 1.9
650 * @access public
651 * @author Grégory Viguier
652 *
653 * @param string $context The context.
654 * @param array $media_ids The media IDs.
655 */
656 public static function cache_process_locks( $context, $media_ids ) {
657 global $wpdb;
658
659 if ( ! $context || ! $media_ids || wp_using_ext_object_cache() ) {
660 return;
661 }
662
663 // Sanitize the IDs.
664 $media_ids = array_filter( $media_ids );
665 $media_ids = array_unique( $media_ids );
666
667 if ( ! $media_ids ) {
668 return;
669 }
670
671 $context_instance = imagify_get_context( $context );
672 $context = $context_instance->get_name();
673 $process_class_name = imagify_get_optimization_process_class_name( $context );
674 $transient_name = sprintf( $process_class_name::LOCK_NAME, $context, '%' );
675 $is_network_wide = $context_instance->is_network_wide();
676
677 // Do 1 DB query per context (and cache results) before doing 1 get_transient() (2 DB queries) per media ID.
678 $prefix = $is_network_wide ? '_site_transient_' : '_transient_';
679
680 if ( $is_network_wide && is_multisite() ) {
681 $network_id = function_exists( 'get_current_network_id' ) ? get_current_network_id() : (int) $wpdb->siteid;
682 $cache_prefix = "$network_id:";
683 $notoptions_key = "$network_id:notoptions";
684 $cache_group = 'site-options';
685 $results = $wpdb->get_results(
686 $wpdb->prepare(
687 "SELECT meta_key as name, meta_value as value FROM $wpdb->sitemeta WHERE ( meta_key LIKE %s OR meta_key LIKE %s ) AND site_id = %d",
688 $prefix . $transient_name,
689 $prefix . 'timeout_' . $transient_name,
690 $network_id
691 ),
692 OBJECT_K
693 ); // WPCS: unprepared SQL ok.
694 } else {
695 $cache_prefix = '';
696 $notoptions_key = 'notoptions';
697 $cache_group = 'options';
698 $results = $wpdb->get_results(
699 $wpdb->prepare(
700 "SELECT option_name as name, option_value as value FROM $wpdb->options WHERE ( option_name LIKE %s OR option_name LIKE %s )",
701 $prefix . $transient_name,
702 $prefix . 'timeout_' . $transient_name
703 ),
704 OBJECT_K
705 ); // WPCS: unprepared SQL ok.
706 }
707
708 $not_exist = [];
709
710 foreach ( [ '', 'timeout_' ] as $maybe_timeout ) {
711 foreach ( $media_ids as $id ) {
712 $option_name = $prefix . $maybe_timeout . str_replace( '%', $id, $transient_name );
713
714 if ( isset( $results[ $option_name ] ) ) {
715 // Cache the value.
716 $value = $results[ $option_name ]->value;
717 $value = maybe_unserialize( $value );
718 wp_cache_set( "$cache_prefix$option_name", $value, $cache_group );
719 } else {
720 // No value.
721 $not_exist[ $option_name ] = true;
722 }
723 }
724 }
725
726 if ( ! $not_exist ) {
727 return;
728 }
729
730 // Cache the options that don't exist in the DB.
731 $notoptions = wp_cache_get( $notoptions_key, $cache_group );
732 $notoptions = is_array( $notoptions ) ? $notoptions : [];
733 $notoptions = array_merge( $notoptions, $not_exist );
734
735 wp_cache_set( $notoptions_key, $notoptions, $cache_group );
736 }
737 }
738