File manager - Edit - /home/vblioqus/dlskitsurl.com/wp-content/plugins/litespeed-cache/src/img-optm-pull.trait.php
Back
<?php /** * Image optimization pull trait * * @package LiteSpeed * @since 7.8 */ namespace LiteSpeed; use WpOrg\Requests\Autoload; use WpOrg\Requests\Requests; defined( 'WPINC' ) || exit(); /** * Trait Img_Optm_Pull * * Handles image optimization pull and notification. */ trait Img_Optm_Pull { /** * Cloud server notify Client img status changed * * @access public * @return array Response array. */ public function notify_img() { // Interval validation to avoid hacking domain_key if ( ! empty( $this->_summary['notify_ts_err'] ) && time() - $this->_summary['notify_ts_err'] < 3 ) { return Cloud::err( 'too_often' ); } $post_data = \json_decode( file_get_contents( 'php://input' ), true ); if ( is_null( $post_data ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing $post_data = $_POST; } global $wpdb; $notified_data = $post_data['data']; if ( empty( $notified_data ) || ! is_array( $notified_data ) ) { self::debug( '❌ notify exit: no notified data' ); return Cloud::err( 'no notified data' ); } if ( empty( $post_data['server'] ) || ( substr( $post_data['server'], -11 ) !== '.quic.cloud' && substr( $post_data['server'], -15 ) !== '.quicserver.com' ) ) { self::debug( 'notify exit: no/wrong server' ); return Cloud::err( 'no/wrong server' ); } if ( empty( $post_data['status'] ) ) { self::debug( 'notify missing status' ); return Cloud::err( 'no status' ); } $status = $post_data['status']; self::debug( 'notified status=' . $status ); $last_log_pid = 0; if ( empty( $this->_summary['reduced'] ) ) { $this->_summary['reduced'] = 0; } if ( self::STATUS_NOTIFIED === $status ) { // Notified data format: [ img_optm_id => [ id=>, src_size=>, ori=>, ori_md5=>, ori_reduced=>, webp=>, webp_md5=>, webp_reduced=> ] ] $q = "SELECT a.*, b.meta_id as b_meta_id, b.meta_value AS b_optm_info FROM `$this->_table_img_optming` a LEFT JOIN `$wpdb->postmeta` b ON b.post_id = a.post_id AND b.meta_key = %s WHERE a.id IN ( " . implode( ',', array_fill( 0, count( $notified_data ), '%d' ) ) . ' )'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $list = $wpdb->get_results( $wpdb->prepare( $q, array_merge( [ self::DB_SIZE ], array_keys( $notified_data ) ) ) ); $ls_optm_size_row_exists_postids = []; foreach ( $list as $v ) { $json = $notified_data[ $v->id ]; // self::debug('Notified data for [id] ' . $v->id, $json); $server = ! empty( $json['server'] ) ? $json['server'] : $post_data['server']; $server_info = [ 'server' => $server, ]; // Save server side ID to send taken notification after pulled $server_info['id'] = $json['id']; if ( ! empty( $json['file_id'] ) ) { $server_info['file_id'] = $json['file_id']; } // Optm info array $postmeta_info = [ 'ori_total' => 0, 'ori_saved' => 0, 'webp_total' => 0, 'webp_saved' => 0, 'avif_total' => 0, 'avif_saved' => 0, ]; // Init postmeta_info for the first one if ( ! empty( $v->b_meta_id ) ) { foreach ( maybe_unserialize( $v->b_optm_info ) as $k2 => $v2 ) { $postmeta_info[ $k2 ] += $v2; } } if ( ! empty( $json['ori'] ) ) { $server_info['ori_md5'] = $json['ori_md5']; $server_info['ori'] = $json['ori']; // Append meta info $postmeta_info['ori_total'] += $json['src_size']; $postmeta_info['ori_saved'] += $json['ori_reduced']; // optimized image size info in img_optm tb will be updated when pull $this->_summary['reduced'] += $json['ori_reduced']; } if ( ! empty( $json['webp'] ) ) { $server_info['webp_md5'] = $json['webp_md5']; $server_info['webp'] = $json['webp']; // Append meta info $postmeta_info['webp_total'] += $json['src_size']; $postmeta_info['webp_saved'] += $json['webp_reduced']; $this->_summary['reduced'] += $json['webp_reduced']; } if ( ! empty( $json['avif'] ) ) { $server_info['avif_md5'] = $json['avif_md5']; $server_info['avif'] = $json['avif']; // Append meta info $postmeta_info['avif_total'] += $json['src_size']; $postmeta_info['avif_saved'] += $json['avif_reduced']; $this->_summary['reduced'] += $json['avif_reduced']; } // Update status and data in working table $q = "UPDATE `$this->_table_img_optming` SET optm_status = %d, server_info = %s WHERE id = %d "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, [ $status, wp_json_encode( $server_info ), $v->id ] ) ); // Update postmeta for optm summary // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize $postmeta_info = serialize( $postmeta_info ); if ( empty( $v->b_meta_id ) && ! in_array( $v->post_id, $ls_optm_size_row_exists_postids, true ) ) { self::debug( 'New size info [pid] ' . $v->post_id ); $q = "INSERT INTO `$wpdb->postmeta` ( post_id, meta_key, meta_value ) VALUES ( %d, %s, %s )"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, [ $v->post_id, self::DB_SIZE, $postmeta_info ] ) ); $ls_optm_size_row_exists_postids[] = $v->post_id; } else { $q = "UPDATE `$wpdb->postmeta` SET meta_value = %s WHERE meta_id = %d "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, [ $postmeta_info, $v->b_meta_id ] ) ); } // write log $pid_log = $last_log_pid === $v->post_id ? '.' : $v->post_id; self::debug( 'notify_img [status] ' . $status . " \t\t[pid] " . $pid_log . " \t\t[id] " . $v->id ); $last_log_pid = $v->post_id; } self::save_summary(); // Mark need_pull tag for cron self::update_option( self::DB_NEED_PULL, self::STATUS_NOTIFIED ); } else { // Other errors will directly remove the working records // Delete from working table $q = "DELETE FROM `$this->_table_img_optming` WHERE id IN ( " . implode( ',', array_fill( 0, count( $notified_data ), '%d' ) ) . ' ) '; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, $notified_data ) ); } return Cloud::ok( [ 'count' => count( $notified_data ) ] ); } /** * Cron start async req * * @since 5.5 */ public static function start_async_cron() { Task::async_call( 'imgoptm' ); } /** * Manually start async req * * @since 5.5 */ public static function start_async() { Task::async_call( 'imgoptm_force' ); $msg = __( 'Started async image optimization request', 'litespeed-cache' ); Admin_Display::success( $msg ); } /** * Check if need to pull or not * * @since 7.2 * @return bool True if need to pull. */ public static function need_pull() { $tag = (int) self::get_option( self::DB_NEED_PULL ); if ( ! $tag || self::STATUS_NOTIFIED !== $tag ) { return false; } return true; } /** * Ajax req handler * * @since 5.5 * @param bool $force Whether to force pull. */ public static function async_handler( $force = false ) { self::debug( '------------async-------------start_async_handler' ); if ( ! self::need_pull() ) { self::debug( '❌ no need pull' ); return; } if ( defined( 'LITESPEED_IMG_OPTM_PULL_CRON' ) && ! constant( 'LITESPEED_IMG_OPTM_PULL_CRON' ) ) { self::debug( 'Cron disabled [define] LITESPEED_IMG_OPTM_PULL_CRON' ); return; } self::cls()->pull( $force ); } /** * Calculate pull threads * * @since 5.8 * @access private * @return int Number of images per request. */ private function _calc_pull_threads() { global $wpdb; if ( defined( 'LITESPEED_IMG_OPTM_PULL_THREADS' ) ) { return constant( 'LITESPEED_IMG_OPTM_PULL_THREADS' ); } // Tune number of images per request based on number of images waiting and cloud packages $imgs_per_req = 1; // base 1, ramp up to ~50 max // Ramp up the request rate based on how many images are waiting $c = "SELECT count(id) FROM `$this->_table_img_optming` WHERE optm_status = %d"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $_c = $wpdb->prepare( $c, [ self::STATUS_NOTIFIED ] ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $images_waiting = $wpdb->get_var( $_c ); if ( $images_waiting && $images_waiting > 0 ) { $imgs_per_req = ceil( $images_waiting / 1000 ); // ie. download 5/request if 5000 images are waiting } // Cap the request rate at 50 images per request $imgs_per_req = min( 50, $imgs_per_req ); self::debug( 'Pulling images at rate: ' . $imgs_per_req . ' Images per request.' ); return $imgs_per_req; } /** * Pull optimized img * * @since 1.6 * @access public * @param bool $manual Whether this is a manual pull. */ public function pull( $manual = false ) { global $wpdb; $timeout_limit = ini_get( 'max_execution_time' ); $endts = time() + $timeout_limit; self::debug( '' . ( $manual ? 'Manually' : 'Cron' ) . ' pull started [timeout: ' . $timeout_limit . 's]' ); if ( $this->cron_running() ) { self::debug( 'Pull cron is running' ); $msg = __( 'Pull Cron is running', 'litespeed-cache' ); Admin_Display::note( $msg ); return; } $this->_summary['last_pulled'] = time(); $this->_summary['last_pulled_by_cron'] = ! $manual; self::save_summary(); $imgs_per_req = $this->_calc_pull_threads(); $q = "SELECT * FROM `$this->_table_img_optming` WHERE optm_status = %d ORDER BY id LIMIT %d"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $_q = $wpdb->prepare( $q, [ self::STATUS_NOTIFIED, $imgs_per_req ] ); $rm_ori_bkup = $this->conf( self::O_IMG_OPTM_RM_BKUP ); $total_pulled_ori = 0; $total_pulled_webp = 0; $total_pulled_avif = 0; $server_list = []; try { // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition, WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared while ( $img_rows = $wpdb->get_results( $_q ) ) { self::debug( 'timeout left: ' . ( $endts - time() ) . 's' ); if ( function_exists( 'set_time_limit' ) ) { $endts += 600; self::debug( 'Endtime extended to ' . gmdate( 'Ymd H:i:s', $endts ) ); set_time_limit( 600 ); // This will be no more important as we use noabort now } // Disabled as we use noabort // if ($endts - time() < 10) { // self::debug("🚨 End loop due to timeout limit reached " . $timeout_limit . "s"); // break; // } /** * Update cron timestamp to avoid duplicated running * * @since 1.6.2 */ $this->_update_cron_running(); // Run requests in parallel $requests = []; // store each request URL for Requests::request_multiple() $imgs_by_req = []; // store original request data so that we can reference it in the response $req_counter = 0; foreach ( $img_rows as $row_img ) { // request original image $server_info = \json_decode( $row_img->server_info, true ); if ( ! empty( $server_info['ori'] ) ) { $image_url = $server_info['server'] . '/' . $server_info['ori']; self::debug( 'Queueing pull: ' . $image_url ); $requests[ $req_counter ] = [ 'url' => $image_url, 'type' => 'GET', ]; $imgs_by_req[ $req_counter++ ] = [ 'type' => 'ori', 'data' => $row_img, ]; } // request webp image $webp_size = 0; if ( ! empty( $server_info['webp'] ) ) { $image_url = $server_info['server'] . '/' . $server_info['webp']; self::debug( 'Queueing pull WebP: ' . $image_url ); $requests[ $req_counter ] = [ 'url' => $image_url, 'type' => 'GET', ]; $imgs_by_req[ $req_counter++ ] = [ 'type' => 'webp', 'data' => $row_img, ]; } // request avif image $avif_size = 0; if ( ! empty( $server_info['avif'] ) ) { $image_url = $server_info['server'] . '/' . $server_info['avif']; self::debug( 'Queueing pull AVIF: ' . $image_url ); $requests[ $req_counter ] = [ 'url' => $image_url, 'type' => 'GET', ]; $imgs_by_req[ $req_counter++ ] = [ 'type' => 'avif', 'data' => $row_img, ]; } } self::debug( 'Loaded images count: ' . $req_counter ); $complete_action = function ( $response, $req_count ) use ( $imgs_by_req, $rm_ori_bkup, &$total_pulled_ori, &$total_pulled_webp, &$total_pulled_avif, &$server_list ) { global $wpdb; $row_data = isset( $imgs_by_req[ $req_count ] ) ? $imgs_by_req[ $req_count ] : false; if ( false === $row_data ) { self::debug( '❌ failed to pull image: Request not found in lookup variable.' ); return; } $row_type = isset( $row_data['type'] ) ? $row_data['type'] : 'ori'; $row_img = $row_data['data']; $local_file = $this->wp_upload_dir['basedir'] . '/' . $row_img->src; $server_info = \json_decode( $row_img->server_info, true ); // Handle status_code 404/5xx too as its success=true if ( empty( $response->success ) || empty( $response->status_code ) || 200 !== $response->status_code ) { self::debug( '❌ Failed to pull optimized img: HTTP error [status_code] ' . ( empty( $response->status_code ) ? 'N/A' : $response->status_code ) ); $this->_step_back_image( $row_img->id ); $msg = __( 'Some optimized image file(s) has expired and was cleared.', 'litespeed-cache' ); Admin_Display::error( $msg ); return; } if ( 'webp' === $row_type ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents file_put_contents( $local_file . '.webp', $response->body ); if ( ! file_exists( $local_file . '.webp' ) || ! filesize( $local_file . '.webp' ) || md5_file( $local_file . '.webp' ) !== $server_info['webp_md5'] ) { self::debug( '❌ Failed to pull optimized webp img: file md5 mismatch, server md5: ' . $server_info['webp_md5'] ); // Delete working table $q = "DELETE FROM `$this->_table_img_optming` WHERE id = %d "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, $row_img->id ) ); $msg = __( 'Pulled WebP image md5 does not match the notified WebP image md5.', 'litespeed-cache' ); Admin_Display::error( $msg ); return; } self::debug( 'Pulled optimized img WebP: ' . $local_file . '.webp' ); $webp_size = filesize( $local_file . '.webp' ); /** * API for WebP * * @since 2.9.5 * @since 3.0 $row_img less elements (see above one) * @see #751737 - API docs for WEBP generation */ do_action( 'litespeed_img_pull_webp', $row_img, $local_file . '.webp' ); ++$total_pulled_webp; } elseif ( 'avif' === $row_type ) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents file_put_contents( $local_file . '.avif', $response->body ); if ( ! file_exists( $local_file . '.avif' ) || ! filesize( $local_file . '.avif' ) || md5_file( $local_file . '.avif' ) !== $server_info['avif_md5'] ) { self::debug( '❌ Failed to pull optimized avif img: file md5 mismatch, server md5: ' . $server_info['avif_md5'] ); // Delete working table $q = "DELETE FROM `$this->_table_img_optming` WHERE id = %d "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, $row_img->id ) ); $msg = __( 'Pulled AVIF image md5 does not match the notified AVIF image md5.', 'litespeed-cache' ); Admin_Display::error( $msg ); return; } self::debug( 'Pulled optimized img AVIF: ' . $local_file . '.avif' ); $avif_size = filesize( $local_file . '.avif' ); /** * API for AVIF * * @since 7.0 */ do_action( 'litespeed_img_pull_avif', $row_img, $local_file . '.avif' ); ++$total_pulled_avif; } else { // "ori" image type // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents file_put_contents( $local_file . '.tmp', $response->body ); if ( ! file_exists( $local_file . '.tmp' ) || ! filesize( $local_file . '.tmp' ) || md5_file( $local_file . '.tmp' ) !== $server_info['ori_md5'] ) { self::debug( '❌ Failed to pull optimized img: file md5 mismatch [url] ' . $server_info['server'] . '/' . $server_info['ori'] . ' [server_md5] ' . $server_info['ori_md5'] ); // Delete working table $q = "DELETE FROM `$this->_table_img_optming` WHERE id = %d "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, $row_img->id ) ); $msg = __( 'One or more pulled images does not match with the notified image md5', 'litespeed-cache' ); Admin_Display::error( $msg ); return; } // Backup ori img if ( ! $rm_ori_bkup ) { $extension = pathinfo( $local_file, PATHINFO_EXTENSION ); $bk_file = substr( $local_file, 0, -strlen( $extension ) ) . 'bk.' . $extension; // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename file_exists( $local_file ) && rename( $local_file, $bk_file ); } // Replace ori img // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename rename( $local_file . '.tmp', $local_file ); self::debug( 'Pulled optimized img: ' . $local_file ); /** * API Hook * * @since 2.9.5 * @since 3.0 $row_img has less elements now. Most useful ones are `post_id`/`src` */ do_action( 'litespeed_img_pull_ori', $row_img, $local_file ); self::debug2( 'Remove _table_img_optming record [id] ' . $row_img->id ); } // Delete working table $q = "DELETE FROM `$this->_table_img_optming` WHERE id = %d "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, $row_img->id ) ); // Save server_list to notify taken if ( empty( $server_list[ $server_info['server'] ] ) ) { $server_list[ $server_info['server'] ] = []; } $server_info_id = ! empty( $server_info['file_id'] ) ? $server_info['file_id'] : $server_info['id']; $server_list[ $server_info['server'] ][] = $server_info_id; ++$total_pulled_ori; }; $force_wp_remote_get = defined( 'LITESPEED_FORCE_WP_REMOTE_GET' ) && constant( 'LITESPEED_FORCE_WP_REMOTE_GET' ); if ( ! $force_wp_remote_get && class_exists( '\WpOrg\Requests\Requests' ) && class_exists( '\WpOrg\Requests\Autoload' ) ) { // Make sure Requests can load internal classes. Autoload::register(); // Run pull requests in parallel Requests::request_multiple( $requests, [ 'timeout' => 60, 'connect_timeout' => 60, 'complete' => $complete_action, 'verify' => false, 'verifyname' => false, ] ); } else { foreach ( $requests as $cnt => $req ) { $wp_response = wp_safe_remote_get( $req['url'], [ 'timeout' => 60 ] ); $request_response = [ 'success' => false, 'status_code' => 0, 'body' => null, 'sslverify' => false, ]; if ( is_wp_error( $wp_response ) ) { $error_message = $wp_response->get_error_message(); self::debug( '❌ failed to pull image: ' . $error_message ); } else { $request_response['success'] = true; $request_response['status_code'] = $wp_response['response']['code']; $request_response['body'] = $wp_response['body']; } self::debug( 'response code [code] ' . $wp_response['response']['code'] . ' [url] ' . $req['url'] ); $request_response = (object) $request_response; $complete_action( $request_response, $cnt ); } } self::debug( 'Current batch pull finished' ); } } catch ( \Exception $e ) { Admin_Display::error( 'Image pull process failure: ' . $e->getMessage() ); } // Notify IAPI images taken foreach ( $server_list as $server => $img_list ) { $data = [ 'action' => self::CLOUD_ACTION_TAKEN, 'list' => $img_list, 'server' => $server, ]; // TODO: improve this so we do not call once per server, but just once and then filter on the server side Cloud::post( Cloud::SVC_IMG_OPTM, $data ); } if ( empty( $this->_summary['img_taken'] ) ) { $this->_summary['img_taken'] = 0; } $this->_summary['img_taken'] += $total_pulled_ori + $total_pulled_webp + $total_pulled_avif; self::save_summary(); // Manually running needs to roll back timestamp for next running if ( $manual ) { $this->_update_cron_running( true ); } // $msg = sprintf(__('Pulled %d image(s)', 'litespeed-cache'), $total_pulled_ori + $total_pulled_webp); // Admin_Display::success($msg); // Check if there is still task in queue $q = "SELECT * FROM `$this->_table_img_optming` WHERE optm_status = %d LIMIT 1"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $to_be_continued = $wpdb->get_row( $wpdb->prepare( $q, self::STATUS_NOTIFIED ) ); if ( $to_be_continued ) { self::debug( 'Task in queue, to be continued...' ); return; // return Router::self_redirect(Router::ACTION_IMG_OPTM, self::TYPE_PULL); } // If all pulled, update tag to done self::debug( 'Marked pull status to all pulled' ); self::update_option( self::DB_NEED_PULL, self::STATUS_PULLED ); } /** * Push image back to previous status * * @since 3.0 * @access private * @param int $id The image ID. */ private function _step_back_image( $id ) { global $wpdb; self::debug( 'Push image back to new status [id] ' . $id ); // Reset the image to gathered status $q = "UPDATE `$this->_table_img_optming` SET optm_status = %d WHERE id = %d "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( $q, [ self::STATUS_RAW, $id ] ) ); } }
| ver. 1.4 |
Github
|
.
| PHP 8.2.30 | Generation time: 0.09 |
proxy
|
phpinfo
|
Settings