This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,76 @@
<?php
namespace Smush\Core\Smush;
use Smush\Core\Controller;
use Smush\Core\Media\Media_Item;
use Smush\Core\Stats\Global_Stats;
use Smush\Core\Stats\Media_Item_Optimization_Global_Stats_Persistable;
class Smush_Controller extends Controller {
const GLOBAL_STATS_OPTION_ID = 'wp-smush-optimization-global-stats';
const SMUSH_OPTIMIZATION_ORDER = 30;
private $global_stats;
public function __construct() {
$this->global_stats = Global_Stats::get();
$this->register_filter( 'wp_smush_optimizations', array(
$this,
'add_smush_optimization',
), self::SMUSH_OPTIMIZATION_ORDER, 2 );
$this->register_filter( 'wp_smush_global_optimization_stats', array( $this, 'add_png2jpg_global_stats' ) );
$this->register_filter( 'wp_smush_optimization_global_stats_instance', array(
$this,
'create_global_stats_instance',
), 10, 2 );
$this->register_action( 'wp_smush_settings_updated', array(
$this,
'maybe_mark_global_stats_as_outdated',
), 10, 2 );
}
/**
* @param $optimizations array
* @param $media_item Media_Item
*
* @return array
*/
public function add_smush_optimization( $optimizations, $media_item ) {
$optimization = new Smush_Optimization( $media_item );
$optimizations[ $optimization->get_key() ] = $optimization;
return $optimizations;
}
public function add_png2jpg_global_stats( $stats ) {
$stats[ Smush_Optimization::KEY ] = new Media_Item_Optimization_Global_Stats_Persistable(
self::GLOBAL_STATS_OPTION_ID,
new Smush_Optimization_Global_Stats()
);
return $stats;
}
public function create_global_stats_instance( $original, $key ) {
if ( $key === Smush_Optimization::KEY ) {
return new Smush_Optimization_Global_Stats();
}
return $original;
}
public function maybe_mark_global_stats_as_outdated( $old_settings, $settings ) {
$old_lossy_status = ! empty( $old_settings['lossy'] ) ? (int) $old_settings['lossy'] : 0;
$new_lossy_status = ! empty( $settings['lossy'] ) ? (int) $settings['lossy'] : 0;
$lossy_status_changed = $old_lossy_status !== $new_lossy_status;
$old_exif_status = ! empty( $old_settings['strip_exif'] );
$new_exif_status = ! empty( $settings['strip_exif'] );
$exif_status_changed = $old_exif_status !== $new_exif_status;
if ( $lossy_status_changed || $exif_status_changed ) {
$this->global_stats->mark_as_outdated();
}
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Smush\Core\Smush;
use Smush\Core\Media\Media_Item_Stats;
class Smush_Media_Item_Stats extends Media_Item_Stats {
private $lossy = false;
/**
* @return mixed
*/
public function is_lossy() {
return $this->lossy;
}
/**
* @param mixed $lossy
*
* @return Smush_Media_Item_Stats
*/
public function set_lossy( $lossy ) {
$this->lossy = $lossy;
return $this;
}
public function to_array() {
$array = parent::to_array();
$array['lossy'] = $this->is_lossy();
return $array;
}
public function from_array( $array ) {
parent::from_array( $array );
$this->set_lossy( ! empty( $array['lossy'] ) );
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace Smush\Core\Smush;
use Smush\Core\Media\Media_Item_Optimization_Global_Stats;
class Smush_Optimization_Global_Stats extends Media_Item_Optimization_Global_Stats {
private $lossy_count = 0;
public function from_array( $array ) {
parent::from_array( $array );
$this->set_lossy_count( (int) $this->get_array_value( $array, 'lossy_count' ) );
}
public function to_array() {
$array = parent::to_array();
$array['lossy_count'] = $this->get_lossy_count();
return $array;
}
/**
* @param $attachment_id int
* @param $item_stats Smush_Media_Item_Stats
*
* @return boolean
*/
public function add_item_stats( $attachment_id, $item_stats ) {
$added = parent::add_item_stats( $attachment_id, $item_stats );
if ( $added && $item_stats->is_lossy() ) {
$this->set_lossy_count( $this->get_lossy_count() + 1 );
}
return $added;
}
/**
* @param $attachment_id int
* @param $item_stats Smush_Media_Item_Stats
*
* @return boolean
*/
public function subtract_item_stats( $attachment_id, $item_stats ) {
$subtracted = parent::subtract_item_stats( $attachment_id, $item_stats );
if ( $subtracted && $item_stats->is_lossy() ) {
// Assuming that we added to the lossy count
$this->set_lossy_count( max( $this->get_lossy_count() - 1, 0 ) );
}
return $subtracted;
}
/**
* @param $addend Smush_Optimization_Global_Stats
*
* @return void
*/
public function add( $addend ) {
parent::add( $addend );
$this->set_lossy_count( $this->get_lossy_count() + $addend->get_lossy_count() );
}
/**
* @param $subtrahend Smush_Optimization_Global_Stats
*
* @return void
*/
public function subtract( $subtrahend ) {
parent::subtract( $subtrahend );
$this->set_lossy_count( max( $this->get_lossy_count() - $subtrahend->get_lossy_count(), 0 ) );
}
/**
* @return int
*/
public function get_lossy_count() {
return $this->lossy_count;
}
/**
* @param int $lossy_count
*
* @return Smush_Optimization_Global_Stats
*/
public function set_lossy_count( $lossy_count ) {
$this->lossy_count = $lossy_count;
return $this;
}
}

View File

@ -0,0 +1,452 @@
<?php
namespace Smush\Core\Smush;
use Smush\Core\Media\Media_Item;
use Smush\Core\Media\Media_Item_Optimization;
use Smush\Core\Media\Media_Item_Size;
use Smush\Core\Media\Media_Item_Stats;
use Smush\Core\Settings;
use WP_Error;
/**
* Smushes a media item and updates the stats.
*/
class Smush_Optimization extends Media_Item_Optimization {
const KEY = 'smush_optimization';
const SMUSH_META_KEY = 'wp-smpro-smush-data';
const LOSSY_META_KEY = 'wp-smush-lossy';
/**
* @var Media_Item_Stats
*/
private $stats;
/**
* @var Media_Item_Stats[]
*/
private $size_stats = array();
/**
* @var Media_Item
*/
private $media_item;
/**
* @var array
*/
private $smush_meta;
/**
* @var int
*/
private $keep_exif;
/**
* @var bool
*/
private $lossy_level;
/**
* @var string
*/
private $api_version;
/**
* @var Settings
*/
private $settings;
private $reset_properties = array(
'stats',
'size_stats',
'smush_meta',
'keep_exif',
'lossy_level',
'api_version',
);
/**
* @var Smusher
*/
private $smusher;
public function __construct( $media_item ) {
$this->media_item = $media_item;
$this->settings = Settings::get_instance();
$this->smusher = new Smusher();
}
public function get_key() {
return self::KEY;
}
public function get_stats() {
if ( is_null( $this->stats ) ) {
$this->stats = $this->prepare_stats();
}
return $this->stats;
}
public function set_stats( $stats ) {
$this->stats = $stats;
}
private function get_meta_sizes() {
$smush_meta = $this->get_smush_meta();
return empty( $smush_meta['sizes'] )
? array()
: $smush_meta['sizes'];
}
private function get_size_meta( $size_key ) {
$sizes = $this->get_meta_sizes();
$size = empty( $sizes[ $size_key ] )
? array()
: (array) $sizes[ $size_key ];
return empty( $size ) ? array() : $size;
}
private function size_meta_exists( $size_key ) {
return ! empty( $this->get_size_meta( $size_key ) );
}
public function get_size_stats( $size_key ) {
if ( empty( $this->size_stats[ $size_key ] ) ) {
$this->size_stats[ $size_key ] = $this->prepare_size_stats( $size_key );
}
return $this->size_stats[ $size_key ];
}
private function prepare_size_stats( $size_key ) {
$stats = new Media_Item_Stats();
$stats->from_array( $this->get_size_meta( $size_key ) );
return $stats;
}
public function save() {
$meta = $this->make_smush_meta();
if ( ! empty( $meta ) ) {
update_post_meta( $this->media_item->get_id(), self::SMUSH_META_KEY, $meta );
// TODO: the separate lossy meta is only necessary for the backup global stats, if enough time has passed and enough people have moved to the new stats then we can remove it
if ( $this->get_lossy_level() ) {
update_post_meta( $this->media_item->get_id(), self::LOSSY_META_KEY, 1 );
} else {
delete_post_meta( $this->media_item->get_id(), self::LOSSY_META_KEY );
}
$this->reset();
}
}
public function is_optimized() {
return ! $this->get_stats()->is_empty();
}
public function should_optimize() {
if ( $this->media_item->is_skipped() || $this->media_item->has_errors() ) {
return false;
}
return ! empty( $this->get_sizes_to_smush() );
}
public function should_reoptimize() {
return $this->should_resmush();
}
public function optimize() {
if ( ! $this->should_optimize() ) {
return false;
}
$media_item = $this->media_item;
$file_paths = array_map( function ( $size ) {
return $size->get_file_path();
}, $this->get_sizes_to_smush() );
$responses = $this->smusher->smush( $file_paths );
$success_responses = array_filter( $responses );
if ( count( $success_responses ) !== count( $responses ) ) {
return false;
}
$media_item_stats = $this->create_media_item_stats_instance();
foreach ( $responses as $size_key => $data ) {
$this->update_from_response( $size_key, $data, $media_item_stats );
}
$this->set_stats( $media_item_stats );
if ( $media_item_stats->get_bytes() >= 0 ) {
do_action( 'wp_smush_image_optimised',
$this->media_item->get_id(),
$this->make_smush_meta(),
$this->media_item->get_wp_metadata()
);
}
// Update media item
$media_item->save();
// Update smush meta
$this->save();
return true;
}
private function prepare_stats() {
$smush_meta = $this->get_smush_meta();
$stats = $this->create_media_item_stats_instance();
$stats_data = empty( $smush_meta['stats'] )
? array()
: $smush_meta['stats'];
$stats->from_array( $stats_data );
$stats->set_lossy( (bool) $this->get_lossy_level() );
return $stats;
}
private function get_smush_meta() {
if ( is_null( $this->smush_meta ) ) {
$this->smush_meta = $this->fetch_smush_meta();
}
return $this->smush_meta;
}
private function fetch_smush_meta() {
$post_meta = get_post_meta( $this->media_item->get_id(), self::SMUSH_META_KEY, true );
return empty( $post_meta ) || ! is_array( $post_meta )
? array()
: $post_meta;
}
public function keep_exif() {
if ( is_null( $this->keep_exif ) ) {
$this->keep_exif = $this->prepare_keep_exif();
}
return $this->keep_exif;
}
private function prepare_keep_exif() {
$smush_meta = $this->get_smush_meta();
return isset( $smush_meta['stats']['keep_exif'] )
? (int) $smush_meta['stats']['keep_exif']
: 0;
}
public function set_keep_exif( $keep_exif ) {
$this->keep_exif = (int) $keep_exif;
}
public function get_lossy_level() {
if ( is_null( $this->lossy_level ) ) {
$this->lossy_level = $this->prepare_lossy_level();
}
return $this->lossy_level;
}
private function prepare_lossy_level() {
$smush_meta = $this->get_smush_meta();
return empty( $smush_meta['stats']['lossy'] )
? 0
: (int) $smush_meta['stats']['lossy'];
}
public function set_lossy_level( $lossy ) {
$this->lossy_level = (int) $lossy;
}
public function get_api_version() {
if ( is_null( $this->api_version ) ) {
$this->api_version = $this->prepare_api_version();
}
return $this->api_version;
}
private function prepare_api_version() {
$smush_meta = $this->get_smush_meta();
return empty( $smush_meta['stats']['api_version'] )
? ''
: $smush_meta['stats']['api_version'];
}
public function set_api_version( $api_version ) {
$this->api_version = $api_version;
}
private function make_smush_meta() {
$smush_meta = $this->get_smush_meta();
// Stats
$media_item_stats = $this->get_stats();
if ( ! $media_item_stats->is_empty() ) {
$smush_meta['stats'] = array_merge(
empty( $smush_meta['stats'] ) ? array() : $smush_meta['stats'],
$media_item_stats->to_array(),
array(
'keep_exif' => $this->keep_exif(),
'lossy' => $this->get_lossy_level(),
'api_version' => $this->get_api_version(),
)
);
}
// Sizes
foreach ( $this->size_stats as $size_key => $size_stats ) {
if ( ! $size_stats->is_empty() ) {
$smush_meta['sizes'][ $size_key ] = (object) $size_stats->to_array();
}
}
return $smush_meta;
}
private function should_resmush() {
if ( ! $this->should_optimize() ) {
return false;
}
if ( $this->is_next_level_available() ) {
return true;
}
if ( $this->settings->get( 'strip_exif' ) && $this->keep_exif() ) {
return true;
}
foreach ( $this->get_sizes_to_smush() as $size_key => $size ) {
$is_smushed = $this->size_meta_exists( $size_key ) || $this->is_file_smushed( $size->get_file_path() );
if ( ! $is_smushed ) {
return true;
}
}
return false;
}
public function is_next_level_available() {
$current_lossy_level = $this->get_lossy_level();
$required_lossy_level = $this->settings->get_lossy_level_setting();
return $current_lossy_level < $required_lossy_level;
}
private function is_file_smushed( $file_path ) {
foreach ( $this->media_item->get_sizes() as $size_key => $size ) {
if ( $size->get_file_path() === $file_path && $this->size_meta_exists( $size_key ) ) {
return true;
}
}
return false;
}
/**
* @param $size_key
* @param object $data
* @param $media_item_stats Smush_Media_Item_Stats
*/
private function update_from_response( $size_key, $data, $media_item_stats ) {
$size_stats = $this->get_size_stats( $size_key );
$this->set_api_version( $data->api_version );
$this->set_lossy_level( (int) $data->lossy );
$this->set_keep_exif( empty( $data->keep_exif ) ? 0 : $data->keep_exif );
// Update the size stats
$size_stats->from_array( $this->size_stats_from_response( $size_stats, $data ) );
// Add the size stats to the media item stats
$media_item_stats->add( $size_stats );
// TODO: maybe remove the lossy count from smush stats
$media_item_stats->set_lossy( (bool) $this->get_lossy_level() );
}
/**
* @param $existing_stats Media_Item_Stats
* @param $data
*
* @return array
*/
private function size_stats_from_response( $existing_stats, $data ) {
$size_before = max( $existing_stats->get_size_before(), $data->before_size ); // We want to use the oldest before size
return array(
'size_before' => $size_before,
'size_after' => $data->after_size,
'time' => $data->time,
);
}
/**
* @return WP_Error
*/
public function get_errors() {
return $this->get_smusher()->get_errors();
}
protected function reset() {
foreach ( $this->reset_properties as $property ) {
$this->$property = null;
}
}
public function delete_data() {
delete_post_meta( $this->media_item->get_id(), self::SMUSH_META_KEY );
$this->reset();
}
/**
* @param $size Media_Item_Size
*
* @return bool
*/
public function should_optimize_size( $size ) {
if ( ! $this->should_optimize() ) {
return false;
}
return array_key_exists(
$size->get_key(),
$this->get_sizes_to_smush()
);
}
/**
* @return Media_Item_Size[]
*/
private function get_sizes_to_smush() {
return $this->media_item->get_smushable_sizes();
}
/**
* @return Smusher
*/
public function get_smusher() {
return $this->smusher;
}
/**
* @return Smush_Media_Item_Stats
*/
private function create_media_item_stats_instance() {
return new Smush_Media_Item_Stats();
}
public function get_optimized_sizes_count() {
$count = 0;
$sizes = $this->get_sizes_to_smush();
foreach ( $sizes as $size_key => $size ) {
$size_stats = $this->get_size_stats( $size_key );
if ( $size_stats && ! $size_stats->is_empty() ) {
$count ++;
}
}
return $count;
}
}

View File

@ -0,0 +1,584 @@
<?php
namespace Smush\Core\Smush;
use Smush\Core\Api\Backoff;
use Smush\Core\Api\Request_Multiple;
use Smush\Core\File_System;
use Smush\Core\Helper;
use Smush\Core\Settings;
use Smush\Core\Upload_Dir;
use WP_Error;
use WP_Smush;
/**
* Takes raw image file paths and processes them through the Smush API.
*/
class Smusher {
const ERROR_SSL_CERT = 'ssl_cert_error';
/**
* @var Settings
*/
private $settings;
/**
* @var Request_Multiple
*/
private $request_multiple;
/**
* @var Backoff
*/
private $backoff;
/**
* @var \WDEV_Logger|null
*/
private $logger;
/**
* @var int
*/
private $retry_attempts;
/**
* @var int
*/
private $retry_wait;
/**
* @var int
*/
private $timeout;
/**
* @var string
*/
private $user_agent;
/**
* @var int
*/
private $connect_timeout;
/**
* @var boolean
*/
private $smush_parallel;
/**
* @var WP_Error
*/
private $errors;
/**
* @var File_System
*/
private $fs;
/**
* @var Upload_Dir
*/
private $upload_dir;
public function __construct() {
$this->retry_attempts = WP_SMUSH_RETRY_ATTEMPTS;
$this->retry_wait = WP_SMUSH_RETRY_WAIT;
$this->user_agent = WP_SMUSH_UA;
$this->smush_parallel = WP_SMUSH_PARALLEL;
$this->timeout = WP_SMUSH_TIMEOUT;
$this->connect_timeout = 5;
$this->settings = Settings::get_instance();
$this->logger = Helper::logger();
$this->request_multiple = new Request_Multiple();
$this->backoff = new Backoff();
$this->errors = new WP_Error();
$this->fs = new File_System();
$this->upload_dir = new Upload_Dir();
}
/**
* @param $file_paths string[]
*
* @return boolean[]|object[]
*/
public function smush( $file_paths ) {
$this->set_errors( new WP_Error() );
if ( $this->parallel_available() ) {
return $this->smush_parallel( $file_paths );
} else {
return $this->smush_sequential( $file_paths );
}
}
/**
* @param $file_paths string[]
*
* @return boolean[]|object[]
*/
private function smush_parallel( $file_paths ) {
$retry = array();
$requests = array();
foreach ( $file_paths as $size_key => $size_file_path ) {
$requests[ $size_key ] = $this->get_parallel_request_args( $size_file_path );
}
// Send off the valid paths to the API
$responses = array();
$this->request_multiple->do_requests( $requests, array(
'timeout' => $this->timeout,
'connect_timeout' => $this->connect_timeout,
'user-agent' => $this->user_agent,
'complete' => function ( $response, $response_size_key ) use ( &$requests, &$responses, &$retry, $file_paths ) {
// Free up memory
$requests[ $response_size_key ] = null;
$size_file_path = $file_paths[ $response_size_key ];
if ( $this->should_retry_smush( $response ) ) {
$retry[ $response_size_key ] = $size_file_path;
} else {
$responses[ $response_size_key ] = $this->handle_response( $response, $response_size_key, $size_file_path );
}
},
) );
// Retry failures with exponential backoff
foreach ( $retry as $retry_size_key => $retry_size_file ) {
$responses[ $retry_size_key ] = $this->smush_file( $retry_size_file, $retry_size_key );
}
return $responses;
}
/**
* @param $file_paths string[]
*
* @return boolean[]|object[]
*/
private function smush_sequential( $file_paths ) {
$responses = array();
foreach ( $file_paths as $size_key => $size_file_path ) {
$responses[ $size_key ] = $this->smush_file( $size_file_path, $size_key );
}
return $responses;
}
/**
* @param $file_path string
* @param $size_key string
*
* @return bool|object
*/
public function smush_file( $file_path, $size_key = '' ) {
$response = $this->backoff->set_wait( $this->retry_wait )
->set_max_attempts( $this->retry_attempts )
->enable_jitter()
->set_decider( array( $this, 'should_retry_smush' ) )
->run( function () use ( $file_path ) {
return $this->make_post_request( $file_path );
} );
return $this->handle_response( $response, $size_key, $file_path );
}
private function make_post_request( $file_path ) {
// Temporary increase the limit.
wp_raise_memory_limit( 'image' );
return wp_remote_post(
$this->get_api_url(),
$this->get_api_request_args( $file_path )
);
}
private function get_api_request_args( $file_path ) {
return array(
'headers' => $this->get_api_request_headers( $file_path ),
'body' => $this->fs->file_get_contents( $file_path ),
'timeout' => $this->timeout,
'user-agent' => $this->user_agent,
);
}
/**
* @param $response
* @param $size_key string
* @param $file_path string
*
* @return bool|object
*/
private function handle_response( $response, $size_key, $file_path ) {
$data = $this->parse_response( $response, $size_key, $file_path );
if ( ! $data ) {
if ( $this->has_error( self::ERROR_SSL_CERT ) ) {
// Switch to http protocol.
$this->settings->set_setting( 'wp-smush-use_http', 1 );
}
return false;
}
if ( $data->bytes_saved > 0 ) {
$optimized_image_saved = $this->save_smushed_image_file( $file_path, $data->image );
if ( ! $optimized_image_saved ) {
$this->add_error(
$size_key,
'image_not_saved',
/* translators: %s: File path. */
sprintf( __( 'Smush was successful but we were unable to save the file due to a file system error: [%s].', 'wp-smushit' ), $this->upload_dir->get_human_readable_path( $file_path ) )
);
return false;
}
}
// No need to pass image data any further
$data->image = null;
$data->image_md5 = null;
// Check for API message and store in db.
if ( ! empty( $data->api_message ) ) {
$this->add_api_message( (array) $data->api_message );
}
return $data;
}
protected function save_smushed_image_file( $file_path, $image ) {
$pre = apply_filters( 'wp_smush_pre_image_write', false, $file_path, $image );
if ( $pre !== false ) {
$this->logger->notice( 'Another plugin/theme short circuited the image write operation using the wp_smush_pre_image_write filter.' );
// Assume that the plugin/theme responsible took care of it
return true;
}
// Backup the old permissions
$permissions = $this->get_file_permissions( $file_path );
// Save the new file
$success = $this->put_smushed_image_file( $file_path, $image );
// Restore the old permissions
// TODO: this is the only chmod but restoring in the comment suggests that we changed the permissions before, what are we doing?
chmod( $file_path, $permissions );
return $success;
}
private function put_smushed_image_file( $file_path, $image ) {
$temp_file = $file_path . '.tmp';
$success = $this->put_image_using_temp_file( $file_path, $image, $temp_file );
// Clean up
if ( $this->fs->file_exists( $temp_file ) ) {
$this->fs->unlink( $temp_file );
}
return $success;
}
private function put_image_using_temp_file( $file_path, $image, $temp_file ) {
$file_written = file_put_contents( $temp_file, $image );
if ( ! $file_written ) {
return false;
}
$renamed = rename( $temp_file, $file_path );
if ( $renamed ) {
return true;
}
$copied = $this->fs->copy( $temp_file, $file_path );
if ( $copied ) {
return true;
}
return false;
}
private function get_file_permissions( $file_path ) {
clearstatcache();
$perms = fileperms( $file_path ) & 0777;
// Some servers are having issue with file permission, this should fix it.
if ( empty( $perms ) ) {
// Source: WordPress Core.
$stat = stat( dirname( $file_path ) );
$perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
}
return $perms;
}
private function add_api_message( $api_message = array() ) {
if ( empty( $api_message ) || ! count( $api_message ) || empty( $api_message['timestamp'] ) || empty( $api_message['message'] ) ) {
return;
}
$o_api_message = get_site_option( 'wp-smush-api_message', array() );
if ( array_key_exists( $api_message['timestamp'], $o_api_message ) ) {
return;
}
$message = array();
$message[ $api_message['timestamp'] ] = array(
'message' => sanitize_text_field( $api_message['message'] ),
'type' => sanitize_text_field( $api_message['type'] ),
'status' => 'show',
);
update_site_option( 'wp-smush-api_message', $message );
}
/**
* @param $response
* @param $size_key string
* @param $file_path string
*
* @return object|false
*/
private function parse_response( $response, $size_key, $file_path ) {
if ( is_wp_error( $response ) ) {
$error = $response->get_error_message();
if ( strpos( $error, 'SSL CA cert' ) !== false ) {
$this->add_error( $size_key, self::ERROR_SSL_CERT, $error );
return false;
} else if ( strpos( $error, 'timed out' ) !== false ) {
$this->add_error(
$size_key,
'time_out',
esc_html__( "Skipped due to a timeout error. You can increase the request timeout to make sure Smush has enough time to process larger files. define('WP_SMUSH_TIMEOUT', 150);", 'wp-smushit' )
);
return false;
} else {
$this->add_error(
$size_key,
'error_posting_to_api',
/* translators: %s: Error message. */
sprintf( __( 'Error posting to API: %s', 'wp-smushit' ), $error )
);
return false;
}
}
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
$error = sprintf(
/* translators: 1: Error code, 2: Error message. */
__( 'Error posting to API: %1$s %2$s', 'wp-smushit' ),
wp_remote_retrieve_response_code( $response ),
wp_remote_retrieve_response_message( $response )
);
$this->add_error( $size_key, 'non_200_response', $error );
return false;
}
$json = json_decode( wp_remote_retrieve_body( $response ) );
if ( empty( $json->success ) ) {
$error = ! empty( $json->data )
? $json->data
: __( "Image couldn't be smushed", 'wp-smushit' );
$this->add_error( $size_key, 'unsuccessful_smush', $error );
return false;
}
if (
empty( $json->data )
|| empty( $json->data->before_size )
|| empty( $json->data->after_size )
) {
$this->add_error( $size_key, 'no_data', __( 'Unknown API error', 'wp-smushit' ) );
return false;
}
$data = $json->data;
$data->bytes_saved = isset( $data->bytes_saved ) ? (int) $data->bytes_saved : 0;
$optimized_image_larger = $data->after_size > $data->before_size;
if ( $optimized_image_larger ) {
$this->add_error(
$size_key,
'optimized_image_larger',
/* translators: 1: File path, 2: Savings bytes. */
sprintf( 'The smushed image is larger than the original image [%s] (bytes saved %d), keep original image.', $this->upload_dir->get_human_readable_path( $file_path ), $data->bytes_saved )
);
return false;
}
$image = empty( $data->image ) ? '' : $data->image;
if ( $data->bytes_saved > 0 ) {
// Because of the API response structure, the following should only be done when there are some bytes_saved.
if ( $data->image_md5 !== md5( $image ) ) {
$error = __( 'Smush data corrupted, try again.', 'wp-smushit' );
$this->add_error( $size_key, 'data_corrupted', $error );
return false;
}
if ( ! empty( $image ) ) {
$data->image = base64_decode( $data->image );
}
}
return $data;
}
public function should_retry_smush( $response ) {
return $this->retry_attempts > 0 && (
is_wp_error( $response )
|| 200 !== wp_remote_retrieve_response_code( $response )
);
}
private function get_parallel_request_args( $file_path ) {
return array(
'url' => $this->get_api_url(),
'headers' => $this->get_api_request_headers( $file_path ),
'data' => $this->fs->file_get_contents( $file_path ),
'type' => 'POST',
);
}
/**
* @return string
*/
private function get_api_url() {
return defined( 'WP_SMUSH_API_HTTP' ) ? WP_SMUSH_API_HTTP : WP_SMUSH_API;
}
/**
* @return string[]
*/
protected function get_api_request_headers( $file_path ) {
$headers = array(
'accept' => 'application/json', // The API returns JSON.
'content-type' => 'application/binary', // Set content type to binary.
'exif' => $this->settings->get( 'strip_exif' ) ? 'false' : 'true',
);
$headers['lossy'] = $this->settings->get_lossy_level_setting();
// Check if premium member, add API key.
$api_key = Helper::get_wpmudev_apikey();
if ( ! empty( $api_key ) && WP_Smush::is_pro() ) {
$headers['apikey'] = $api_key;
$is_large_file = $this->is_large_file( $file_path );
if ( $is_large_file ) {
$headers['islarge'] = 1;
}
}
return $headers;
}
private function is_large_file( $file_path ) {
$file_size = file_exists( $file_path ) ? filesize( $file_path ) : 0;
$cut_off = $this->settings->get_large_file_cutoff();
return $file_size > $cut_off;
}
/**
* @return bool
*/
public function parallel_available() {
if ( ! $this->smush_parallel ) {
return false;
}
return $this->curl_multi_exec_available();
}
/**
* @return bool
*/
public function curl_multi_exec_available() {
if ( ! function_exists( 'curl_multi_exec' ) ) {
return false;
}
$disabled_functions = explode( ',', ini_get( 'disable_functions' ) );
if ( in_array( 'curl_multi_exec', $disabled_functions ) ) {
return false;
}
return true;
}
/**
* @param int $retry_attempts
*
* @return Smusher
*/
public function set_retry_attempts( $retry_attempts ) {
$this->retry_attempts = $retry_attempts;
return $this;
}
/**
* @param int $timeout
*/
public function set_timeout( $timeout ) {
$this->timeout = $timeout;
}
/**
* @param bool $smush_parallel
*
* @return Smusher
*/
public function set_smush_parallel( $smush_parallel ) {
$this->smush_parallel = $smush_parallel;
return $this;
}
/**
* @param Request_Multiple $request_multiple
*
* @return Smusher
*/
public function set_request_multiple( $request_multiple ) {
$this->request_multiple = $request_multiple;
return $this;
}
public function get_errors() {
return $this->errors;
}
/**
* @param $errors WP_Error
*
* @return void
*/
private function set_errors( $errors ) {
$this->errors = $errors;
}
/**
* @param $size_key string
* @param $code string
* @param $message string
*
* @return void
*/
private function add_error( $size_key, $code, $message ) {
// Log the error
$this->logger->error( "[$size_key] $message" );
// Add the error
$this->errors->add( $code, "[$size_key] $message" );
}
/**
* @param $code string
*
* @return bool
*/
private function has_error( $code ) {
return ! empty( $this->errors->get_error_message( $code ) );
}
}