: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* S3 integration: S3 class
* Minimum supported version - Offload Media 2.4
* @package Smush\Core\Modules\Integrations
* @author Umesh Kumar <umesh@incsub.com>
* @copyright (c) 2017, Incsub (http://incsub.com)
namespace Smush\Core\Integrations;
use Amazon_S3_And_CloudFront;
use DeliciousBrains\WP_Offload_Media\Items\Media_Library_Item;
if ( ! defined( 'WPINC' ) ) {
class S3 extends Abstract_Integration {
* Save list of files need to remove
* when user enabling "Remove Files From Server".
private $files_to_remove = array();
* Cache list of files download failed.
private $files_download_failed = array();
* On Smush mode, we will disable auto download and auto-upload
* while working with Smush.
* Cache list of files to delete via remove_file method.
* Cache list of filters for get_attached_file.
private $list_file_filters;
public function __construct() {
$this->enabled = function_exists( 'as3cf_init' ) || function_exists( 'as3cf_pro_init' );
// Hook at the end of setting row to output a error div.
add_action( 'smush_setting_column_right_inside', array( $this, 's3_setup_message' ), 15 );
// Show S3 integration message, if user hasn't enabled it.
add_action( 'wp_smush_header_notices', array( $this, 'show_s3_support_required_notice' ) );
if ( ! WP_Smush::is_pro() || ! $this->enabled ) {
add_action( 'smush_setting_column_tag', array( $this, 'add_pro_tag' ) );
// Do not continue if not enabled or S3 Offload plugin is not installed.
if ( ! $this->is_active() ) {
// Load all our custom actions/filters after loading as3cf.
if ( did_action( 'as3cf_init' ) ) {
add_action( 'as3cf_init', array( $this, 'init' ) );
* Register actions, filters for S3.
// TODO: (stats refactor) we still need some of the stuff from this controller e.g. notices
add_filter( 'wp_smush_file_exists', array( $this, 'file_exists_on_s3' ), 10, 4 );
add_filter( 'wp_smush_get_attached_file', array( $this, 'get_attached_file' ), 10, 5 );
// Check unique file with the backup file.
add_filter( 'wp_unique_filename', array( $this, 'filter_unique_filename' ), 99, 3 );// S3 is using priority 10.
// Check if the file exists for the given path and download.
add_action( 'wp_smush_before_restore_backup', array( $this, 'maybe_download_file' ), 10, 2 );
// Update original source path of as3cf_items after converting PNG to JPG.
add_action( 'wp_smush_image_url_changed', array( $this, 'update_original_source_path_after_png2jpg' ), 10, 4 );
// Update original source path of as3cf_items after restore the converted PNG2JPG file.
add_action( 'wp_smush_image_url_updated', array( $this, 'update_original_source_path_after_restore_png' ), 10, 3 );
// If user is enabling copy to S3.
if ( $as3cf->get_setting( 'copy-to-s3' ) ) {
* Activate Smush mode (Disable auto download and upload attachments).
* Note, we managed to release this mode by using Helper::wp_update_attachment_metadata() or
* via action "wp_smush_after_smush_file"
// Activate smush mode before smushing/restoring.
add_action( 'wp_smush_before_smush_file', array( $this, 'activate_smush_mode' ), 1 );
add_action( 'wp_smush_before_restore_backup', array( $this, 'activate_smush_mode' ), 1 );
* When WP create a new attachment, S3 will try to upload it to the server,
* and then it will upload the thumbnails later after regenerating the thumbnails.
* So we use this filter to temporary disable upload if smush can do with this file.
* Note, if we don't work with the current image, e.g animated file,
* we managed to release this mode by using maybe_release_smush_mode.
add_filter( 'wp_update_attachment_metadata', array( $this, 'maybe_active_smush_mode' ), 1, 2 );
// Reset smush mode after smushing/restoring.
add_action( 'wp_smush_after_smush_file', array( $this, 'release_smush_mode' ) );
add_action( 'wp_smush_after_restore_backup', array( $this, 'release_smush_mode' ) );
* Make sure we exit Smush mode before updating the metadata,
* this will help we to upload the attachments, and remove them if it's required.
add_action( 'wp_smush_before_update_attachment_metadata', array( $this, 'release_smush_mode' ) );
// Release mode when we don't smush the image.
add_action( 'wp_smush_no_smushit', array( $this, 'maybe_release_smush_mode' ) );
// Remove .bak file after restoring, and JPG files after restoring PNG file.
add_action( 'wp_smush_after_remove_file', array( $this, 'remove_file' ), 10, 3 );
// Make sure we removed all downloaded files.
add_filter( 'shutdown', array( $this, 'maybe_remove_downloaded_files' ), 10, 2 );
* Disable module functionality if not PRO.
public function setting_status() {
return ! WP_Smush::is_pro() ? true : ! $this->enabled;
/**************************************
* OVERWRITE PARENT CLASS FUNCTIONALITY
* Filters the setting variable to add S3 setting title and description.
* @param array $settings Settings array.
public function register( $settings ) {
$plugin_url = esc_url( 'https://wordpress.org/plugins/amazon-s3-and-cloudfront/' );
$settings[ $this->module ] = array(
'label' => __( 'Enable Amazon S3 support', 'wp-smushit' ),
'short_label' => __( 'Amazon S3', 'wp-smushit' ),
'desc' => sprintf( /* translators: %1$s - <a>, %2$s - </a> */
"Storing your image on S3 buckets using %1\$sWP Offload Media%2\$s? Smush can detect and smush those assets for you, including when you're removing files from your host server.",
"<a href='$plugin_url' target = '_blank'>",
/**************************************
* Check if the file is served by S3 and download the file for given path
* @param string $file_path Full file path.
* @param string $attachment_id Attachment ID.
* @return bool|string False/ File Path
public function maybe_download_file( $file_path, $attachment_id ) {
// Download the backup file if it doesn't exist on the server.
return $this->download_file( $file_path, $attachment_id );
* Checks if the given attachment is on S3 or not, Returns S3 URL or WP Error
* @param string $attachment_id Attachment ID.
public function is_image_on_s3( $attachment_id = '' ) {
if ( empty( $attachment_id ) ) {
// If the file path contains S3, get the s3 URL for the file.
if ( function_exists( 'as3cf_get_attachment_url' ) ) {
return as3cf_get_attachment_url( $attachment_id );
Helper::logger()->integrations()->error( 'S3 - Function as3cf_get_attachment_url does not exists.' );
* Checks if file exits on S3.
* @param bool $exists If file exists on S3.
* @param mixed $file_path File path or empty value.
* @param string $attachment_id Attachment ID.
* @param bool $should_download Should download attachment.
* @return bool|string Returns TRUE OR File path if it exists, FALSE otherwise.
public function file_exists_on_s3( $exists, $file_path, $attachment_id, $should_download ) {
if ( empty( $file_path ) ) {
// Maybe file is not uploaded to provider, try to get the raw file path.
$file_path = $this->get_raw_attached_file( $attachment_id );
$exists = file_exists( $file_path );
if ( $should_download ) {
$exists = $this->download_file( $file_path, $attachment_id );
$exists = $this->does_image_exists( $attachment_id, $file_path );
* Error message to show when S3 support is required.
* Show a error message to admins, if they need to enable S3 support. If "remove files from
* server" option is enabled in WP Offload Media plugin, we need WP Smush Pro to enable S3 support.
public function show_s3_support_required_notice() {
// Do not display it for other users. Do not display on network screens, if network-wide option is disabled.
if ( ! current_user_can( 'manage_options' ) || ! Settings::can_access( 'integrations' ) ) {
// If already dismissed, do not show.
if ( '1' === get_site_option( 'wp-smush-hide_s3support_alert' ) ) {
// Return early, if support is not required.
if ( ! $this->s3_support_required() ) {
$settings_link = is_multisite() && is_network_admin()
? network_admin_url( 'admin.php?page=smush-integrations' )
: menu_page_url( 'smush-integrations', false );
if ( WP_Smush::is_pro() ) {
* If premium user, but S3 support is not enabled.
/* Translators: %1$s: opening strong tag, %2$s: closing strong tag, %s: settings link, %3$s: opening a and strong tags, %4$s: closing a and strong tags */
'We can see you have WP Offload Media installed. If you want to optimize your S3 images, you’ll need to enable the %3$sAmazon S3 Support%4$s feature in Smush’s Integrations.',
"<a href='$settings_link'><strong>",
/* Translators: %1$s: opening strong tag, %2$s: closing strong tag, %s: settings link, %3$s: opening a and strong tags, %4$s: closing a and strong tags */
"We can see you have WP Offload Media installed. If you want to optimize your S3 images you'll need to %3\$supgrade to Smush Pro%4\$s",
'<a href=' . esc_url( 'https://wpmudev.com/project/wp-smush-pro' ) . '><strong>',
$message = '<p>' . $message . '</p>';
echo '<div role="alert" id="wp-smush-s3support-alert" class="sui-notice" data-message="' . esc_attr( $message ) . '" aria-live="assertive"></div>';
* Prints the message for S3 setup
* @param string $setting_key Settings key.
public function s3_setup_message( $setting_key ) {
if ( $this->module !== $setting_key ) {
* Amazon_S3_And_CloudFront global.
* @var Amazon_S3_And_CloudFront $as3cf
// If S3 integration is not enabled, return.
$setting_val = WP_Smush::is_pro() ? $this->settings->get( $this->module ) : 0;
// If integration is disabled when S3 offload is active, do not continue.
if ( ! $setting_val && is_object( $as3cf ) ) {
// If S3 offload global variable is not available, plugin is not active.
if ( ! is_object( $as3cf ) ) {
$message = __( 'To use this feature you need to install WP Offload Media and have an Amazon S3 account setup.', 'wp-smushit' );
} elseif ( ! method_exists( $as3cf, 'is_plugin_setup' ) || ! method_exists( $as3cf, 'get_plugin_page_url' ) ) {
// Check if in case for some reason, we couldn't find the required function.
$class = ' sui-notice-warning';
$message = sprintf( /* translators: %1$s: opening a tag, %2$s: closing a tag */
'We are having trouble interacting with WP Offload Media, make sure the plugin is activated. Or you can %1$sreport a bug%2$s.',
'<a href="' . esc_url( 'https://wpmudev.com/contact' ) . '" target="_blank">',
} elseif ( ! $as3cf->is_plugin_setup() ) {
// Plugin is not setup, or some information is missing.
$class = ' sui-notice-warning';
$message = sprintf( /* translators: %1$s: opening a tag, %2$s: closing a tag */
'It seems you haven’t finished setting up WP Offload Media yet. %1$sConfigure it now%2$s to enable Amazon S3 support.',
'<a href="' . $as3cf->get_plugin_page_url() . '" target="_blank">',
$class = ' sui-notice-info';
$message = __( 'Amazon S3 support is active.', 'wp-smushit' );
<div class="sui-toggle-content">
<div class="sui-notice<?php echo esc_attr( $class ); ?>">
<div class="sui-notice-content">
<div class="sui-notice-message">
<i class="sui-notice-icon sui-icon-info" aria-hidden="true"></i>
<p><?php echo wp_kses_post( $message ); ?></p>
* Add a pro tag next to the setting title.
* @param string $setting_key Setting key name.
public function add_pro_tag( $setting_key ) {
// Return if not NextGen integration.
if ( $this->module !== $setting_key || WP_Smush::is_pro() ) {
<span class="sui-tag sui-tag-pro">
<?php esc_html_e( 'Pro', 'wp-smushit' ); ?>
* Disable auto downloading the file to local while Smush doing, only allow it with our custom methods:
* Helper::get_attached_file( $attachment_id, $type='smush|resize|original')
* or Helper::exists_or_downloaded( $file, $attachment_id ),
* and set wait_for_generate_attachment_metadata is TRUE to disable auto upload attachments while
* we are working with it.
* Enable copy file back to local and wait_for_generate_attachment_metadata while we working with smush or restore.
public function activate_smush_mode() {
if ( $this->is_active() && ! $this->on_smush_mode ) {
$this->on_smush_mode = true;
* Disable auto download to local, we only enable it via self::downoad_file method to avoid automatic download link.
* e.g. wp_attachment_is_image()
add_filter( 'as3cf_get_attached_file_copy_back_to_local', '__return_false', 9998 );
// Disable auto upload attachments.
add_filter( 'as3cf_wait_for_generate_attachment_metadata', '__return_true', 9998 );
// If user enabling remove file on local.
if ( $as3cf && $as3cf->get_setting( 'remove-local-file' ) ) {
// We don't remove the filter because it might be called later.
add_filter( 'as3cf_upload_attachment_local_files_to_remove', array( $this, 'remove_missing_files_to_avoid_error_log_from_s3' ), 99 );
* Set Smush mode while creating a new image.
* @param array $image_meta Image meta.
* @param int $attachment_id Attachment ID.
* @return array The provided metadata.
public function maybe_active_smush_mode( $image_meta, $attachment_id ) {
// When async uploading or the image is created from Gutenberg, activate smush mode.
&& empty( $new_meta['sizes'] )
&& ! empty( $image_meta['file'] )
&& ( isset( $_POST['post_id'] ) || isset( $_FILES['async-upload'] ) || ! Helper::is_non_rest_media() )
// If enabling auto-smush.
&& $this->settings->get( 'auto' )
&& ! did_action( 'wp_smush_before_smush_file' )
&& ! did_action( 'wp_smush_before_restore_backup' )
&& ! did_action( 'wp_smush_before_update_attachment_metadata' )
&& ! did_action( 'wp_smush_no_smushit' )
&& Helper::is_smushable( $attachment_id )
// Make sure we only disable while async upload new image.
&& ! $this->does_image_exists( $attachment_id, $this->get_raw_attached_file( $attachment_id, 'original' ) )
$this->activate_smush_mode();