: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Smush Settings class: Settings
* @since 3.0 Migrated from old settings class.
use Smush\Core\CDN\CDN_Helper;
use Smush\Core\Stats\Global_Stats;
if ( ! defined( 'WPINC' ) ) {
const SUBSITE_CONTROLS_OPTION_KEY = 'wp-smush-networkwide';
const SETTINGS_KEY = 'wp-smush-settings';
const LEVEL_LOSSLESS = 0;
const LEVEL_SUPER_LOSSY = 1;
const LEVEL_ULTRA_LOSSY = 2;
private static $instance = null;
private $settings = array();
* Default settings array.
* We don't want it to be edited directly, so we use public get_*, set_* and delete_* methods.
* @since 3.0 Improved structure.
* @since 3.2.2 Changed to be a default array.
* @since 3.8.0 Added webp_mod.
private $defaults = array(
'auto' => true, // works with CDN.
'lossy' => 0, // works with CDN.
'strip_exif' => true, // works with CDN.
'png_to_jpg' => false, // works with CDN.
'accessible_colors' => false,
'background_images' => true,
'rest_api_support' => false, // CDN option.
'webp_mod' => false, // WebP module.
'background_email' => false,
'webp_direct_conversion' => false,
'webp_fallback' => false,
* @since 3.8.0 Added webp.
private $modules = array( 'bulk', 'integrations', 'lazy_load', 'cdn', 'webp', 'settings' );
* List of features/settings that are free.
* @var array $basic_features
public static $basic_features = array( 'bulk', 'auto', 'strip_exif', 'resize', 'original', 'gutenberg', 'js_builder', 'gform', 'lazy_load', 'lossy' );
* List of fields in bulk smush form.
* @used-by save_settings()
private $bulk_fields = array( 'lossy', 'bulk', 'auto', 'strip_exif', 'resize', 'original', 'backup', 'png_to_jpg', 'no_scale', 'background_email' );
private $upsell_fields = array( 'background_email' );
* List of fields in integration form.
* @used-by save_settings()
private $integrations_fields = array( 'gutenberg', 'gform', 'js_builder', 's3', 'nextgen' );
* List of fields in CDN form.
* @used-by save_settings()
private $cdn_fields = array( 'cdn', 'background_images', 'auto_resize', 'webp', 'rest_api_support' );
* List of fields in CDN form.
* @used-by save_settings()
private $webp_fields = array( 'webp_mod', 'webp_direct_conversion', 'webp_fallback' );
* List of fields in Settings form.
* @used-by save_settings()
private $settings_fields = array( 'detection', 'accessible_colors', 'usage', 'keep_data', 'api_auth' );
* List of fields in lazy loading form.
* @used-by save_settings()
private $lazy_load_fields = array( 'lazy_load' );
private $activated_subsite_pages;
* Return the plugin instance.
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self();
* WP_Smush_Settings constructor.
private function __construct() {
// Do not initialize if not in admin area
// wp_head runs specifically in the frontend, good check to make sure we're accidentally not loading settings on required pages.
if ( ! is_admin() && ! wp_doing_ajax() && did_action( 'wp_head' ) ) {
add_action( 'wp_ajax_smush_save_settings', array( $this, 'save_settings' ) );
add_action( 'wp_ajax_reset_settings', array( $this, 'reset' ) );
add_filter( 'wp_smush_settings', array( $this, 'remove_unavailable' ) );
add_action( 'switch_blog', array( $this, 'maybe_reset_cache_site_settings' ), 10, 2 );
* Remove settings that are not available on a specific version of WordPress.
* @param array $settings Current settings.
public function remove_unavailable( $settings ) {
if ( version_compare( $wp_version, '5.3', '<' ) ) {
if ( isset( $this->bulk_fields['no_scale'] ) ) {
unset( $this->bulk_fields['no_scale'] );
if ( isset( $settings['no_scale'] ) ) {
unset( $settings['no_scale'] );
* Get descriptions for all settings.
* @since 3.8.6 Moved from Core
* @param string $id Setting ID to get data for.
* @param string $type What value to get. Accepts: label, short_label or desc.
public static function get_setting_data( $id, $type = '' ) {
$bg_optimization = WP_Smush::get_instance()->core()->mod->bg_optimization;
if ( $bg_optimization->can_use_background() ) {
$bg_email_desc = esc_html__( 'Be notified via email about the bulk smush status when the process has completed.', 'wp-smushit' );
$bg_email_desc = sprintf(
/* translators: %s Email address */
esc_html__( "Be notified via email about the bulk smush status when the process has completed. You'll receive an email at %s.", 'wp-smushit' ),
'<strong>' . $bg_optimization->get_mail_recipient() . '</strong>'
'background_email' => array(
'label' => esc_html__( 'Enable email notification', 'wp-smushit' ),
'short_label' => esc_html__( 'Email Notification', 'wp-smushit' ),
'desc' => $bg_email_desc,
'short_label' => esc_html__( 'Image Sizes', 'wp-smushit' ),
'desc' => esc_html__( 'WordPress generates multiple image thumbnails for each image you upload. Choose which of those thumbnail sizes you want to include when bulk smushing.', 'wp-smushit' ),
'label' => esc_html__( 'Automatically compress my images on upload', 'wp-smushit' ),
'short_label' => esc_html__( 'Automatic compression', 'wp-smushit' ),
'desc' => esc_html__( 'When you upload images to your site, we will automatically optimize and compress them for you.', 'wp-smushit' ),
'label' => esc_html__( 'Choose Compression Level', 'wp-smushit' ),
'short_label' => esc_html__( 'Smush Mode', 'wp-smushit' ),
/* translators: 1: Opening <strong> 2: Closing </strong> */
esc_html__( 'Choose the level of compression that suits your needs. We recommend %1$sUltra%2$s for faster sites and impressive image quality.', 'wp-smushit' ),
'label' => esc_html__( 'Strip my image metadata', 'wp-smushit' ),
'short_label' => esc_html__( 'Metadata', 'wp-smushit' ),
'desc' => esc_html__( 'Photos often store camera settings in the file, i.e., focal length, date, time and location. Removing EXIF data reduces the file size. Note: it does not strip SEO metadata.', 'wp-smushit' ),
'label' => esc_html__( 'Resize original images', 'wp-smushit' ),
'short_label' => esc_html__( 'Image Resizing', 'wp-smushit' ),
'desc' => esc_html__( 'As of version 5.3, WordPress creates a scaled version of uploaded images over 2560x2560px by default, and keeps your original uploaded images as a backup. If desired, you can choose a different resizing threshold or disable the scaled images altogether.', 'wp-smushit' ),
'label' => esc_html__( 'Disable scaled images', 'wp-smushit' ),
'short_label' => esc_html__( 'Disable Scaled Images', 'wp-smushit' ),
'desc' => esc_html__( 'Enable this feature to disable automatic resizing of images above the threshold, keeping only your original uploaded images. Note: WordPress excludes PNG images from automatic image resizing. As a result, only uploaded JPEG images are affected by these settings.', 'wp-smushit' ),
'label' => esc_html__( 'Detect and show incorrectly sized images', 'wp-smushit' ),
'short_label' => esc_html__( 'Image Resize Detection', 'wp-smushit' ),
'desc' => esc_html__( 'This will add functionality to your website that highlights images that are either too large or too small for their containers.', 'wp-smushit' ),
'label' => esc_html__( 'Optimize original images', 'wp-smushit' ),
'short_label' => esc_html__( 'Original Images', 'wp-smushit' ),
'desc' => esc_html__( 'Choose how you want Smush to handle the original image file when you run a bulk smush.', 'wp-smushit' ),
'label' => esc_html__( 'Backup original images', 'wp-smushit' ),
'short_label' => esc_html__( 'Backup Original Images', 'wp-smushit' ),
'desc' => esc_html__( 'Enable this feature to save a copy of your original images so you can restore them at any point. Note: Keeping a copy of the original images can significantly increase the size of your uploads folder.', 'wp-smushit' ),
'label' => esc_html__( 'Auto-convert PNGs to JPEGs (lossy)', 'wp-smushit' ),
'short_label' => esc_html__( 'PNG to JPEG Conversion', 'wp-smushit' ),
'desc' => esc_html__( 'When you compress a PNG, Smush will check if converting it to JPEG could further reduce its size.', 'wp-smushit' ),
'accessible_colors' => array(
'label' => esc_html__( 'Enable high contrast mode', 'wp-smushit' ),
'short_label' => esc_html__( 'Color Accessibility', 'wp-smushit' ),
'desc' => esc_html__( 'Increase the visibility and accessibility of elements and components to meet WCAG AAA requirements.', 'wp-smushit' ),
'label' => esc_html__( 'Allow usage tracking', 'wp-smushit' ),
'short_label' => esc_html__( 'Usage Tracking', 'wp-smushit' ),
'desc' => esc_html__( 'Help make Smush better by letting our designers learn how you’re using the plugin.', 'wp-smushit' ),
* Allow adding other settings via filtering the variable
* Like Nextgen and S3 integration
$settings = apply_filters( 'wp_smush_settings', $settings );
if ( ! isset( $settings[ $id ] ) ) {
if ( 'short-label' === $type ) {
return ! empty( $settings[ $id ]['short_label'] ) ? $settings[ $id ]['short_label'] : $settings[ $id ]['label'];
if ( 'label' === $type ) {
return ! empty( $settings[ $id ]['label'] ) ? $settings[ $id ]['label'] : $settings[ $id ]['short_label'];
if ( 'desc' === $type ) {
return $settings[ $id ]['desc'];
* Getter method for bulk settings fields.
public function get_bulk_fields() {
return $this->bulk_fields;
* Getter method for integration fields.
public function get_integrations_fields() {
return $this->integrations_fields;
* Getter method for CDN fields.
public function get_cdn_fields() {
return $this->cdn_fields;
public function is_upsell_field( $field ) {
return in_array( $field, $this->upsell_fields, true );
public function is_pro_field( $field ) {
return ! in_array( $field, self::$basic_features, true );
public function can_access_pro_field( $field ) {
if ( WP_Smush::is_pro() ) {
$bg_optimization = WP_Smush::get_instance()->core()->mod->bg_optimization;
return 'background_email' === $field && $bg_optimization->can_use_background();
* Getter method for settings fields.
public function get_settings_fields() {
return $this->settings_fields;
* Getter method for lazy loading fields.
public function get_lazy_load_fields() {
return $this->lazy_load_fields;
public function get_webp_fields() {
return $this->webp_fields;
* If there are no settings in the database, populate it with the defaults, if settings are present
* Checks whether the settings are applicable for the whole network/site or sitewise (multisite).
public function is_network_enabled() {
return $this->is_network_setting( self::SETTINGS_KEY );
public function is_network_setting( $option_id ) {
if ( ! is_multisite() ) {
$global_setting_keys = array(
self::SUBSITE_CONTROLS_OPTION_KEY,
if ( in_array( $option_id, $global_setting_keys, true ) ) {
$subsite_modules = $this->get_activated_subsite_pages();
if ( empty( $subsite_modules ) ) {
$module_option_keys = array(
'wp-smush-image_sizes' => 'bulk',
'wp-smush-resize_sizes' => 'bulk',
'wp-smush-lazy_load' => 'lazy_load',
'wp-smush-cdn_status' => 'cdn',
if ( ! isset( $module_option_keys[ $option_id ] ) ) {
return self::is_ajax_network_admin() || is_network_admin();
$module = $module_option_keys[ $option_id ];
return ! in_array( $module, $subsite_modules, true );
* Check if user is able to access the page.
* @param string|bool $module Check if a specific module is allowed.
* @param bool $top_menu Is this a top level menu point? Defaults to a Smush sub page.
* @return bool|array Can access page or not. If custom access rules defined - return custom rules array.
public static function can_access( $module = false, $top_menu = false ) {
// Allow all access on single site installs.
if ( ! is_multisite() ) {
$access = get_site_option( self::SUBSITE_CONTROLS_OPTION_KEY );
// Check to if the settings update is network-wide or not ( only if in network admin ).
$action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_SPECIAL_CHARS );
$is_network_admin = is_network_admin() || 'save_settings' === $action;
if ( self::is_ajax_network_admin() ) {
$is_network_admin = true;