PluginProbe
WPSSO Core – Complete Schema Markup and Meta Tags / 22.7.0
WPSSO Core – Complete Schema Markup and Meta Tags v22.7.0
22.7.0 22.6.1 22.6.0 22.5.3 22.5.2 22.5.1 22.4.0 22.3.0 22.2.1 22.2.0 22.1.3 22.1.2 22.1.1 22.1.0 22.0.0 21.13.3 21.13.2 trunk 21.13.1
← All changes | lib/com/cache.php +1305 -0 22.2.022.7.0 View file →
@@ -1,0 +1,1305 @@
1 +<?php
2 +/*
3 + * License: GPLv3
4 + * License URI: https://www.gnu.org/licenses/gpl.txt
5 + * Copyright 2012-2026 Jean-Sebastien Morisset (https://surniaulula.com/)
6 + */
7 +
8 +if ( ! defined( 'ABSPATH' ) ) {
9 +
10 + die( 'These aren\'t the droids you\'re looking for.' );
11 +}
12 +
13 +if ( ! class_exists( 'SucomCache' ) ) {
14 +
15 + class SucomCache {
16 +
17 + private $p; // Plugin class object.
18 +
19 + private $plugin_id = 'sucom';
20 + private $plugin_ucid = 'SUCOM';
21 + private $text_domain = 'sucom';
22 + private $label_transl = '';
23 +
24 + public $base_dir = '';
25 + public $base_url = '/cache/';
26 +
27 + public $default_file_cache_exp = DAY_IN_SECONDS;
28 + public $default_object_cache_exp = DAY_IN_SECONDS;
29 +
30 + public $curl_connect_timeout = 5; // The number of seconds to wait while trying to connect. Use 0 to wait indefinitely.
31 + public $curl_timeout = 10; // The maximum number of seconds to allow cURL functions to execute.
32 + public $curl_max_redirs = 10; // The maximum amount of HTTP redirections to follow.
33 +
34 + private $url_get_mtimes = array(); // Associative array of URL fetch times (true, false, or microtime).
35 + private $ignored = array(
36 + 'expires' => DAY_IN_SECONDS,
37 + 'loaded' => false,
38 + 'for_secs' => 600, // Ignore failed URLs for 10 mins by default.
39 + 'urls' => array(),
40 + );
41 +
42 + public function __construct( $plugin = null, $plugin_id = null, $text_domain = null, $label_transl = null ) {
43 +
44 + if ( ! class_exists( 'SucomUtil' ) ) { // Just in case.
45 +
46 + require_once trailingslashit( dirname( __FILE__ ) ) . 'util.php';
47 + }
48 +
49 + $this->set_config( $plugin, $plugin_id, $text_domain, $label_transl );
50 +
51 + add_action( 'shutdown', array( $this, 'save_ignored_urls' ), -3000, 0 );
52 + }
53 +
54 + /*
55 + * Set property values for text domain, notice label, etc.
56 + */
57 + private function set_config( $plugin = null, $plugin_id = null, $text_domain = null, $label_transl = null ) {
58 +
59 + if ( $plugin !== null ) {
60 +
61 + $this->p =& $plugin;
62 +
63 + if ( ! empty( $this->p->debug->enabled ) ) {
64 +
65 + $this->p->debug->mark();
66 + }
67 + }
68 +
69 + if ( $plugin_id !== null ) {
70 +
71 + $this->plugin_id = $plugin_id;
72 +
73 + } elseif ( ! empty( $this->p->id ) ) {
74 +
75 + $this->plugin_id = $this->p->id;
76 + }
77 +
78 + $this->plugin_ucid = strtoupper( $this->plugin_id );
79 +
80 + if ( $text_domain !== null ) {
81 +
82 + $this->text_domain = $text_domain;
83 +
84 + } elseif ( ! empty( $this->p->cf[ 'plugin' ][ $this->plugin_id ][ 'text_domain' ] ) ) {
85 +
86 + $this->text_domain = $this->p->cf[ 'plugin' ][ $this->plugin_id ][ 'text_domain' ];
87 + }
88 +
89 + if ( $label_transl !== null ) {
90 +
91 + $this->label_transl = $label_transl; // argument is already translated
92 +
93 + } elseif ( ! empty( $this->p->cf[ 'menu' ][ 'title' ] ) ) {
94 +
95 + $this->label_transl = sprintf( __( '%s Notice', $this->text_domain ),
96 + _x( $this->p->cf[ 'menu' ][ 'title' ], 'menu title', $this->text_domain ) );
97 +
98 + } else {
99 +
100 + $this->label_transl = __( 'Notice', $this->text_domain );
101 + }
102 +
103 + $this->base_dir = trailingslashit( constant( $this->plugin_ucid . '_CACHE_DIR' ) );
104 +
105 + $this->base_url = trailingslashit( constant( $this->plugin_ucid . '_CACHE_URL' ) );
106 + }
107 +
108 + /*
109 + * This method is only run on an as-needed basis.
110 + */
111 + public function maybe_load_ignored() {
112 +
113 + if ( $this->ignored[ 'loaded' ] ) {
114 +
115 + return;
116 + }
117 +
118 + if ( ! empty( $this->p->debug->enabled ) ) {
119 +
120 + $this->p->debug->log( 'getting transient for ignored urls' );
121 + }
122 +
123 + $cache_md5_pre = $this->plugin_id . '_';
124 + $cache_salt = __CLASS__ . '::ignored_urls';
125 + $cache_id = $cache_md5_pre . md5( $cache_salt );
126 + $cache_ret = get_transient( $cache_id );
127 +
128 + /*
129 + * Retrieve the list of ignored URLs cached, while keeping the existing 'expires' and 'for_secs' array values.
130 + */
131 + if ( isset( $cache_ret[ 'urls' ] ) ) {
132 +
133 + $this->ignored[ 'urls' ] = $cache_ret[ 'urls' ];
134 +
135 + $this->ignored[ 'saved_urls' ] = $cache_ret[ 'urls' ];
136 +
137 + } else {
138 +
139 + $this->ignored[ 'urls' ] = array();
140 +
141 + $this->ignored[ 'saved_urls' ] = array();
142 + }
143 +
144 + $this->ignored[ 'for_secs' ] = (int) apply_filters( 'sucom_cache_ignored_for_secs', $this->ignored[ 'for_secs' ] );
145 +
146 + $this->ignored[ 'loaded' ] = true;
147 + }
148 +
149 + public function save_ignored_urls() {
150 +
151 + if ( ! empty( $this->p->debug->enabled ) ) {
152 +
153 + $this->p->debug->mark();
154 + }
155 +
156 + /*
157 + * 'loaded' will be true if any ignored URL method has been run.
158 + */
159 + if ( $this->ignored[ 'loaded' ] ) {
160 +
161 + /*
162 + * Only save ignored URLs if the current URL array is different to the saved URL array.
163 + */
164 + if ( $this->ignored[ 'urls' ] !== $this->ignored[ 'saved_urls' ] ) {
165 +
166 + $cache_md5_pre = $this->plugin_id . '_';
167 + $cache_salt = __CLASS__ . '::ignored_urls';
168 + $cache_id = $cache_md5_pre . md5( $cache_salt );
169 +
170 + if ( ! empty( $this->p->debug->enabled ) ) {
171 +
172 + $this->p->debug->log( 'saving ' . count( $this->ignored[ 'urls' ] ) . ' ignored urls' );
173 + }
174 +
175 + set_transient( $cache_id, $this->ignored, $this->ignored[ 'expires' ] );
176 +
177 + $this->ignored[ 'saved_urls' ] = $this->ignored[ 'urls' ]; // Just in case.
178 + }
179 + }
180 + }
181 +
182 + public function count_ignored_urls() {
183 +
184 + return count( $this->get_ignored_urls() );
185 + }
186 +
187 + public function get_ignored_urls() {
188 +
189 + $this->maybe_load_ignored();
190 +
191 + return $this->ignored[ 'urls' ];
192 + }
193 +
194 + /*
195 + * Returns false or time in seconds.
196 + */
197 + public function is_ignored_url( $url_nofrag ) {
198 +
199 + $this->maybe_load_ignored();
200 +
201 + if ( isset( $this->ignored[ 'urls' ][ $url_nofrag ] ) ) {
202 +
203 + $time_diff = time() - $this->ignored[ 'urls' ][ $url_nofrag ];
204 + $time_left = $this->ignored[ 'for_secs' ] - $time_diff;
205 +
206 + if ( $time_left > 0 ) {
207 +
208 + if ( $this->p->debug->enabled ) {
209 +
210 + $this->p->debug->log( 'requests to retrieve ' . $url_nofrag . ' ignored for another ' . $time_left . ' second(s)' );
211 + }
212 +
213 + return $time_left;
214 +
215 + }
216 +
217 + unset( $this->ignored[ 'urls' ][ $url_nofrag ] );
218 + }
219 +
220 + return false;
221 + }
222 +
223 + public function add_ignored_url( $url_nofrag, $mtime_total = null, $curl_errnum = 0, $curl_errmsg = '', $ssl_verify = 0, $http_code = 0 ) {
224 +
225 + $this->maybe_load_ignored();
226 +
227 + $this->ignored[ 'urls' ][ $url_nofrag ] = time();
228 +
229 + if ( is_admin() ) {
230 +
231 + $errors = array();
232 +
233 + if ( $mtime_total ) {
234 +
235 + $errors[] = sprintf( __( 'Error retrieving %1$s for caching (after %2$.3f seconds).',
236 + $this->text_domain ), '<a href="' . $url_nofrag . '">' . $url_nofrag . '</a>', $mtime_total );
237 +
238 + } else {
239 +
240 + $errors[] = sprintf( __( 'Error retrieving %1$s for caching.',
241 + $this->text_domain ), '<a href="' . $url_nofrag . '">' . $url_nofrag . '</a>' );
242 + }
243 +
244 + if ( $curl_errnum ) {
245 +
246 + $errors[] = sprintf( __( 'cURL error code %1$d %2$s.', $this->text_domain ),
247 + $curl_errnum, trim( $curl_errmsg, ' .' ) );
248 + }
249 +
250 + if ( $ssl_verify ) {
251 +
252 + $errors[] = sprintf( __( 'SSL verification failed with code %1$d.', $this->text_domain ), $ssl_verify );
253 + }
254 +
255 + if ( $http_code ) {
256 +
257 + $errors[] = sprintf( __( 'HTTP connection returned code %1$d.', $this->text_domain ), $http_code );
258 + }
259 +
260 + if ( 301 === $http_code || 302 === $http_code ) {
261 +
262 + /*
263 + * PHP safe mode is an attempt to solve the shared-server security problem. It is
264 + * architecturally incorrect to try to solve this problem at the PHP level, but since the
265 + * alternatives at the web server and OS levels aren't very realistic, many people,
266 + * especially ISP's, use safe mode for now.
267 + *
268 + * This feature has been DEPRECATED as of PHP 5.3 and REMOVED as of PHP 5.4.
269 + */
270 + if ( ini_get( 'safe_mode' ) ) {
271 +
272 + $errors[] = sprintf( __( 'The PHP "%s" setting is enabled - the PHP cURL library cannot follow URL redirects.',
273 + $this->text_domain ), 'safe_mode' );
274 +
275 + $errors[] = sprintf( __( 'The "%s" setting is deprecated since PHP version 5.3 and removed from PHP since version 5.4.',
276 + $this->text_domain ), 'safe_mode' );
277 +
278 + $errors[] = __( 'Please contact your hosting provider to have this setting disabled or install a newer version of PHP.',
279 + $this->text_domain );
280 +
281 + /*
282 + * open_basedir can be used to limit the files that can be accessed by PHP to the specified
283 + * directory-tree, including the file itself. When a script tries to access the filesystem,
284 + * for example using include, or fopen(), the location of the file is checked. When the
285 + * file is outside the specified directory-tree, PHP will refuse to access it.
286 + */
287 + } elseif ( ini_get( 'open_basedir' ) ) {
288 +
289 + $errors[] = sprintf( __( 'The PHP "%s" setting is enabled - the PHP cURL library cannot follow URL redirects.',
290 + $this->text_domain ), 'open_basedir' );
291 +
292 + $errors[] = __( 'Please contact your hosting provider to have this setting disabled.',
293 + $this->text_domain );
294 +
295 + } else {
296 +
297 + $errors[] = sprintf( __( 'The maximum number of URL redirects (%d) may have been exceeded.',
298 + $this->text_domain ), $this->curl_max_redirs );
299 + }
300 + }
301 +
302 + $errors[] = sprintf( __( 'Additional requests to retrieve this URL will be ignored for another %d second(s).',
303 + $this->text_domain ), $this->ignored[ 'for_secs' ] );
304 +
305 + /*
306 + * Combine all strings into one error notice.
307 + */
308 + $this->p->notice->err( $errors );
309 + }
310 +
311 + if ( $this->p->debug->enabled ) {
312 +
313 + $this->p->debug->log( 'ignoring requests to retrieve ' . $url_nofrag );
314 + }
315 + }
316 +
317 + /*
318 + * Clear all ignored URLs.
319 + *
320 + * Returns a count of cleared URLs.
321 + */
322 + public function clear_ignored_urls() {
323 +
324 + $this->maybe_load_ignored();
325 +
326 + $count = count( $this->ignored[ 'urls' ] );
327 +
328 + $this->ignored[ 'urls' ] = array(); // Clear the ignored URLs.
329 +
330 + $this->save_ignored_urls();
331 +
332 + return $count;
333 + }
334 +
335 + /*
336 + * Clear a single ignored URL.
337 + *
338 + * Returns true if the URL was ignored and is now cleared.
339 + */
340 + public function clear_ignored_url( $url_nofrag ) {
341 +
342 + $this->maybe_load_ignored();
343 +
344 + if ( isset( $this->ignored[ 'urls' ][ $url_nofrag ] ) ) {
345 +
346 + unset( $this->ignored[ 'urls' ][ $url_nofrag ] );
347 +
348 + return true;
349 + }
350 +
351 + return false;
352 + }
353 +
354 + public function clear( $url, $cache_pre_ext = '' ) {
355 +
356 + if ( ! empty( $this->p->debug->enabled ) ) {
357 +
358 + $this->p->debug->mark();
359 + }
360 +
361 + $url_nofrag = preg_replace( '/#.*$/', '', $url ); // Remove the fragment.
362 +
363 + $cache_salt = __CLASS__ . '::get(url:' . $url_nofrag . ')';
364 +
365 + $this->clear_ignored_url( $url_nofrag );
366 +
367 + $this->clear_cache_data( $cache_salt, $cache_pre_ext, $url );
368 + }
369 +
370 + /*
371 + * Get the formatted microtime it took to retrieve the URL.
372 + *
373 + * Returns false if retrieving failed, true if the URL was already cached, or the formatted microtime.
374 + */
375 + public function get_url_mtime( $url, $precision = 3 ) {
376 +
377 + if ( isset( $this->url_get_mtimes[ $url ] ) ) {
378 +
379 + if ( is_bool( $this->url_get_mtimes[ $url ] ) ) {
380 +
381 + return $this->url_get_mtimes[ $url ];
382 +
383 + } else {
384 +
385 + return sprintf( '%.0' . $precision . 'f', $this->url_get_mtimes[ $url ] );
386 + }
387 +
388 + } else {
389 +
390 + return false;
391 + }
392 + }
393 +
394 + /*
395 + * Get image size for remote URL and cache for 300 seconds (5 minutes) by default.
396 + *
397 + * If $cache_exp_secs is null, then use the default expiration time.
398 + *
399 + * If $cache_exp_secs is false, then get but do not save the data.
400 + *
401 + * Note that PHP v7.1 or better is required to get the image size of WebP images.
402 + */
403 + public function get_image_size( $image_url, $cache_exp_secs = 300, $curl_opts = array(), $error_handler = null ) {
404 +
405 + if ( $this->p->debug->enabled ) {
406 +
407 + $this->p->debug->mark();
408 + }
409 +
410 + $file_path = $this->get( $image_url, 'file_path', $cache_type = 'file', $cache_exp_secs, $cache_pre_ext = '', $curl_opts );
411 +
412 + if ( ! empty( $file_path ) ) { // False on error.
413 +
414 + if ( file_exists( $file_path ) ) {
415 +
416 + if ( null !== $error_handler ) {
417 +
418 + $previous_error_handler = set_error_handler( $error_handler );
419 + }
420 +
421 + $image_size = getimagesize( $file_path ); // Note that WebP is only supported since PHP v7.1.
422 +
423 + if ( null !== $error_handler ) {
424 +
425 + restore_error_handler();
426 + }
427 +
428 + if ( $this->p->debug->enabled ) {
429 +
430 + $this->p->debug->log_arr( 'getimagesize', $image_size );
431 + }
432 +
433 + return $image_size;
434 +
435 + } elseif ( $this->p->debug->enabled ) {
436 +
437 + $this->p->debug->log( $file_path.' does not exist' );
438 + }
439 +
440 + } elseif ( $this->p->debug->enabled ) {
441 +
442 + $this->p->debug->log( 'returned image file path is empty' );
443 + }
444 +
445 + return false;
446 + }
447 +
448 + /*
449 + * Called by SucomForm->get_event_load_json_script().
450 + */
451 + public function get_data_url( $cache_salt, $cache_data, $cache_exp_secs = null, $cache_pre_ext = '' ) {
452 +
453 + if ( $this->p->debug->enabled ) {
454 +
455 + $this->p->debug->mark();
456 + }
457 +
458 + $file_name = md5( $cache_salt ) . $cache_pre_ext;
459 + $cache_file = $this->base_dir . $file_name;
460 + $cache_url = $this->base_url . $file_name;
461 +
462 + if ( file_exists( $cache_file ) ) {
463 +
464 + $file_mod_time = filemtime( $cache_file );
465 + $file_exp_secs = null === $cache_exp_secs ? $this->default_file_cache_exp : $cache_exp_secs;
466 +
467 + if ( false !== $file_exp_secs && $file_mod_time > time() - $file_exp_secs ) {
468 +
469 + if ( $this->p->debug->enabled ) {
470 +
471 + $this->p->debug->log( 'cached file found: returning url ' . $cache_url );
472 + }
473 +
474 + $cache_url = add_query_arg( 'modtime', $file_mod_time, $cache_url );
475 +
476 + return $cache_url;
477 +
478 + } elseif ( @unlink( $cache_file ) ) {
479 +
480 + if ( $this->p->debug->enabled ) {
481 +
482 + $this->p->debug->log( 'removed expired cache file ' . $cache_file );
483 + }
484 +
485 + } else {
486 +
487 + if ( $this->p->debug->enabled ) {
488 +
489 + $this->p->debug->log( 'error removing cache file ' . $cache_file );
490 + }
491 +
492 + $notice_pre = sprintf( '%s error:', __METHOD__ );
493 + $notice_msg = sprintf( __( 'Error removing cache file %s.', $this->text_domain ), $cache_file );
494 +
495 + $this->p->notice->err( $notice_msg );
496 +
497 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
498 + }
499 +
500 + } elseif ( $this->p->debug->enabled ) {
501 +
502 + $this->p->debug->log( 'cached file not found: creating ' . $cache_url );
503 + }
504 +
505 + if ( $this->save_cache_data( $cache_salt, $cache_data, $cache_type = 'file', $cache_exp_secs, $cache_pre_ext ) ) {
506 +
507 + if ( file_exists( $cache_file ) ) {
508 +
509 + $file_mod_time = filemtime( $cache_file );
510 +
511 + if ( $this->p->debug->enabled ) {
512 +
513 + $this->p->debug->log( 'cache data sucessfully saved' );
514 + }
515 +
516 + $cache_url = add_query_arg( 'modtime', $file_mod_time, $cache_url );
517 +
518 + return $cache_url;
519 + }
520 + }
521 +
522 + return false;
523 + }
524 +
525 + public function get_cache_files_size_mb() {
526 +
527 + if ( ! $dh = @opendir( $this->base_dir ) ) return false;
528 +
529 + $cache_files = array();
530 +
531 + while ( $file_name = @readdir( $dh ) ) {
532 +
533 + $cache_file = $this->base_dir . $file_name;
534 +
535 + if ( ! preg_match( '/^(\..*|index\.php)$/', $file_name ) && is_file( $cache_file ) ) {
536 +
537 + if ( $file_ext = strtoupper( pathinfo( $cache_file, PATHINFO_EXTENSION ) ) ) {
538 +
539 + if ( ! isset( $cache_files[ $file_ext ] ) ) {
540 +
541 + $cache_files[ $file_ext ] = array( 'count' => 0, 'size' => 0 );
542 + }
543 +
544 + $cache_files[ $file_ext ][ 'count' ]++;
545 +
546 + $cache_files[ $file_ext ][ 'size' ] += filesize( $cache_file );
547 + }
548 + }
549 + }
550 +
551 + closedir( $dh );
552 +
553 + foreach ( $cache_files as $ext => &$info ) {
554 +
555 + $info[ 'size' ] = $info[ 'size' ] / 1024 / 1024;
556 + }
557 +
558 + return $cache_files;
559 + }
560 +
561 + /*
562 + * If $cache_exp_secs is null, then use the default expiration time.
563 + *
564 + * If $cache_exp_secs is false, then get but do not save the data.
565 + */
566 + public function get( $url, $cache_format = 'url', $cache_type = 'file', $cache_exp_secs = null, $cache_pre_ext = '', $curl_opts = array() ) {
567 +
568 + if ( $this->p->debug->enabled ) {
569 +
570 + $this->p->debug->mark();
571 + }
572 +
573 + $failure = 'url' === $cache_format ? $url : false;
574 +
575 + $this->url_get_mtimes[ $url ] = false; // Default value for failure.
576 +
577 + if ( ! extension_loaded( 'curl' ) ) {
578 +
579 + if ( $this->p->debug->enabled ) {
580 +
581 + $this->p->debug->log( 'exiting early: curl library is missing' );
582 + }
583 +
584 + $notice_pre = sprintf( '%s error:', __METHOD__ );
585 + $notice_msg = __( 'PHP cURL library missing - contact your hosting provider to have the cURL library installed.', $this->text_domain );
586 +
587 + $this->p->notice->err( $notice_msg );
588 +
589 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
590 +
591 + return $failure;
592 + }
593 +
594 + $url_nofrag = preg_replace( '/#.*$/', '', $url ); // Remove the URL fragment.
595 + $url_path = wp_parse_url( $url_nofrag, PHP_URL_PATH );
596 +
597 + if ( '' === $cache_pre_ext ) { // Default is an empty string.
598 +
599 + if ( $cache_pre_ext = pathinfo( $url_path, PATHINFO_EXTENSION ) ) {
600 +
601 + $cache_pre_ext = '.' . $cache_pre_ext;
602 + }
603 + }
604 +
605 + $url_fragment = wp_parse_url( $url, PHP_URL_FRAGMENT );
606 +
607 + if ( ! empty( $url_fragment ) ) {
608 +
609 + $url_fragment = '#' . $url_fragment;
610 + }
611 +
612 + $cache_salt = __CLASS__ . '::get(url:' . $url_nofrag . ')';
613 + $cache_file = $this->base_dir . md5( $cache_salt ) . $cache_pre_ext;
614 + $cache_url = $this->base_url . md5( $cache_salt ) . $cache_pre_ext . $url_fragment;
615 + $cache_data = false;
616 +
617 + /*
618 + * Return immediately if the cache contains what we need.
619 + */
620 + switch ( $cache_format ) {
621 +
622 + case 'raw':
623 +
624 + $cache_data = $this->get_cache_data( $cache_salt, $cache_type, $cache_exp_secs, $cache_pre_ext );
625 +
626 + if ( false !== $cache_data ) {
627 +
628 + if ( $this->p->debug->enabled ) {
629 +
630 + $this->p->debug->log( 'cached data found: returning ' . strlen( $cache_data ) . ' chars' );
631 + }
632 +
633 + $this->url_get_mtimes[ $url ] = true; // Signal return is from cache.
634 +
635 + return $cache_data;
636 + }
637 +
638 + break;
639 +
640 + case 'url':
641 + case 'file_path':
642 +
643 + if ( file_exists( $cache_file ) ) {
644 +
645 + $file_mod_time = filemtime( $cache_file );
646 + $file_exp_secs = null === $cache_exp_secs ? $this->default_file_cache_exp : $cache_exp_secs;
647 +
648 + if ( false !== $cache_exp_secs && $file_mod_time > time() - $file_exp_secs ) {
649 +
650 + $this->url_get_mtimes[ $url ] = true; // Signal that return is from cache.
651 +
652 + if ( 'url' === $cache_format ) {
653 +
654 + $cache_url = add_query_arg( 'modtime', $file_mod_time, $cache_url );
655 +
656 + if ( $this->p->debug->enabled ) {
657 +
658 + $this->p->debug->log( 'cached file found: returning ' . $cache_format . ' ' . $cache_url );
659 + }
660 +
661 + return $cache_url;
662 +
663 + } else {
664 +
665 + if ( $this->p->debug->enabled ) {
666 +
667 + $this->p->debug->log( 'cached file found: returning ' . $cache_format . ' ' . $cache_file );
668 + }
669 +
670 + return $cache_file;
671 + }
672 +
673 + } elseif ( @unlink( $cache_file ) ) { // Remove expired file.
674 +
675 + if ( $this->p->debug->enabled ) {
676 +
677 + $this->p->debug->log( 'removed expired cache file ' . $cache_file );
678 + }
679 +
680 + } else {
681 +
682 + if ( $this->p->debug->enabled ) {
683 +
684 + $this->p->debug->log( 'error removing cache file ' . $cache_file );
685 + }
686 +
687 + $notice_pre = sprintf( '%s error:', __METHOD__ );
688 + $notice_msg = sprintf( __( 'Error removing cache file %s.', $this->text_domain ), $cache_file );
689 +
690 + $this->p->notice->err( $notice_msg );
691 +
692 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
693 + }
694 + }
695 +
696 + break;
697 +
698 + default:
699 +
700 + if ( $this->p->debug->enabled ) {
701 +
702 + $this->p->debug->log( 'cache return type for ' . $cache_format . ' is unknown' );
703 + }
704 +
705 + return $failure;
706 + }
707 +
708 + if ( $this->is_ignored_url( $url_nofrag ) ) { // Returns false or time in seconds.
709 +
710 + return $failure;
711 + }
712 +
713 + /*
714 + * Maybe throttle connections for specific hosts, like YouTube for example.
715 + */
716 + $this->maybe_throttle_host( $url_nofrag, $curl_opts );
717 +
718 + $ch = curl_init();
719 +
720 + curl_setopt( $ch, CURLOPT_URL, $url_nofrag );
721 + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
722 + curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $this->curl_connect_timeout ); // 10 seconds (default is 300 seconds).
723 + curl_setopt( $ch, CURLOPT_TIMEOUT, $this->curl_timeout ); // 15 seconds (default is no timeout).
724 +
725 + /*
726 + * When negotiating a TLS or SSL connection, the server sends a certificate indicating its identity. Curl
727 + * verifies whether the certificate is authentic, i.e. that you can trust that the server is who the
728 + * certificate says it is. This trust is based on a chain of digital signatures, rooted in certification
729 + * authority (CA) certificates you supply. curl uses a default bundle of CA certificates (the path for that
730 + * is determined at build time) and you can specify alternate certificates with the CURLOPT_CAINFO option
731 + * or the CURLOPT_CAPATH option.
732 + *
733 + * See https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYPEER.html
734 + */
735 + curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 1 );
736 +
737 + /*
738 + * When CURLOPT_SSL_VERIFYHOST is 2, that certificate must indicate that the server is the server to which
739 + * you meant to connect, or the connection fails. Simply put, it means it has to have the same name in the
740 + * certificate as is in the URL you operate against.
741 + *
742 + * See https://curl.haxx.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html
743 + */
744 + curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
745 +
746 + /*
747 + * Define and disable the "Expect: 100-continue" header.
748 + */
749 + curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Expect:' ) );
750 +
751 + if ( ! ini_get( 'safe_mode' ) && ! ini_get( 'open_basedir' ) ) {
752 +
753 + curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );
754 + curl_setopt( $ch, CURLOPT_MAXREDIRS, $this->curl_max_redirs );
755 +
756 + } elseif ( $this->p->debug->enabled ) {
757 +
758 + $this->p->debug->log( 'curl: PHP safe_mode or open_basedir defined - cannot use CURLOPT_FOLLOWLOCATION' );
759 + }
760 +
761 + /*
762 + * Define cURL options from values defined as constants.
763 + */
764 + $allowed_curl_const = array(
765 + 'CAINFO',
766 + 'CONNECTTIMEOUT',
767 + 'PROXY',
768 + 'PROXYUSERPWD',
769 + 'SSLVERSION',
770 + 'USERAGENT',
771 + );
772 +
773 + foreach ( $allowed_curl_const as $const_suffix ) {
774 +
775 + $curl_const_name = 'CURLOPT_' . $const_suffix;
776 +
777 + $custom_const_name = $this->plugin_ucid . '_PHP_CURL_' . $const_suffix;
778 +
779 + if ( defined( $custom_const_name ) ) {
780 +
781 + curl_setopt( $ch, constant( $curl_const_name ), constant( $custom_const_name ) );
782 + }
783 + }
784 +
785 + /*
786 + * Define cURL options provided by the $curl_opts method arguent (empty by default).
787 + *
788 + * Example:
789 + *
790 + * $curl_opts = array(
791 + * 'CURLOPT_USERAGENT' => 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)',
792 + * 'CURLOPT_COOKIELIST' => 'ALL', // Erases all cookies held in memory.
793 + * 'CURLOPT_CACHE_THROTTLE' => $throttle_secs, // Internal curl option.
794 + * );
795 + */
796 + if ( ! empty( $curl_opts ) ) {
797 +
798 + foreach ( $curl_opts as $opt_key => $opt_val ) {
799 +
800 + if ( 0 === strpos( $opt_key, 'CURLOPT_' ) ) { // Just in case.
801 +
802 + if ( 'CURLOPT_CACHE_THROTTLE' === $opt_key ) continue; // Internal curl option.
803 +
804 + if ( $this->p->debug->enabled ) {
805 +
806 + $this->p->debug->log( 'curl: setting custom curl option ' . $opt_key . ' = ' . $opt_val );
807 + }
808 +
809 + curl_setopt( $ch, constant( $opt_key ), $opt_val );
810 + }
811 + }
812 + }
813 +
814 + if ( $this->p->debug->enabled ) {
815 +
816 + $this->p->debug->log( 'curl: fetching ' . $url_nofrag );
817 + }
818 +
819 + $mtime_start = microtime( $get_float = true );
820 + $cache_data = curl_exec( $ch );
821 + $mtime_total = microtime( $get_float = true ) - $mtime_start;
822 + $curl_errnum = curl_errno( $ch );
823 + $curl_errmsg = curl_error( $ch ); // See https://curl.se/libcurl/c/libcurl-errors.html.
824 + $ssl_verify = (int) curl_getinfo( $ch, CURLINFO_SSL_VERIFYRESULT ); // See https://www.php.net/manual/en/function.curl-getinfo.php.
825 + $http_code = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
826 +
827 + curl_close( $ch );
828 +
829 + $http_success_codes = array( 200, 201 );
830 +
831 + if ( $this->p->debug->enabled ) {
832 +
833 + $this->p->debug->log( 'curl: execution time = ' . $mtime_total );
834 + $this->p->debug->log( 'curl: error number = ' . $curl_errnum );
835 + $this->p->debug->log( 'curl: error message = ' . $curl_errmsg );
836 + $this->p->debug->log( 'curl: ssl verify code = ' . $ssl_verify );
837 + $this->p->debug->log( 'curl: http return code = ' . $http_code );
838 + }
839 +
840 + if ( $curl_errnum || $ssl_verify || ! in_array( $http_code, $http_success_codes ) ) {
841 +
842 + $this->add_ignored_url( $url_nofrag, $mtime_total, $curl_errnum, $curl_errmsg, $ssl_verify, $http_code );
843 +
844 + } else {
845 +
846 + $this->url_get_mtimes[ $url ] = $mtime_total;
847 +
848 + if ( empty( $cache_data ) ) {
849 +
850 + if ( $this->p->debug->enabled ) {
851 +
852 + $this->p->debug->log( 'cache data returned is empty' );
853 + }
854 +
855 + } elseif ( false !== $cache_exp_secs ) { // Optimize and check first.
856 +
857 + if ( $this->save_cache_data( $cache_salt, $cache_data, $cache_type, $cache_exp_secs, $cache_pre_ext ) ) {
858 +
859 + if ( $this->p->debug->enabled ) {
860 +
861 + $this->p->debug->log( 'cache data sucessfully saved' );
862 + }
863 + }
864 + }
865 +
866 + switch ( $cache_format ) {
867 +
868 + case 'raw':
869 +
870 + return $cache_data;
871 +
872 + case 'url':
873 +
874 + $file_mod_time = filemtime( $cache_file );
875 +
876 + $cache_url = add_query_arg( 'modtime', $file_mod_time, $cache_url );
877 +
878 + return $cache_url;
879 +
880 + case 'file_path':
881 +
882 + return $cache_file;
883 + }
884 + }
885 +
886 + return $failure;
887 + }
888 +
889 + /*
890 + * If $cache_exp_secs is null, then use the default expiration time.
891 + *
892 + * If $cache_exp_secs is false, then get but do not save the data.
893 + */
894 + public function get_cache_data( $cache_salt, $cache_type = 'file', $cache_exp_secs = null, $cache_pre_ext = '' ) {
895 +
896 + $cache_data = false;
897 +
898 + if ( false === $cache_exp_secs ) {
899 +
900 + return $cache_data;
901 + }
902 +
903 + if ( $this->p->debug->enabled ) {
904 +
905 + $this->p->debug->log( $cache_type . ' cache salt ' . $cache_salt );
906 + }
907 +
908 + $cache_md5_pre = $cache_pre_ext ? $cache_pre_ext : $this->plugin_id . '_'; // Default is an empty string.
909 + $cache_id = $cache_md5_pre . md5( $cache_salt );
910 +
911 + switch ( $cache_type ) {
912 +
913 + case 'wp_cache':
914 +
915 + $cache_data = wp_cache_get( $cache_id, __CLASS__ );
916 +
917 + break;
918 +
919 + case 'transient':
920 +
921 + $cache_data = get_transient( $cache_id );
922 +
923 + break;
924 +
925 + case 'file':
926 +
927 + $file_name = md5( $cache_salt ) . $cache_pre_ext;
928 + $cache_file = $this->base_dir . $file_name;
929 + $file_exp_secs = null === $cache_exp_secs ? $this->default_file_cache_exp : $cache_exp_secs;
930 +
931 + if ( ! file_exists( $cache_file ) ) {
932 +
933 + if ( $this->p->debug->enabled ) {
934 +
935 + $this->p->debug->log( 'cache file ' . $cache_file . ' does not exist' );
936 + }
937 +
938 + } elseif ( ! is_readable( $cache_file ) ) {
939 +
940 + if ( $this->p->debug->enabled ) {
941 +
942 + $this->p->debug->log( 'cache file ' . $cache_file . ' is not readable' );
943 + }
944 +
945 + $notice_pre = sprintf( '%s error:', __METHOD__ );
946 + $notice_msg = sprintf( __( 'Cache file %s is not readable.', $this->text_domain ), $cache_file );
947 +
948 + $this->p->notice->err( $notice_msg );
949 +
950 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
951 +
952 + } elseif ( filemtime( $cache_file ) < time() - $file_exp_secs ) {
953 +
954 + if ( $this->p->debug->enabled ) {
955 +
956 + $this->p->debug->log( $cache_file . ' is expired' );
957 + }
958 +
959 + @unlink( $cache_file );
960 +
961 + } elseif ( ! $fz = filesize( $cache_file ) ) { // Check if file size is greater then 0.
962 +
963 + if ( $this->p->debug->enabled ) {
964 +
965 + $this->p->debug->log( $cache_file . ' is empty' );
966 + }
967 +
968 + @unlink( $cache_file );
969 +
970 + } elseif ( ! $fh = @fopen( $cache_file, 'rb' ) ) { // Check if we get a file handle.
971 +
972 + if ( $this->p->debug->enabled ) {
973 +
974 + $this->p->debug->log( 'failed to open the cache file ' . $cache_file . ' for reading' );
975 + }
976 +
977 + $notice_pre = sprintf( '%s error:', __METHOD__ );
978 + $notice_msg = sprintf( __( 'Failed to open the cache file %s for reading.', $this->text_domain ), $cache_file );
979 +
980 + $this->p->notice->err( $notice_msg );
981 +
982 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
983 +
984 + } else {
985 +
986 + $cache_data = fread( $fh, $fz );
987 +
988 + fclose( $fh );
989 + }
990 +
991 + break;
992 +
993 + default:
994 +
995 + if ( $this->p->debug->enabled ) {
996 +
997 + $this->p->debug->log( 'unknown cache type: ' . $cache_type );
998 + }
999 +
1000 + break;
1001 + }
1002 +
1003 + if ( $this->p->debug->enabled && false !== $cache_data ) {
1004 +
1005 + $this->p->debug->log( 'cache data retrieved from ' . $cache_type );
1006 + }
1007 +
1008 + return $cache_data; // return data or empty string
1009 + }
1010 +
1011 + /*
1012 + * If $cache_exp_secs is null, then use the default expiration time.
1013 + *
1014 + * If $cache_exp_secs is false, then get but do not save the data.
1015 + */
1016 + public function save_cache_data( $cache_salt, $cache_data = '', $cache_type = 'file', $cache_exp_secs = null, $cache_pre_ext = '' ) {
1017 +
1018 + $data_saved = false;
1019 +
1020 + if ( empty( $cache_data ) || false === $cache_exp_secs ) {
1021 +
1022 + return $data_saved;
1023 + }
1024 +
1025 + $obj_exp_secs = null === $cache_exp_secs ? $this->default_object_cache_exp : $cache_exp_secs;
1026 + $file_exp_secs = null === $cache_exp_secs ? $this->default_file_cache_exp : $cache_exp_secs;
1027 +
1028 + if ( $this->p->debug->enabled ) {
1029 +
1030 + $this->p->debug->log( $cache_type . ' cache salt ' . $cache_salt );
1031 + }
1032 +
1033 + $cache_md5_pre = $cache_pre_ext ? $cache_pre_ext : $this->plugin_id . '_'; // Default is an empty string.
1034 + $cache_id = $cache_md5_pre . md5( $cache_salt );
1035 +
1036 + switch ( $cache_type ) {
1037 +
1038 + case 'wp_cache':
1039 +
1040 + wp_cache_set( $cache_id, $cache_data, __CLASS__, $obj_exp_secs );
1041 +
1042 + if ( $this->p->debug->enabled ) {
1043 +
1044 + $this->p->debug->log( 'cache data saved to ' . $cache_type . ' ' .
1045 + $cache_id . ' (' . $obj_exp_secs . ' seconds)' );
1046 + }
1047 +
1048 + $data_saved = true; // Success.
1049 +
1050 + break;
1051 +
1052 + case 'transient':
1053 +
1054 + set_transient( $cache_id, $cache_data, $obj_exp_secs );
1055 +
1056 + if ( $this->p->debug->enabled ) {
1057 +
1058 + $this->p->debug->log( 'cache data saved to ' . $cache_type . ' ' .
1059 + $cache_id . ' (' . $obj_exp_secs . ' seconds)' );
1060 + }
1061 +
1062 + $data_saved = true; // Success.
1063 +
1064 + break;
1065 +
1066 + case 'file':
1067 +
1068 + $file_name = md5( $cache_salt ) . $cache_pre_ext;
1069 + $cache_file = $this->base_dir . $file_name;
1070 +
1071 + if ( ! $file_exp_secs ) { // False or 0.
1072 +
1073 + if ( file_exists( $cache_file ) ) {
1074 +
1075 + @unlink( $cache_file );
1076 + }
1077 +
1078 + } elseif ( ! is_dir( $this->base_dir ) && ! mkdir( $this->base_dir ) ) {
1079 +
1080 + if ( $this->p->debug->enabled ) {
1081 +
1082 + $this->p->debug->log( 'failed to create the ' . $this->base_dir . ' cache folder.' );
1083 + }
1084 +
1085 + $notice_pre = sprintf( '%s error:', __METHOD__ );
1086 + $notice_msg = sprintf( __( 'Failed to create the %s cache folder.', $this->text_domain ), $this->base_dir );
1087 +
1088 + $this->p->notice->err( $notice_msg );
1089 +
1090 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
1091 +
1092 + } elseif ( ! is_writable( $this->base_dir ) ) {
1093 +
1094 + if ( $this->p->debug->enabled ) {
1095 +
1096 + $this->p->debug->log( 'cache folder ' . $this->base_dir . ' is not writable' );
1097 + }
1098 +
1099 + $notice_pre = sprintf( '%s error:', __METHOD__ );
1100 + $notice_msg = sprintf( __( 'Cache folder %s is not writable.', $this->text_domain ), $this->base_dir );
1101 +
1102 + $this->p->notice->err( $notice_msg );
1103 +
1104 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
1105 +
1106 + } elseif ( ! $fh = @fopen( $cache_file, 'wb' ) ) { // Check if we get a file handle.
1107 +
1108 + if ( $this->p->debug->enabled ) {
1109 +
1110 + $this->p->debug->log( 'failed to open the cache file ' . $cache_file . ' for writing' );
1111 + }
1112 +
1113 + $notice_pre = sprintf( '%s error:', __METHOD__ );
1114 + $notice_msg = sprintf( __( 'Failed to open the cache file %s for writing.', $this->text_domain ), $cache_file );
1115 +
1116 + $this->p->notice->err( $notice_msg );
1117 +
1118 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
1119 +
1120 + } elseif ( fwrite( $fh, $cache_data ) ) {
1121 +
1122 + if ( $this->p->debug->enabled ) {
1123 +
1124 + $this->p->debug->log( 'data saved to cache file ' . $cache_file );
1125 + }
1126 +
1127 + fclose( $fh );
1128 +
1129 + $data_saved = true; // Success.
1130 +
1131 + } else { // Error writing to cache file.
1132 +
1133 + fclose( $fh );
1134 +
1135 + @unlink( $cache_file );
1136 +
1137 + if ( $this->p->debug->enabled ) {
1138 +
1139 + $this->p->debug->log( 'failed writing data to cache file ' . $cache_file );
1140 + }
1141 +
1142 + $notice_pre = sprintf( '%s error:', __METHOD__ );
1143 + $notice_msg = sprintf( __( 'Failed writing data to cache file %s.', $this->text_domain ), $cache_file );
1144 +
1145 + $this->p->notice->err( $notice_msg );
1146 +
1147 + SucomUtil::safe_error_log( $notice_pre . ' ' . $notice_msg );
1148 + }
1149 +
1150 + break;
1151 +
1152 + default:
1153 +
1154 + if ( $this->p->debug->enabled ) {
1155 +
1156 + $this->p->debug->log( 'unknown cache type: ' . $cache_type );
1157 + }
1158 +
1159 + break;
1160 + }
1161 +
1162 + return $data_saved; // Return true or false.
1163 + }
1164 +
1165 + public function clear_cache_data( $cache_salt, $cache_pre_ext = '', $url = '' ) {
1166 +
1167 + if ( 0 !== strpos( $cache_pre_ext, '.' ) ) { // Not a filename extension.
1168 +
1169 + $cache_md5_pre = $cache_pre_ext ? $cache_pre_ext : $this->plugin_id . '_'; // Default is an empty string.
1170 + $cache_id = $cache_md5_pre . md5( $cache_salt );
1171 +
1172 + if ( wp_cache_delete( $cache_id, __CLASS__ ) ) {
1173 +
1174 + if ( $this->p->debug->enabled ) {
1175 +
1176 + $this->p->debug->log( 'cleared object cache salt: ' . $cache_salt );
1177 + }
1178 + }
1179 +
1180 + if ( delete_transient( $cache_id ) ) {
1181 +
1182 + if ( $this->p->debug->enabled ) {
1183 +
1184 + $this->p->debug->log( 'cleared transient cache salt: ' . $cache_salt );
1185 + }
1186 + }
1187 + }
1188 +
1189 + if ( $this->base_dir ) { // Just in case.
1190 +
1191 + if ( '' === $cache_pre_ext ) { // Default is an empty string.
1192 +
1193 + if ( ! empty( $url ) ) {
1194 +
1195 + $url_nofrag = preg_replace( '/#.*$/', '', $url ); // Remove the fragment.
1196 + $url_path = wp_parse_url( $url_nofrag, PHP_URL_PATH );
1197 +
1198 + if ( $cache_pre_ext = pathinfo( $url_path, PATHINFO_EXTENSION ) ) {
1199 +
1200 + $cache_pre_ext = '.' . $cache_pre_ext;
1201 + }
1202 + }
1203 + }
1204 +
1205 + $file_name = md5( $cache_salt ) . $cache_pre_ext;
1206 + $cache_file = $this->base_dir . $file_name;
1207 +
1208 + if ( file_exists( $cache_file ) ) {
1209 +
1210 + if ( @unlink( $cache_file ) ) {
1211 +
1212 + if ( $this->p->debug->enabled ) {
1213 +
1214 + $this->p->debug->log( 'cleared local cache file: ' . $cache_file );
1215 + }
1216 + }
1217 + }
1218 + }
1219 + }
1220 +
1221 + public function is_cached( $url, $cache_format = 'url', $cache_type = 'file', $cache_exp_secs = null, $cache_pre_ext = '' ) {
1222 +
1223 + $url_nofrag = preg_replace( '/#.*$/', '', $url ); // Remove the URL fragment.
1224 + $url_path = wp_parse_url( $url_nofrag, PHP_URL_PATH );
1225 +
1226 + if ( '' === $cache_pre_ext ) { // Default is an empty string.
1227 +
1228 + if ( $cache_pre_ext = pathinfo( $url_path, PATHINFO_EXTENSION ) ) {
1229 +
1230 + $cache_pre_ext = '.' . $cache_pre_ext;
1231 + }
1232 + }
1233 +
1234 + $cache_salt = __CLASS__ . '::get(url:' . $url_nofrag . ')';
1235 + $cache_file = $this->base_dir . md5( $cache_salt ) . $cache_pre_ext;
1236 + $cache_url = $this->base_url . md5( $cache_salt ) . $cache_pre_ext;
1237 +
1238 + /*
1239 + * Return immediately if the cache contains what we need.
1240 + */
1241 + switch ( $cache_format ) {
1242 +
1243 + case 'raw':
1244 +
1245 + if ( false !== $this->get_cache_data( $cache_salt, $cache_type, $cache_exp_secs, $cache_pre_ext ) ) {
1246 +
1247 + return true;
1248 + }
1249 +
1250 + case 'url':
1251 + case 'file_path':
1252 +
1253 + if ( file_exists( $cache_file ) ) {
1254 +
1255 + $file_mod_time = filemtime( $cache_file );
1256 + $file_exp_secs = null === $cache_exp_secs ? $this->default_file_cache_exp : $cache_exp_secs;
1257 +
1258 + if ( false !== $cache_exp_secs && $file_mod_time > time() - $file_exp_secs ) {
1259 +
1260 + if ( 'url' === $cache_format ) {
1261 +
1262 + $cache_url = add_query_arg( 'moddate', gmdate( 'c', $file_mod_time ), $cache_url );
1263 +
1264 + return $cache_url;
1265 +
1266 + } else return $cache_file;
1267 + }
1268 + }
1269 +
1270 + break;
1271 + }
1272 +
1273 + return false;
1274 + }
1275 +
1276 + /*
1277 + * Maybe throttle connections for specific hosts, like YouTube for example.
1278 + */
1279 + public function maybe_throttle_host( $url, $curl_opts ) {
1280 +
1281 + if ( ! empty( $curl_opts[ 'CURLOPT_CACHE_THROTTLE' ] ) ) {
1282 +
1283 + $url_nofrag = preg_replace( '/#.*$/', '', $url ); // Remove the fragment.
1284 + $url_host = wp_parse_url( $url_nofrag, PHP_URL_HOST );
1285 + $cache_md5_pre = $this->plugin_id . '_!_'; // Preserved on clear cache.
1286 + $cache_salt = __METHOD__ . '(url_host:' . $url_host . ')'; // Throttle by host.
1287 + $cache_id = $cache_md5_pre . md5( $cache_salt );
1288 +
1289 + while ( $cache_ret = get_transient( $cache_id ) ) {
1290 +
1291 + if ( $cache_ret + $curl_opts[ 'CURLOPT_CACHE_THROTTLE' ] > time() ) {
1292 +
1293 + sleep( 1 );
1294 +
1295 + continue;
1296 + }
1297 +
1298 + break;
1299 + }
1300 +
1301 + set_transient( $cache_id, time(), $curl_opts[ 'CURLOPT_CACHE_THROTTLE' ] );
1302 + }
1303 + }
1304 + }
1305 +}