ay_push( $new_results, array( 'name' => $key, 'shortname' => self::size_shortname( $key ), 'status' => is_array( $value ) ? 'CANNOT' : $value, 'required' => is_array( $value ) ? $value : null ) ); } return $new_results; } return $result; } function webp_retina_info( $id, $output_type = OBJECT ) { $result = array(); $meta = wp_get_attachment_metadata( $id ); if ( !$this->is_image_meta( $meta ) ) return $result; $original_width = $meta['width']; $original_height = $meta['height']; // Check if the full-size-retina version of the generation source exists. If it exists, use its dimensions. $uploads = wp_upload_dir(); if ( $retina = $this->get_retina( $uploads['basedir'] . '/' . $meta['file'] ) ) { $dim = getimagesize( $retina ); $original_width = $dim[0]; $original_height = $dim[1]; } $sizes = $this->get_image_sizes(); $originalfile = get_attached_file( $id ); $pathinfo_fullsize = pathinfo( $originalfile ); $basepath = $pathinfo_fullsize['dirname']; // Image Sizes if ( $sizes ) { foreach ( $sizes as $name => $attr ) { $validSize = !empty( $attr['width'] ) || !empty( $attr['height'] ); if ( !$validSize ) { $result[$name] = 'IGNORED'; continue; } // Check if the file related to this size is present $pathinfo = null; $webp_retina_file = null; if ( isset( $meta['sizes'][$name]['width'] ) && isset( $meta['sizes'][$name]['height']) && isset($meta['sizes'][$name]) && isset($meta['sizes'][$name]['file']) && file_exists( trailingslashit( $basepath ) . $meta['sizes'][$name]['file'] ) ) { $normal_file = trailingslashit( $basepath ) . $meta['sizes'][$name]['file']; $pathinfo = pathinfo( $normal_file ) ; $new_webp_ext = $pathinfo['extension'] === 'webp' ? '' : $this->webp_extension(); $webp_retina_file = trailingslashit( $pathinfo['dirname'] ) . $pathinfo['filename'] . $this->retina_extension() . $pathinfo['extension'] . $new_webp_ext; } // None of the file exist else { $result[$name] = 'MISSING'; continue; } // The WebP Retina file exists if ( $webp_retina_file && file_exists( $webp_retina_file ) ) { $result[$name] = 'EXISTS'; continue; } else if ( $webp_retina_file ) { // The size file exists $result[$name] = 'PENDING'; } // The retina file exists $required_width = $meta['sizes'][$name]['width'] * 2; $required_height = $meta['sizes'][$name]['height'] * 2; if ( !$this->are_dimensions_ok( $original_width, $original_height, $required_width, $required_height ) ) { $result[$name] = array( 'width' => $required_width, 'height' => $required_height ); } } } // Full-Size (if required in the settings) $fullsize_required = $this->get_option( "full_size" ) && class_exists( 'MeowPro_WR2X_Core' ); $retina_file = trailingslashit( $pathinfo_fullsize['dirname'] ) . $pathinfo_fullsize['filename'] . $this->retina_extension() . $pathinfo_fullsize['extension']; if ( $retina_file && file_exists( $retina_file ) ) $result['full-size'] = 'EXISTS'; else if ( $fullsize_required && $retina_file ) $result['full-size'] = array( 'width' => $original_width * 2, 'height' => $original_height * 2 ); if ( $output_type === ARRAY_A ) { $new_results = array(); foreach ( $result as $key => $value ) { array_push( $new_results, array( 'name' => $key, 'shortname' => self::size_shortname( $key ), 'status' => is_array( $value ) ? 'CANNOT' : $value, 'required' => is_array( $value ) ? $value : null ) ); } return $new_results; } return $result; } /** * * FILTERS * */ function validate_src( $src ) { if ( preg_match( "/^data:/i", $src ) ) return null; return $src; } /** * * LOAD SCRIPTS IF REQUIRED * */ function wp_enqueue_scripts () { // Picturefill Debug if ( $this->method == "Picturefill" && $this->is_debug() ) { $physical_file = trailingslashit( WR2X_PATH ) . 'app/debug.js'; $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : WR2X_VERSION; wp_enqueue_script( 'wr2x-debug-js', trailingslashit( WR2X_URL ) . 'app/debug.js', array(), $cache_buster, false ); } // No Picturefill Script if ( $this->method == "Picturefill" && !$this->get_option( "picturefill_noscript" ) ) { $physical_file = trailingslashit( WR2X_PATH ) . 'app/picturefill.min.js'; $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : WR2X_VERSION; wp_enqueue_script( 'wr2x-picturefill-js', trailingslashit( WR2X_URL ) . 'app/picturefill.min.js', array(), $cache_buster, false ); } // Lazysizes if ( $this->get_option( "picturefill_lazysizes" ) && class_exists( 'MeowPro_WR2X_Core' ) ) { $physical_file = trailingslashit( WR2X_PATH ) . 'app/lazysizes.min.js'; $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : WR2X_VERSION; wp_enqueue_script( 'wr2x-picturefill-js', trailingslashit( WR2X_URL ) . 'app/lazysizes.min.js', array(), $cache_buster, false ); } // Debug + HTML Rewrite = No JS! if ( $this->is_debug() && $this->method == "HTML Rewrite" ) { return; } // Debug mode, we force the devicePixelRatio to be Retina if ( $this->is_debug() ) { $physical_file = trailingslashit( WR2X_PATH ) . 'app/debug.js'; $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : WR2X_VERSION; wp_enqueue_script( 'wr2x-debug-js', trailingslashit( WR2X_URL ) . 'app/debug.js', array(), $cache_buster, false ); } // Retina-Images and HTML Rewrite both need the devicePixelRatio cookie on the server-side if ( $this->method == "Retina-Images" || $this->method == "HTML Rewrite" ) { $physical_file = trailingslashit( WR2X_PATH ) . 'app/retina-cookie.js'; $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : WR2X_VERSION; wp_enqueue_script( 'wr2x-debug-js', trailingslashit( WR2X_URL ) . 'app/retina-cookie.js', array(), $cache_buster, false ); } // Retina.js only needs itself if ( $this->method == "retina.js" ) { $physical_file = trailingslashit( WR2X_PATH ) . 'app/retina.min.js'; $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : WR2X_VERSION; wp_enqueue_script( 'wr2x-retinajs-js', trailingslashit( WR2X_URL ) . 'app/retina.min.js', array(), $cache_buster, false ); } } /** * * Roles & Access Rights * */ function can_access_settings() { return apply_filters( 'wr2x_allow_setup', current_user_can( 'manage_options' ) ); } function can_access_features() { return apply_filters( 'wr2x_allow_usage', current_user_can( 'administrator' ) ); } #region Options static function get_plugin_option_name() { return self::$plugin_option_name; } function get_option_name() { return $this->option_name; } function get_option( $option, $default = null ) { $options = $this->get_all_options(); return $options[$option] ?? $default; } function list_options() { return array( 'method' => 'none', 'webp_method' => 'none', 'retina_sizes' => [], 'disabled_sizes' => [], 'webp_sizes' => [], 'webp_retina_sizes' => [], 'picturefill_lazysizes' => false, 'big_image_size_threshold' => false, 'hide_retina_dashboard' => false, 'hide_retina_column' => true, 'hide_optimize' => true, 'auto_generate' => false, 'webp_auto_generate' => false, 'ignore_sizes' => [], 'picturefill_keep_src' => false, 'picturefill_css_background' => false, 'disable_responsive' => false, 'full_size' => false, 'webp_full_size' => false, 'quality' => 75, 'over_http_check' => false, 'easyio_domain' => '', 'cdn_domain' => '', 'easyio_lossless' => '', 'debug' => false, 'picturefill_noscript' => false, 'image_replace' => false, 'sizes' => [], 'module_retina_enabled' => true, 'module_optimize_enabled' => true, 'module_ui_enabled' => true, 'module_webp_enabled' => true, 'gif_thumbnails_disabled' => false, 'logs_path' => null, 'custom_image_sizes' => [], ); } function get_all_options() { //delete_option( $this->option_name ); $options = get_option( $this->option_name, null ); $options = $this->check_options( $options ); $needs_update = false; foreach ( $options as $option => $value ) { if ($option === 'retina_sizes' || $option === 'disabled_sizes' || $option === 'webp_sizes' || $option === 'webp_retina_sizes' ) { if ( !is_array( $value ) ) { $this->log( "⚠️ Option $option is not an array. Resetting it." ); if ( strpos( $value, ',' ) !== false ) { $this->log( "⚠️ Option $option is a string with commas. Splitting it into an array." ); $options[$option] = explode( ',', $value ); } else { $options[$option] = array( $value ); } $needs_update = true; continue; } $options[$option] = array_values( $value ); continue; } } $options['sizes'] = $this->get_image_sizes( ARRAY_A, $options ); if( $needs_update ) { $this->update_options( $options ); } return $options; } // Upgrade from the old way of storing options to the new way. function check_options( $options = [] ) { $plugin_options = $this->list_options(); $options = empty( $options ) ? [] : $options; $hasChanges = false; foreach ( $plugin_options as $option => $default ) { // The option already exists if ( array_key_exists( $option, $options ) ) { continue; }else{ error_log( '[PERFECT IMAGES] ⚙️ Option does not exist: ' . $option . '. Adding it.' ); } // The option does not exist, so we need to add it. // Let's use the old value if any, or the default value. $options[$option] = get_option( 'wr2x_' . $option, $default ); delete_option( 'wr2x_' . $option ); $hasChanges = true; } if ( empty( $options['sizes'] ) ) { $options['sizes'] = $this->get_image_sizes( ARRAY_A, $options ); $hasChanges = true; } if ( $hasChanges ) { update_option( $this->option_name , $options ); } return $options; } function update_options( $options ) { if ( !update_option( $this->option_name, $options, false ) ) { return false; } $options = $this->sanitize_options(); return $options; } // Validate and keep the options clean and logical. function sanitize_options() { $options = $this->get_all_options(); // Update member variables. $this->method = $options["method"]; $this->retina_sizes = $options['retina_sizes'] ?? array(); $this->disabled_sizes = $options['disabled_sizes'] ?? array(); $this->webp_sizes = $options['webp_sizes'] ?? array(); $this->webp_retina_sizes = $options['webp_retina_sizes'] ?? array(); $this->lazy = $options["picturefill_lazysizes"] && class_exists( 'MeowPro_WR2X_Core' ); $options['sizes'] = $this->get_image_sizes( ARRAY_A, $options ); update_option( $this->option_name, $options, false ); return $options; } // #endregion // Custom Image Sizes function get_custom_image_size_changes ( $old_options, $new_options ) { $get_diff_one = function ( $name, $options ) { return array_slice( array_filter( $options, function( $option ) use ( $name ) { return $option['name'] === $name; }), 0, 1 )[0]; }; // Add or delete if ( count( $new_options ) !== count( $old_options ) ) { $old_option_names = array_column( $old_options, 'name' ); $new_option_names = array_column( $new_options, 'name' ); return ( count( $new_option_names ) > count( $old_option_names ) ) ? [ 'type' => 'add', 'value' => $get_diff_one( array_slice( array_diff( $new_option_names, $old_option_names ), 0, 1 )[0], $new_options ) ] : [ 'type' => 'delete', 'value' => $get_diff_one( array_slice( array_diff( $old_option_names, $new_option_names ), 0, 1 )[0], $old_options ) ]; } // Update or no change $diff_keys = ['width', 'height', 'crop']; foreach ( $diff_keys as $diff_key ) { $new_values = array_column( $new_options, $diff_key, 'name' ); $old_values = array_column( $old_options, $diff_key, 'name' ); $diff_values = array_diff( $new_values, $old_values ); if ( !empty( $diff_values ) ) { return [ 'type' => 'update', 'value' => $get_diff_one( array_keys( $diff_values )[0], $new_options ) ]; } } return null; } function register_custom_image_size ( $type, $name, $width, $height, $crop ) { if ( $type === 'delete' ) { if ( !remove_image_size( $name ) ) { throw new Exception( "Could not remove image size '{$name}'." ); } return; } if ( in_array( $crop, ['yes', 'no'] ) ) { $crop = $crop === 'yes'; } else { $crop = explode( '-', $crop ); } add_image_size( $name, $width, $height, $crop ); } private function random_ascii_chars( $length = 8 ) { $characters = array_merge( range( 'A', 'Z' ), range( 'a', 'z' ), range( '0', '9' ) ); $characters_length = count( $characters ); $random_string = ''; for ($i = 0; $i < $length; $i++) { $random_string .= $characters[rand(0, $characters_length - 1)]; } return $random_string; } } // Used by WP Rocket (and maybe by other plugins) function wr2x_is_registered() { global $wr2x_core; return $wr2x_core->admin->is_registered(); } ?>