: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Class that is responsible for all stats calculations.
use Smush\Core\Media\Media_Item;
use Smush\Core\Media\Media_Item_Query;
use Smush\Core\Png2Jpg\Png2Jpg_Optimization;
use Smush\Core\Resize\Resize_Optimization;
use Smush\Core\Smush\Smush_Optimization;
use Smush\Core\Smush\Smush_Optimization_Global_Stats;
use Smush\Core\Stats\Global_Stats;
if ( ! defined( 'WPINC' ) ) {
* Stores the stats for all the images.
* Compressed attachments from selected directories.
* Set a limit of MySQL query. Default: 3000.
* Set a limit to max number of rows in MySQL query. Default: 5000.
* @var array $attachments
public $attachments = array();
* Image ids that needs to be resmushed.
* @var array $resmush_ids
public $resmush_ids = array();
* Percentage of the smushed images.
public $percent_optimized;
* Class name of grade type.
* Protected init class, used in child methods instead of constructor.
protected function init() {}
public function __construct() {
$this->query_limit = apply_filters( 'wp_smush_query_limit', 3000 );
$this->max_rows = apply_filters( 'wp_smush_max_rows', 5000 );
// Recalculate resize savings.
'wp_smush_image_resized',
return $this->get_savings( 'resize' );
// Update Conversion savings.
'wp_smush_png_jpg_converted',
return $this->get_savings( 'pngjpg' );
// Update the media_attachments list.
add_action( 'add_attachment', array( $this, 'add_to_media_attachments_list' ) );
add_action( 'delete_attachment', array( $this, 'update_lists' ), 12 );
* Runs the expensive queries to get our global smush stats
* @param bool $force_update Whether to force update the global stats or not.
public function setup_global_stats( $force_update = false ) {
if ( ! $this->mod->dir ) {
$this->mod->dir = new Modules\Dir();
// Set directory smush status.
$this->dir_stats = Modules\Dir::should_continue() ? $this->mod->dir->total_stats() : array();
// Set Attachment IDs, and total count.
$this->attachments = $this->get_media_attachments();
$this->total_count = ! empty( $this->attachments ) && is_array( $this->attachments ) ? count( $this->attachments ) : 0;
$this->stats = $this->global_stats( $force_update );
if ( empty( $this->smushed_attachments ) ) {
// Get smushed attachments.
$this->smushed_attachments = $this->get_smushed_attachments( $force_update );
// Get super smushed images count.
if ( ! $this->super_smushed ) {
$this->super_smushed = count( $this->get_super_smushed_attachments() );
// Get skipped attachments.
$this->skipped_attachments = $this->skipped_count( $force_update );
$this->skipped_count = count( $this->skipped_attachments );
$this->smushed_count = ! empty( $this->smushed_attachments ) ? count( $this->smushed_attachments ) : 0;
$this->remaining_count = $this->remaining_count();
list( $percent_optimized, $percent_metric, $grade ) = $this->get_grade_data(
$this->percent_grade = $grade;
$this->percent_metric = $percent_metric;
$this->percent_optimized = $percent_optimized;
* Get the savings from image resizing or PNG -> JPG conversion savings.
* @param string $type Savings type. Accepts: resize, pngjpg.
* @param bool $force_update Force update to re-calculate all stats. Default: false.
* @param bool $format Format the bytes in readable format. Default: false.
* @param bool $return_count Return the resized image count. Default: false.
public function get_savings( $type, $force_update = true, $format = false, $return_count = false ) {
$key = 'wp-smush-' . $type . '_savings';
$key_count = 'wp-smush-resize_count';
$savings = wp_cache_get( $key, 'wp-smush' );
if ( ! $return_count && $savings ) {
$count = wp_cache_get( $key_count, 'wp-smush' );
if ( $return_count && false !== $count ) {
// If savings or resize image count is not stored in db, recalculate.
$query_data = $wpdb->get_results(
"SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key=%s LIMIT %d, %d",
// No results - break out of loop.
if ( empty( $query_data ) ) {
foreach ( $query_data as $data ) {
if ( ! empty( $this->resmush_ids ) && in_array( $data->post_id, $this->resmush_ids, true ) ) {
$meta = maybe_unserialize( $data->meta_value );
// Resize mete already contains all the stats.
if ( ! empty( $meta ) && ! empty( $meta['bytes'] ) ) {
$savings['resize']['bytes'] += $meta['bytes'];
$savings['resize']['size_before'] += $meta['size_before'];
$savings['resize']['size_after'] += $meta['size_after'];
// PNG - JPG conversion meta contains stats by attachment size.
if ( is_array( $meta ) ) {
foreach ( $meta as $size ) {
$savings['pngjpg']['bytes'] += isset( $size['bytes'] ) ? $size['bytes'] : 0;
$savings['pngjpg']['size_before'] += isset( $size['size_before'] ) ? $size['size_before'] : 0;
$savings['pngjpg']['size_after'] += isset( $size['size_after'] ) ? $size['size_after'] : 0;
$offset += $this->query_limit;
// Compare the offset value to total images.
$query_next = $this->total_count > $offset;
$savings[ $type ]['bytes'] = size_format( $savings[ $type ]['bytes'], 1 );
wp_cache_set( 'wp-smush-resize_savings', $savings['resize'], 'wp-smush' );
wp_cache_set( 'wp-smush-pngjpg_savings', $savings['pngjpg'], 'wp-smush' );
wp_cache_set( $key_count, $count, 'wp-smush' );
return $return_count ? $count : $savings[ $type ];
* Get the media attachment IDs.
* @param bool $force_update Force update.
public function get_media_attachments( $force_update = false ) {
// Return results from cache.
$posts = wp_cache_get( 'media_attachments', 'wp-smush' );
// Remove the Filters added by WP Media Folder.
do_action( 'wp_smush_remove_filters' );
'SELECT ID FROM `%s` WHERE post_type = "attachment" AND post_mime_type IN (%s)',
implode( ',', array_fill( 0, count( Core::$mime_types ), '%s' ) )
// Add the attachments to cache.
wp_cache_set( 'media_attachments', $posts, 'wp-smush' );
* Adds the ID of the smushed image to the media_attachments list.
* @param int $id Attachment's ID.
public function add_to_media_attachments_list( $id ) {
$posts = wp_cache_get( 'media_attachments', 'wp-smush' );
// Return if there's no list to update.
$mime_type = get_post_mime_type( $id );
$id_string = (string) $id;
// Add the ID if the mime type is allowed and the ID isn't in the list already.
if ( $mime_type && in_array( $mime_type, Core::$mime_types, true ) && ! in_array( $id_string, $posts, true ) ) {
wp_cache_set( 'media_attachments', $posts, 'wp-smush' );
* Updates the IDs lists when an attachment is deleted.
* @param integer $id Deleted attachment ID.
public function update_lists( $id ) {
$this->remove_from_media_attachments_list( $id );
self::remove_from_smushed_list( $id );
* Removes the ID of the deleted image from the media_attachments list.
* @param int $id Attachment's ID.
private function remove_from_media_attachments_list( $id ) {
$posts = wp_cache_get( 'media_attachments', 'wp-smush' );
// Return if there's no list to update.
$index = array_search( (string) $id, $posts, true );
if ( false !== $index ) {
unset( $posts[ $index ] );
wp_cache_set( 'media_attachments', $posts, 'wp-smush' );
* @param bool $force_update Force update.
public function get_smushed_attachments( $force_update = false ) {
// Remove the Filters added by WP Media Folder.
do_action( 'wp_smush_remove_filters' );
"SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE meta_key=%s",
Modules\Smush::$smushed_meta_key
* Adds an ID to the smushed IDs list from the object cache.
* @param integer $attachment_id ID of the smushed attachment.
public static function add_to_smushed_list( $attachment_id ) {
$smushed_ids = wp_cache_get( 'wp-smush-smushed_ids', 'wp-smush' );
if ( ! empty( $smushed_ids ) ) {
$attachment_id = strval( $attachment_id );
if ( ! in_array( $attachment_id, $smushed_ids, true ) ) {
$smushed_ids[] = $attachment_id;
wp_cache_set( 'wp-smush-smushed_ids', $smushed_ids, 'wp-smush' );
* Removes an ID from the smushed IDs list from the object cache.
* @param integer $attachment_id ID of the smushed attachment.
public static function remove_from_smushed_list( $attachment_id ) {
$smushed_ids = wp_cache_get( 'wp-smush-smushed_ids', 'wp-smush' );
if ( ! empty( $smushed_ids ) ) {
$index = array_search( strval( $attachment_id ), $smushed_ids, true );
if ( false !== $index ) {
unset( $smushed_ids[ $index ] );
wp_cache_set( 'wp-smush-smushed_ids', $smushed_ids, 'wp-smush' );
* Get all the attachments with wp-smush-lossy.
public function get_super_smushed_attachments() {
'key' => 'wp-smush-lossy',
return $this->run_query( $meta_query );
* Fetch all the unsmushed attachments.
public function get_unsmushed_attachments() {
return $this->run_query( self::get_unsmushed_meta_query() );
* Temporary remove Smush metadata.
* We use this in order to temporary remove the stats metadata,
* e.g While generating thumbnail or wp_generate_ when disabled auto smush.
* Note, if member's site allows compression of the original file,
* when we remove stats, we might lose a large amount of storage (stats) that we saved for the member's site.
* => TODO: Delete stats or just update new stats with re-smush?
* @param int $attachment_id Attachment ID.
public function remove_stats( $attachment_id ) {
delete_post_meta( $attachment_id, Modules\Smush::$smushed_meta_key );
delete_post_meta( $attachment_id, 'wp-smush-lossy' );
// Finally, remove the attachment ID from cache.
self::remove_from_smushed_list( $attachment_id );
* Get unsmushed meta query.