debug_message( '' . __METHOD__ . '()' );
global $exactdn;
if ( \is_object( $exactdn ) ) {
$this->debug_message( 'you are doing it wrong' );
return;
}
$this->content_url();
// Bail out on customizer.
if ( \is_customize_preview() ) {
return;
}
$this->request_uri = \add_query_arg( '', '' );
if ( false === \strpos( $this->request_uri, 'page=ewww-image-optimizer-options' ) ) {
$this->debug_message( "request uri is {$this->request_uri}" );
} else {
$this->debug_message( 'request uri is EWWW IO settings' );
}
if ( '/robots.txt' === $this->request_uri || '/sitemap.xml' === $this->request_uri ) {
return;
}
\add_filter( 'exactdn_skip_page', array( $this, 'skip_page' ), 10, 2 );
/**
* Allow pre-empting the parsers by page.
*
* @param bool Whether to skip parsing the page.
* @param string The URI/path of the page.
*/
if ( \apply_filters( 'exactdn_skip_page', false, $this->request_uri ) ) {
return;
}
if ( ! $this->scheme ) {
$scheme = 'http';
if ( \strpos( $this->site_url, 'https://' ) !== false ) {
$this->debug_message( "{$this->site_url} contains https" );
$scheme = 'https';
} elseif ( \strpos( \get_home_url(), 'https://' ) !== false ) {
$this->debug_message( \get_home_url() . ' contains https' );
$scheme = 'https';
} elseif ( \strpos( $this->content_url, 'https://' ) !== false ) {
$this->debug_message( $this->content_url . ' contains https' );
$scheme = 'https';
} elseif ( isset( $_SERVER['HTTPS'] ) && 'off' !== $_SERVER['HTTPS'] ) {
$this->debug_message( 'page requested over https' );
$scheme = 'https';
} else {
$this->debug_message( 'using plain http' );
}
$this->scheme = $scheme;
}
if ( \is_multisite() && \defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
$this->debug_message( 'working in sub-domain mode' );
} elseif ( \is_multisite() ) {
if ( \defined( 'EXACTDN_SUB_FOLDER' ) && EXACTDN_SUB_FOLDER ) {
$this->sub_folder = true;
$this->debug_message( 'working in sub-folder mode due to constant override' );
} elseif ( \defined( 'EXACTDN_SUB_FOLDER' ) ) {
$this->debug_message( 'working in sub-domain mode due to constant override' );
} elseif ( \get_site_option( 'exactdn_sub_folder' ) ) {
$this->sub_folder = true;
$this->debug_message( 'working in sub-folder mode due to global option' );
} elseif ( \get_current_blog_id() > 1 ) {
$network_site_url = \network_site_url();
$network_domain = $this->parse_url( $network_site_url, PHP_URL_HOST );
if ( $network_domain === $this->upload_domain ) {
$this->sub_folder = true;
$this->debug_message( 'working in sub-folder mode due to matching domain' );
}
}
}
// Make sure we have an ExactDN domain to use.
if ( ! $this->setup() ) {
return;
}
// Enables scheduled health checks via wp-cron.
\add_action( 'easyio_verification_checkin', array( $this, 'health_check' ) );
// Images in post content and galleries.
\add_filter( 'the_content', array( $this, 'filter_the_content' ), 999999 );
// Start an output buffer before any output starts.
\add_filter( $this->prefix . 'filter_page_output', array( $this, 'filter_page_output' ), 5 );
// Core image retrieval.
if ( \defined( 'EIO_DISABLE_DEEP_INTEGRATION' ) && EIO_DISABLE_DEEP_INTEGRATION ) {
$this->debug_message( 'deep (image_downsize) integration disabled' );
} elseif ( ! \function_exists( '\aq_resize' ) ) {
\add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), 10, 3 );
} else {
$this->debug_message( 'aq_resize detected, image_downsize filter disabled' );
}
// Disable image_downsize filter during themify_get_image().
\add_action( 'themify_before_post_image', array( $this, 'disable_image_downsize' ) );
if ( \defined( 'EXACTDN_IMAGE_DOWNSIZE_SCALE' ) && EXACTDN_IMAGE_DOWNSIZE_SCALE ) {
\add_action( 'exactdn_image_downsize_array', array( $this, 'image_downsize_scale' ) );
}
// Check REST API requests to see if ExactDN should be running.
\add_filter( 'rest_request_before_callbacks', array( $this, 'parse_restapi_maybe' ), 10, 3 );
// Check to see if the OMGF plugin is active, and suppress our font rewriting if it is.
if ( ( \defined( 'OMGF_PLUGIN_FILE' ) || \defined( 'OMGF_DB_VERSION' ) ) && ! \defined( 'EASYIO_REPLACE_GOOGLE_FONTS' ) ) {
\define( 'EASYIO_REPLACE_GOOGLE_FONTS', false );
}
// Overrides for admin-ajax images.
\add_filter( 'exactdn_admin_allow_image_downsize', array( $this, 'allow_admin_image_downsize' ), 10, 2 );
\add_filter( 'exactdn_admin_allow_image_srcset', array( $this, 'allow_admin_image_downsize' ), 10, 2 );
\add_filter( 'exactdn_admin_allow_plugin_url', array( $this, 'allow_admin_image_downsize' ), 10, 2 );
// Overrides for "pass through" images.
\add_filter( 'exactdn_pre_args', array( $this, 'exactdn_remove_args' ), 10, 3 );
// Overrides for user exclusions.
\add_filter( 'exactdn_skip_image', array( $this, 'exactdn_skip_user_exclusions' ), 9, 2 );
\add_filter( 'exactdn_skip_for_url', array( $this, 'exactdn_skip_user_exclusions' ), 9, 2 );
// Responsive image srcset substitution.
\add_filter( 'wp_calculate_image_srcset', array( $this, 'filter_srcset_array' ), 1001, 5 );
\add_filter( 'wp_calculate_image_sizes', array( $this, 'filter_sizes' ), 1, 2 ); // Early so themes can still filter.
// Filter for generic use by other plugins/themes.
\add_filter( 'exactdn_local_to_cdn_url', array( $this, 'plugin_get_image_url' ) );
// Filter to check for Elementor full_width layouts.
\add_filter( 'elementor/frontend/builder_content_data', array( $this, 'elementor_builder_content_data' ) );
// Filter for FacetWP JSON responses.
\add_filter( 'facetwp_render_output', array( $this, 'filter_facetwp_json_output' ) );
// Filter for Envira image URLs.
\add_filter( 'envira_gallery_output_item_data', array( $this, 'envira_gallery_output_item_data' ) );
\add_filter( 'envira_gallery_image_src', array( $this, 'plugin_get_image_url' ) );
// Filters for BuddyBoss image URLs.
\add_filter( 'bp_media_get_preview_image_url', array( $this, 'bp_media_get_preview_image_url' ), PHP_INT_MAX, 5 );
\add_filter( 'bb_video_get_thumb_url', array( $this, 'bb_video_get_thumb_url' ), PHP_INT_MAX, 5 );
\add_filter( 'bp_document_get_preview_url', array( $this, 'bp_document_get_preview_url' ), PHP_INT_MAX, 6 );
\add_filter( 'bb_video_get_symlink', array( $this, 'bb_video_get_symlink' ), PHP_INT_MAX, 4 );
// Filters for BuddyBoss URL symlinks.
\add_filter( 'bb_media_do_symlink', array( $this, 'buddyboss_do_symlink' ), PHP_INT_MAX );
\add_filter( 'bb_document_do_symlink', array( $this, 'buddyboss_do_symlink' ), PHP_INT_MAX );
\add_filter( 'bb_video_do_symlink', array( $this, 'buddyboss_do_symlink' ), PHP_INT_MAX );
\add_filter( 'bb_video_create_thumb_symlinks', array( $this, 'buddyboss_do_symlink' ), PHP_INT_MAX );
// Filters for BuddyBoss media access checks.
\add_filter( 'bb_media_check_default_access', '__return_true', PHP_INT_MAX );
\add_filter( 'bb_media_settings_callback_symlink_direct_access', array( $this, 'buddyboss_media_directory_allow_access' ), PHP_INT_MAX, 2 );
// Filter for NextGEN image URLs within JS.
\add_filter( 'ngg_pro_lightbox_images_queue', array( $this, 'ngg_pro_lightbox_images_queue' ) );
\add_filter( 'ngg_get_image_url', array( $this, 'plugin_get_image_url' ) );
// Filter for Spotlight Social Media Feeds.
\add_filter( 'spotlight/instagram/server/transform_item', array( $this, 'spotlight_instagram_response' ) );
// Filter for legacy WooCommerce API endpoints.
\add_filter( 'woocommerce_api_product_response', array( $this, 'woocommerce_api_product_response' ) );
// DNS prefetching.
\add_filter( 'wp_resource_hints', array( $this, 'resource_hints' ), 100, 2 );
// Get all the script/css urls and rewrite them (if enabled).
if ( $this->get_option( 'exactdn_all_the_things' ) ) {
\add_filter( 'style_loader_src', array( $this, 'parse_enqueue' ), 9999 );
\add_filter( 'script_loader_src', array( $this, 'parse_enqueue' ), 9999 );
}
if ( ! $this->get_option( 'exactdn_prevent_db_queries' ) ) {
$this->set_option( 'exactdn_prevent_db_queries', true );
}
// Improve the default content_width for Twenty Nineteen.
global $content_width;
if ( \function_exists( '\twentynineteen_setup' ) && 640 === (int) $content_width ) {
$content_width = 932;
}
// Configure Autoptimize with our CDN domain.
\add_filter( 'autoptimize_filter_cssjs_multidomain', array( $this, 'add_cdn_domain' ) );
if ( $this->is_as3cf_cname_active() ) {
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_as3cf_cname_active' );
return;
}
$upload_url_parts = $this->parse_url( $this->site_url );
if ( empty( $upload_url_parts ) ) {
$this->debug_message( "could not break down URL: $this->site_url" );
return;
}
$stored_local_domain = $this->get_exactdn_option( 'local_domain' );
if ( empty( $stored_local_domain ) ) {
$this->set_exactdn_option( 'local_domain', \base64_encode( $this->upload_domain ) );
$stored_local_domain = $this->upload_domain;
} elseif ( false !== \strpos( $stored_local_domain, '.' ) ) {
$this->set_exactdn_option( 'local_domain', \base64_encode( $stored_local_domain ) );
} else {
$stored_local_domain = \base64_decode( $stored_local_domain );
}
$this->debug_message( "saved domain is $stored_local_domain" );
$this->debug_message( "allowing images from here: $this->upload_domain" );
if (
(
false !== \strpos( $this->upload_domain, 'amazonaws.com' ) ||
false !== \strpos( $this->upload_domain, 'digitaloceanspaces.com' ) ||
false !== \strpos( $this->upload_domain, 'storage.googleapis.com' )
)
&& ! empty( $upload_url_parts['path'] )
) {
$this->remove_path = \rtrim( $upload_url_parts['path'], '/' );
$this->debug_message( "removing this from urls: $this->remove_path" );
}
if (
$stored_local_domain !== $this->upload_domain &&
! $this->allow_image_domain( $stored_local_domain ) &&
\is_admin()
) {
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_domain_mismatch' );
}
$this->allowed_domains[] = $this->exactdn_domain;
$this->allowed_domains = \apply_filters( 'exactdn_allowed_domains', $this->allowed_domains );
$this->debug_message( 'allowed domains: ' . \implode( ',', $this->allowed_domains ) );
$this->get_allowed_paths();
$this->validate_user_exclusions();
}
/**
* If ExactDN is enabled, validates and configures the ExactDN domain name.
*/
public function setup() {
$this->debug_message( '' . __METHOD__ . '()' );
// If we don't have a domain yet, go grab one.
$this->plan_id = (int) $this->get_exactdn_option( 'plan_id' );
$new_site = false;
if ( ! $this->get_exactdn_domain() ) {
$this->debug_message( 'attempting to activate exactDN' );
$exactdn_domain = $this->activate_site();
$new_site = true;
} else {
$this->debug_message( 'grabbing existing exactDN domain' );
$exactdn_domain = $this->get_exactdn_domain();
}
if ( ! $exactdn_domain ) {
\delete_option( $this->prefix . 'exactdn' );
\delete_site_option( $this->prefix . 'exactdn' );
$this->cron_setup( false );
return false;
}
$verified = true;
// If we have a domain, verify it.
if ( $new_site ) {
$verified = $this->verify_domain( $exactdn_domain );
if ( $verified ) {
// When this is a new site that is verified, setup health check.
$this->cron_setup();
}
}
if ( $verified ) {
$this->exactdn_domain = $exactdn_domain;
$this->debug_message( 'exactdn_domain: ' . $exactdn_domain );
$this->debug_message( 'exactdn_plan_id: ' . $this->plan_id );
return true;
}
\delete_option( $this->prefix . 'exactdn_domain' );
\delete_option( $this->prefix . 'exactdn_verified' );
\delete_site_option( $this->prefix . 'exactdn_domain' );
\delete_site_option( $this->prefix . 'exactdn_verified' );
$this->cron_setup( false );
return false;
}
/**
* Setup wp_cron tasks for scheduled verification.
*
* @global object $wpdb
*
* @param bool $schedule True to add event, false to remove/unschedule it.
*/
public function cron_setup( $schedule = true ) {
$this->debug_message( '' . __FUNCTION__ . '()' );
$event = 'easyio_verification_checkin';
// Setup scheduled optimization if the user has enabled it, and it isn't already scheduled.
if ( $schedule && ! \wp_next_scheduled( $event ) ) {
$this->debug_message( "scheduling $event" );
\wp_schedule_event( \time() + DAY_IN_SECONDS, \apply_filters( 'easyio_verification_schedule', 'daily', $event ), $event );
} elseif ( $schedule ) {
$this->debug_message( "$event already scheduled: " . \wp_next_scheduled( $event ) );
} elseif ( \wp_next_scheduled( $event ) ) {
$this->debug_message( "un-scheduling $event" );
\wp_clear_scheduled_hook( $event );
if ( ! \function_exists( '\is_plugin_active_for_network' ) && \is_multisite() ) {
// Need to include the plugin library for the is_plugin_active function.
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
if ( \is_multisite() && \is_plugin_active_for_network( \constant( \strtoupper( $this->prefix ) . 'PLUGIN_FILE_REL' ) ) ) {
global $wpdb;
$blogs = $wpdb->get_results( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d", $wpdb->siteid ), ARRAY_A );
if ( $this->is_iterable( $blogs ) ) {
foreach ( $blogs as $blog ) {
\switch_to_blog( $blog['blog_id'] );
\wp_clear_scheduled_hook( $event );
\restore_current_blog();
}
}
}
}
}
/**
* Use the Site URL to get the zone domain.
*/
public function activate_site() {
$this->debug_message( '' . __METHOD__ . '()' );
if ( $this->is_as3cf_cname_active() ) {
global $exactdn_activate_error;
$exactdn_activate_error = 'as3cf_cname_active';
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_as3cf_cname_active' );
return false;
}
$site_url = $this->content_url();
$url = 'http://optimize.exactlywww.com/exactdn/activate.php';
$ssl = \wp_http_supports( array( 'ssl' ) );
if ( $ssl ) {
$url = \set_url_scheme( $url, 'https' );
}
\add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
$result = \wp_remote_post(
$url,
array(
'timeout' => 10,
'body' => array(
'site_url' => $site_url,
'home_url' => \home_url(),
),
)
);
if ( \is_wp_error( $result ) ) {
$error_message = $result->get_error_message();
$this->debug_message( "exactdn activation request failed: $error_message" );
global $exactdn_activate_error;
$exactdn_activate_error = $error_message;
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
} elseif ( ! empty( $result['body'] ) && \strpos( $result['body'], 'domain' ) !== false ) {
$response = \json_decode( $result['body'], true );
if ( ! empty( $response['domain'] ) ) {
if (
false !== \strpos( $site_url, 'amazonaws.com' ) ||
false !== \strpos( $site_url, 'digitaloceanspaces.com' ) ||
false !== \strpos( $site_url, 'storage.googleapis.com' ) ||
$this->s3_active
) {
$this->set_exactdn_option( 'verify_method', -1, false );
}
if ( ! empty( $response['plan_id'] ) ) {
$this->set_exactdn_option( 'plan_id', (int) $response['plan_id'] );
$this->plan_id = (int) $response['plan_id'];
}
if ( \get_option( 'exactdn_never_been_active' ) ) {
$this->set_option( $this->prefix . 'lazy_load', true );
$this->set_option( 'exactdn_lossy', true );
$this->set_option( 'exactdn_all_the_things', true );
\delete_option( 'exactdn_never_been_active' );
}
if ( \function_exists( '\envira_flush_all_cache' ) ) {
\envira_flush_all_cache();
}
return $this->set_exactdn_domain( $response['domain'] );
}
} elseif ( ! empty( $result['body'] ) && false !== \strpos( $result['body'], 'error' ) ) {
$response = \json_decode( $result['body'], true );
$error_message = $response['error'];
$this->debug_message( "exactdn activation request failed: $error_message" );
global $exactdn_activate_error;
$exactdn_activate_error = $error_message;
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
}
return false;
}
/**
* Do a health check to verify the Easy IO domain is still good.
*/
public function health_check() {
$this->debug_message( '' . __METHOD__ . '()' );
$this->verify_domain( $this->exactdn_domain );
$this->set_exactdn_option( 'checkin', time() - 60 );
}
/**
* Verify the ExactDN domain.
*
* @param string $domain The ExactDN domain to verify.
* @return bool Whether the domain is still valid.
*/
public function verify_domain( $domain ) {
if ( empty( $domain ) ) {
return false;
}
$this->debug_message( '' . __METHOD__ . '()' );
// Check the time, to see how long it has been since we verified the domain.
$last_checkin = (int) $this->get_exactdn_option( 'checkin' );
if ( $this->get_exactdn_option( 'verified' ) && $last_checkin > time() ) {
$this->debug_message( 'not time yet: ' . $this->human_time_diff( $last_checkin ) );
return true;
}
$this->check_verify_method();
$this->set_exactdn_option( 'checkin', \time() + HOUR_IN_SECONDS );
// Set a default error.
global $exactdn_activate_error;
$exactdn_activate_error = 'zone not verified';
// Primary check sends the test URL to the API for full verification.
$api_url = 'http://optimize.exactlywww.com/exactdn/verify.php';
$ssl = \wp_http_supports( array( 'ssl' ) );
if ( $ssl ) {
$api_url = \set_url_scheme( $api_url, 'https' );
}
if ( ! \defined( 'EXACTDN_LOCAL_DOMAIN' ) && (int) $this->get_exactdn_option( 'verify_method' ) > 0 ) {
// Test with an image file that should be available on the ExactDN zone.
$test_url = \plugins_url( '/images/test.png', \constant( \strtoupper( $this->prefix ) . 'PLUGIN_FILE' ) );
$local_domain = $this->parse_url( $test_url, PHP_URL_HOST );
$test_url = \str_replace( $local_domain, $domain, $test_url );
$this->debug_message( "test url is $test_url" );
\add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
$test_result = \wp_remote_post(
$api_url,
array(
'timeout' => 10,
'body' => array(
'alias' => $domain,
'url' => $test_url,
'origin' => $this->content_url(),
),
)
);
if ( \is_wp_error( $test_result ) ) {
$error_message = $test_result->get_error_message();
$this->debug_message( "exactdn (1) verification request failed: $error_message" );
$exactdn_activate_error = $error_message;
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
} elseif ( ! empty( $test_result['body'] ) && false === \strpos( $test_result['body'], 'error' ) ) {
$response = \json_decode( $test_result['body'], true );
if ( ! empty( $response['success'] ) ) {
$this->debug_message( 'exactdn (real-world) verification succeeded' );
$this->set_exactdn_option( 'verified', 1, false );
$this->set_exactdn_option( 'verify_method', -1, false ); // After initial activation, use simpler API verification.
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_success' );
return true;
}
} elseif ( ! empty( $test_result['body'] ) ) {
$response = \json_decode( $test_result['body'], true );
$error_message = $response['error'];
$this->debug_message( "exactdn (1) verification request failed: $error_message" );
$exactdn_activate_error = $error_message;
if ( false !== \strpos( $error_message, 'not found' ) ) {
\delete_option( $this->prefix . 'exactdn_domain' );
\delete_site_option( $this->prefix . 'exactdn_domain' );
}
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
}
if ( ! empty( $test_result['response']['code'] ) && 200 !== (int) $test_result['response']['code'] ) {
$this->debug_message( 'received response code: ' . $test_result['response']['code'] );
}
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
}
// Secondary test against the API db.
\add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
$result = \wp_remote_post(
$api_url,
array(
'timeout' => 10,
'body' => array(
'alias' => $domain,
'origin' => $this->content_url(),
),
)
);
if ( \is_wp_error( $result ) ) {
$error_message = $result->get_error_message();
$this->debug_message( "exactdn verification request failed: $error_message" );
$exactdn_activate_error = $error_message;
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
} elseif ( ! empty( $result['body'] ) && false === \strpos( $result['body'], 'error' ) ) {
$response = \json_decode( $result['body'], true );
if ( ! empty( $response['success'] ) ) {
if ( ! empty( $response['plan_id'] ) ) {
$this->set_exactdn_option( 'plan_id', (int) $response['plan_id'] );
$this->plan_id = (int) $response['plan_id'];
}
$this->debug_message( 'exactdn verification via API succeeded' );
$this->set_exactdn_option( 'verified', 1, false );
if ( empty( $last_checkin ) ) {
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_success' );
}
return true;
}
} elseif ( ! empty( $result['body'] ) ) {
$response = \json_decode( $result['body'], true );
$error_message = $response['error'];
$this->debug_message( "exactdn verification request failed: $error_message" );
$exactdn_activate_error = $error_message;
if ( false !== \strpos( $error_message, 'not found' ) ) {
\delete_option( $this->prefix . 'exactdn_domain' );
\delete_site_option( $this->prefix . 'exactdn_domain' );
}
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
}
if ( ! empty( $result['response']['code'] ) && 200 !== (int) $result['response']['code'] ) {
$this->debug_message( 'received response code: ' . $result['response']['code'] );
}
\add_action( 'admin_notices', $this->prefix . 'notice_exactdn_activation_error' );
return false;
}
/**
* Run a simulation to decide which verification method to use.
*/
public function check_verify_method() {
if ( ! $this->get_exactdn_option( 'verify_method' ) ) {
$this->debug_message( '' . __METHOD__ . '()' );
// Prelim test with a known valid image to ensure http(s) connectivity.
$sim_url = 'https://optimize.exactdn.com/exactdn/testorig.jpg';
\add_filter( 'http_headers_useragent', $this->prefix . 'cloud_useragent', PHP_INT_MAX );
$sim_result = \wp_remote_get( $sim_url );
if ( \is_wp_error( $sim_result ) ) {
$error_message = $sim_result->get_error_message();
$this->debug_message( "exactdn (simulated) verification request failed: $error_message" );
} elseif ( ! empty( $sim_result['body'] ) && \strlen( $sim_result['body'] ) > 300 ) {
if ( 'ffd8ff' === \bin2hex( \substr( $sim_result['body'], 0, 3 ) ) ) {
$this->debug_message( 'exactdn (simulated) verification succeeded' );
$this->set_exactdn_option( 'verify_method', 1, false );
return;
}
} else {
$this->debug_message( 'exactdn (simulated) verification request failed, error unknown' );
}
$this->set_exactdn_option( 'verify_method', -1, false );
}
}
/**
* Allow external classes/functions to check the Easy IO Plan ID (to customize UI).
*
* @return int The currently validated plan ID (1-3).
*/
public function get_plan_id() {
return (int) $this->plan_id;
}
/**
* Validate the ExactDN domain.
*
* @param string $domain The unverified ExactDN domain.
* @return string The validated ExactDN domain.
*/
public function sanitize_domain( $domain ) {
$this->debug_message( '' . __METHOD__ . '()' );
if ( ! $domain ) {
return;
}
$domain = \trim( $domain );
if ( \strlen( $domain ) > 80 ) {
$this->debug_message( "$domain too long" );
return false;
}
if ( ! \preg_match( '#^[A-Za-z0-9\.\-]+$#', $domain ) ) {
$this->debug_message( "$domain has bad characters" );
return false;
}
return $domain;
}
/**
* Get the ExactDN domain name to use.
*
* @return string The ExactDN domain name for this site or network.
*/
public function get_exactdn_domain() {
if ( \defined( 'EXACTDN_DOMAIN' ) && EXACTDN_DOMAIN ) {
return $this->sanitize_domain( EXACTDN_DOMAIN );
}
if ( \is_multisite() ) {
if ( $this->sub_folder ) {
return $this->sanitize_domain( \get_site_option( $this->prefix . 'exactdn_domain' ) );
}
}
return $this->sanitize_domain( \get_option( $this->prefix . 'exactdn_domain' ) );
}
/**
* Method to override the ExactDN domain at runtime, use with caution.
*
* @param string $domain The ExactDN domain to use instead.
*/
public function set_domain( $domain ) {
if ( \is_string( $domain ) ) {
$this->exactdn_domain = $domain;
}
}
/**
* Get the ExactDN option.
*
* @param string $option_name The name of the ExactDN option.
* @return int The numerical value of the option.
*/
public function get_exactdn_option( $option_name ) {
if ( \defined( 'EXACTDN_DOMAIN' ) && EXACTDN_DOMAIN ) {
return \get_option( $this->prefix . 'exactdn_' . $option_name );
}
if ( \is_multisite() ) {
if ( $this->sub_folder ) {
return \get_site_option( $this->prefix . 'exactdn_' . $option_name );
}
}
return \get_option( $this->prefix . 'exactdn_' . $option_name );
}
/**
* Set the ExactDN domain name to use.
*
* @param string $domain The ExactDN domain name for this site or network.
*/
public function set_exactdn_domain( $domain ) {
$this->debug_message( '' . __METHOD__ . '()' );
if ( \defined( 'EXACTDN_DOMAIN' ) && $this->sanitize_domain( EXACTDN_DOMAIN ) ) {
return true;
}
$domain = $this->sanitize_domain( $domain );
if ( ! $domain ) {
return false;
}
if ( \is_multisite() ) {
if ( $this->sub_folder ) {
\update_site_option( $this->prefix . 'exactdn_domain', $domain );
return $domain;
}
}
\update_option( $this->prefix . 'exactdn_domain', $domain );
return $domain;
}
/**
* Set an option for ExactDN.
*
* @param string $option_name The name of the ExactDN option.
* @param int $option_value The value to set for the ExactDN option.
* @param bool $autoload Optional. Whether to load the option when WordPress starts up.
*/
public function set_exactdn_option( $option_name, $option_value, $autoload = null ) {
$this->debug_message( '' . __METHOD__ . '()' );
if ( \defined( 'EXACTDN_DOMAIN' ) && EXACTDN_DOMAIN ) {
return \update_option( $this->prefix . 'exactdn_' . $option_name, $option_value, $autoload );
}
if ( \is_multisite() ) {
if ( $this->sub_folder ) {
return \update_site_option( $this->prefix . 'exactdn_' . $option_name, $option_value );
}
}
return \update_option( $this->prefix . 'exactdn_' . $option_name, $option_value, $autoload );
}
/**
* Check to see if a CNAME is configured in WP Offload Media.
*
* @return bool True if a CNAME is active, false otherwise.
*/
public function is_as3cf_cname_active() {
// Find the WP Offload Media domain/path.
global $as3cf;
if ( \class_exists( '\Amazon_S3_And_CloudFront' ) && \is_object( $as3cf ) ) {
if ( 'storage' !== $as3cf->get_setting( 'delivery-provider' ) ) {
$this->debug_message( 'active delivery provider: ' . $as3cf->get_setting( 'delivery-provider' ) );
if ( $as3cf->get_setting( 'enable-delivery-domain' ) && $as3cf->get_setting( 'delivery-domain' ) ) {
$delivery_domain = $as3cf->get_setting( 'delivery-domain' );
$this->debug_message( "found WOM CNAME domain: $delivery_domain" );
return true;
}
}
}
return false;
}
/**
* Get the paths for wp-content, wp-includes, and the uploads directory.
* These are used to help determine which URLs are allowed to be rewritten for Easy IO.
*/
public function get_allowed_paths() {
$wp_content_path = \trim( $this->parse_url( \content_url(), PHP_URL_PATH ), '/' );
$wp_include_path = \trim( $this->parse_url( \includes_url(), PHP_URL_PATH ), '/' );
$this->debug_message( "wp-content path: $wp_content_path" );
$this->debug_message( "wp-includes path: $wp_include_path" );
$this->content_path = \wp_basename( $wp_content_path );
$this->include_path = \wp_basename( $wp_include_path );
$this->uploads_path = \wp_basename( $wp_content_path );
// NOTE: $this->uploads_path is not currently in use, so we'll see if anyone needs it.
$uploads_info = \wp_get_upload_dir();
if ( ! empty( $uploads_info['baseurl'] ) && ! empty( $wp_content_path ) && false === \strpos( $uploads_info['baseurl'], $wp_content_path ) ) {
$uploads_path = \trim( $this->parse_url( $uploads_info['baseurl'], PHP_URL_PATH ), '/' );
$this->debug_message( "wp uploads path: $uploads_path" );
$this->uploads_path = \wp_basename( $uploads_path );
}
}
/**
* Validate the user-defined exclusions for "all the things" rewriting.
*/
public function validate_user_exclusions() {
$user_exclusions = $this->get_option( 'exactdn_exclude' );
if ( ! empty( $user_exclusions ) ) {
if ( \is_string( $user_exclusions ) ) {
$user_exclusions = array( $user_exclusions );
}
if ( \is_array( $user_exclusions ) ) {
foreach ( $user_exclusions as $exclusion ) {
if ( ! \is_string( $exclusion ) ) {
continue;
}
$exclusion = \trim( $exclusion );
if ( 0 === \strpos( $exclusion, 'page:' ) ) {
$this->user_page_exclusions[] = \str_replace( 'page:', '', $exclusion );
continue;
}
if ( $this->content_path && false !== \strpos( $exclusion, $this->content_path ) ) {
$exclusion = \preg_replace( '#([^"\'?>]+?)?' . $this->content_path . '/#i', '', $exclusion );
}
$this->user_exclusions[] = \ltrim( $exclusion, '/' );
}
}
}
$this->user_exclusions[] = 'plugins/anti-captcha/';
}
/**
* Parse Elementor content data to check for full_width layouts.
*
* @param array $data The builder content data.
* @return array $data
*/
public function elementor_builder_content_data( $data ) {
$this->debug_message( '' . __METHOD__ . '()' );
if ( $this->is_iterable( $data ) ) {
foreach ( $data as $section_data ) {
if ( ! empty( $section_data['settings']['layout'] ) && 'full_width' === $section_data['settings']['layout'] ) {
$this->debug_message( 'we have a winner (full_width container)!' );
$this->full_width = true;
break;
}
}
}
return $data;
}
/**
* Get $content_width, with a filter.
*
* @return bool|string The content width, if set. Default false.
*/
public function get_content_width() {
$content_width = isset( $GLOBALS['content_width'] ) && \is_numeric( $GLOBALS['content_width'] ) && $GLOBALS['content_width'] > 100 ? $GLOBALS['content_width'] : 1920;
if ( \function_exists( '\twentynineteen_setup' ) && 640 === (int) $content_width ) {
$content_width = 932;
}
if ( \defined( 'EXACTDN_CONTENT_WIDTH' ) && EXACTDN_CONTENT_WIDTH ) {
$content_width = EXACTDN_CONTENT_WIDTH;
} elseif ( $this->full_width ) {
$content_width = 1920;
}
/**
* Filter the Content Width value.
*
* @param string $content_width Content Width value.
*/
return (int) \apply_filters( 'exactdn_content_width', $content_width );
}
/**
* Get width within an ExactDN url.
*
* @param string $url The ExactDN url to parse.
* @return string The width, if found.
*/
public function get_exactdn_width_from_url( $url ) {
$url_args = $this->parse_url( $url, PHP_URL_QUERY );
if ( ! $url_args ) {
return '';
}
$args = \explode( '&', $url_args );
foreach ( $args as $arg ) {
if ( \preg_match( '#w=(\d+)#', $arg, $width_match ) ) {
return $width_match[1];
}
if ( \preg_match( '#resize=(\d+)#', $arg, $width_match ) ) {
return $width_match[1];
}
if ( \preg_match( '#fit=(\d+)#', $arg, $width_match ) ) {
return $width_match[1];
}
}
return '';
}
/**
* Identify images in page content, and if images are local (uploaded to the current site), pass through ExactDN.
*
* @param string $content The page/post content.
* @return string The content with ExactDN image urls.
*/
public function filter_page_output( $content ) {
$this->debug_message( '' . __METHOD__ . '()' );
$this->filtering_the_page = true;
$content = $this->filter_the_content( $content );
/**
* Allow parsing the full page content after ExactDN is finished with it.
*
* @param string $content The fully-parsed HTML code of the page.
*/
$content = \apply_filters( 'exactdn_the_page', $content );
$this->filtering_the_page = false;
$this->debug_message( "parsing page took $this->elapsed_time seconds" );
return $content;
}
/**
* Identify images in the content, and if images are local (uploaded to the current site), pass through ExactDN.
*
* @param string $content The page/post content.
* @return string The content with ExactDN image urls.
*/
public function filter_the_content( $content ) {
if ( $this->is_json( $content ) ) {
return $content;
}
if ( \apply_filters( 'exactdn_skip_page', false, $this->request_uri ) ) {
return $content;
}
$started = \microtime( true );
$this->debug_message( '' . __METHOD__ . '()' );
$images = $this->get_images_from_html( $content, true );
if ( ! empty( $images ) ) {
$this->debug_message( 'we have images to parse' );
if ( false !== \strpos( $content, 'elementor-section-full_width' ) ) {
$this->debug_message( 'elementor full_width section found!' );
$this->full_width = true;
}
$content_width = false;
if ( ! $this->filtering_the_page ) {
$this->filtering_the_content = true;
$this->debug_message( 'filtering the content' );
$content_width = $this->get_content_width();
$this->debug_message( "configured/filtered content_width: $content_width" );
}
$resize_existing = \defined( 'EXACTDN_RESIZE_EXISTING' ) && EXACTDN_RESIZE_EXISTING;
$image_sizes = $this->image_sizes();
foreach ( $images[0] as $index => $tag ) {
// Default to resize, though fit may be used in certain cases where a dimension cannot be ascertained.
$transform = 'resize';
// Start with a clean slate each time.
$attachment_id = false;
$exactdn_url = false;
$width = false;
$lazy = false;
$srcset_fill = false;
// Flag if we need to munge a fullsize URL.
$fullsize_url = false;
// Identify image source.
$src = \trim( $images['img_url'][ $index ] );
$src_orig = $images['img_url'][ $index ]; // Don't trim, because we'll use it for search/replacement later.
if ( \is_string( $src ) ) {
$this->debug_message( $src );
} else {
$this->debug_message( '$src is not a string?' );
}
/**
* Allow specific images to be skipped by ExactDN.
*
* @param bool false Should ExactDN ignore this image. Default false.
* @param string $src Image URL.
* @param string $tag Image HTML Tag.
*/
if ( \apply_filters( 'exactdn_skip_image', false, $src, $tag ) ) {
continue;
}
$this->debug_message( 'made it passed the filters' );
// Log 0-size Pinterest schema images.
if ( \strpos( $tag, 'data-pin-description=' ) && \strpos( $tag, 'width="0" height="0"' ) ) {
$this->debug_message( 'data-pin/Pinterest image' );
}
// Pre-empt srcset fill if the surrounding link has a background image or if there is a data-desktop attribute indicating a potential slider.
if ( \strpos( $tag, 'background-image:' ) || \strpos( $tag, 'data-desktop=' ) ) {
$srcset_fill = false;
}
/**
* Documented in generate_url, in this case used to detect images that should bypass srcset fill.
*
* @param array|string $args Array of ExactDN arguments.
* @param string $image_url Image URL.
* @param string|null $scheme Image scheme. Default to null.
*/
$args = \apply_filters( 'exactdn_pre_args', array( 'test' => 'lazy-test' ), $src, null );
if ( empty( $args ) ) {
$srcset_fill = false;
}
// Support Lazy Load plugins.
// Don't modify $tag yet as we need unmodified version later.
$lazy_load_src = \trim( $this->get_attribute( $images['img_tag'][ $index ], 'data-lazy-src' ) );
if ( $lazy_load_src ) {
$placeholder_src = $src;
$placeholder_src_orig = $src;
$src = $lazy_load_src;
$src_orig = $lazy_load_src;
$this->srcset_attr = 'data-lazy-srcset';
$lazy = true;
$srcset_fill = true;
}
// Must be a legacy Jetpack thing as far as I can tell, no matches found in any currently installed plugins.
$lazy_load_src = \trim( $this->get_attribute( $images['img_tag'][ $index ], 'data-lazy-original' ) );
if ( ! $lazy && $lazy_load_src ) {
$placeholder_src = $src;
$placeholder_src_orig = $src;
$src = $lazy_load_src;
$src_orig = $lazy_load_src;
$lazy = true;
}
if ( ! $lazy && \strpos( $images['img_tag'][ $index ], 'a3-lazy-load/assets/images/lazy_placeholder' ) ) {
$lazy_load_src = \trim( $this->get_attribute( $images['img_tag'][ $index ], 'data-src' ) );
}
if (
! $lazy &&
\strpos( $images['img_tag'][ $index ], ' data-src=' ) &&
\strpos( $images['img_tag'][ $index ], 'lazyload' ) &&
(
\strpos( $images['img_tag'][ $index ], 'data:image/gif' ) ||
\strpos( $images['img_tag'][ $index ], 'data:image/svg' )
)
) {
$lazy_load_src = $this->get_attribute( $images['img_tag'][ $index ], 'data-src' );
$this->debug_message( "found eio ll src: $lazy_load_src" );
}
if ( ! $lazy && $lazy_load_src ) {
$placeholder_src = $src;
$placeholder_src_orig = $src;
$src = $lazy_load_src;
$src_orig = $lazy_load_src;
$this->srcset_attr = 'data-srcset';
$lazy = true;
$srcset_fill = true;
}
if ( ! $lazy && \strpos( $images['img_tag'][ $index ], 'revslider/admin/assets/images/dummy' ) ) {
$lazy_load_src = \trim( $this->get_attribute( $images['img_tag'][ $index ], 'data-lazyload' ) );
}
if ( ! $lazy && $lazy_load_src ) {
$placeholder_src = $src;
$placeholder_src_orig = $src;
$src = $lazy_load_src;
$src_orig = $lazy_load_src;
$lazy = true;
}
if ( $lazy ) {
$this->debug_message( 'handling lazy image' );
}
$is_relative = false;
// Check for relative URLs that start with a slash.
if (
'/' === \substr( $src, 0, 1 ) &&
'/' !== \substr( $src, 1, 1 ) &&
false === \strpos( $this->upload_domain, 'amazonaws.com' ) &&
false === \strpos( $this->upload_domain, 'digitaloceanspaces.com' ) &&
false === \strpos( $this->upload_domain, 'storage.googleapis.com' )
) {
$src = '//' . $this->upload_domain . $src;
$is_relative = true;
}
// Check if image URL should be used with ExactDN.
if ( $this->validate_image_url( $src ) ) {
$this->debug_message( 'url validated' );
// Find the width and height attributes.
$width = $this->get_attribute( $images['img_tag'][ $index ], 'width' );
$height = $this->get_attribute( $images['img_tag'][ $index ], 'height' );
// Can't pass both a relative width and height, so unset the dimensions in favor of not breaking the horizontal layout.
if ( false !== \strpos( $width, '%' ) ) {
$width = false;
}
if ( false !== \strpos( $height, '%' ) ) {
$height = false;
}
// Falsify them if empty.
$width = $width && \is_numeric( $width ) ? $width : false;
$height = $height && \is_numeric( $height ) ? $height : false;
// Get width/height attributes from the URL/file if they are missing.
$insert_dimensions = false;
if ( \apply_filters( 'eio_add_missing_width_height_attrs', $this->get_option( $this->prefix . 'add_missing_dims' ) ) && ( empty( $width ) || empty( $height ) ) ) {
$this->debug_message( 'missing width attr or height attr' );
$insert_dimensions = true;
}
// See if there is a width/height set in the style attribute.
$style_width = $this->get_img_style_width( $images['img_tag'][ $index ] );
$style_height = $this->get_img_style_height( $images['img_tag'][ $index ] );
if ( $style_width && $style_height ) {
$width = \min( $style_width, $width );
$height = \min( $style_height, $height );
} elseif ( $style_width && $style_width < $width ) {
$width = $style_width;
$transform = 'fit';
} elseif ( $style_height && $style_height < $height ) {
$height = $style_height;
$transform = 'fit';
}
// Detect WP registered image size from HTML class.
if ( \preg_match( '#class=["|\']?[^"\']*size-([^"\'\s]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $size ) ) {
$size = \array_pop( $size );
$this->debug_message( "detected $size" );
if ( false === $width && false === $height && 'full' !== $size && \array_key_exists( $size, $image_sizes ) ) {
$width = (int) $image_sizes[ $size ]['width'];
$height = (int) $image_sizes[ $size ]['height'];
$transform = $image_sizes[ $size ]['crop'] ? 'resize' : 'fit';
}
} else {
unset( $size );
}
list( $filename_width, $filename_height ) = $this->get_dimensions_from_filename( $src );
if ( false === $width && false === $height ) {
$width = $filename_width;
$height = $filename_height;
}
// WP Attachment ID, if uploaded to this site.
$attachment_id = $this->get_attribute( $images['img_tag'][ $index ], 'data-id' );
if ( empty( $attachment_id ) ) {
$this->debug_message( 'data-id not found, looking for wp-image-x in class' );
\preg_match( '#class=["|\']?[^"\']*wp-image-([\d]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $attachment_id );
}
if ( ! $this->get_option( 'exactdn_prevent_db_queries' ) && empty( $attachment_id ) ) {
$this->debug_message( 'looking for attachment id' );
$attachment_id = attachment_url_to_postid( $src );
}
if ( ! $this->get_option( 'exactdn_prevent_db_queries' ) && ! empty( $attachment_id ) ) {
if ( \is_array( $attachment_id ) ) {
$attachment_id = \intval( \array_pop( $attachment_id ) );
}
$this->debug_message( "using attachment id ($attachment_id) to get source image" );
if ( $attachment_id ) {
$this->debug_message( "detected attachment $attachment_id" );
$attachment = get_post( $attachment_id );
// Basic check on returned post object.
if ( \is_object( $attachment ) && ! \is_wp_error( $attachment ) && 'attachment' === $attachment->post_type ) {
$src_per_wp = \wp_get_attachment_image_src( $attachment_id, 'full' );
if ( $src_per_wp && \is_array( $src_per_wp ) ) {
$this->debug_message( "src retrieved from db: {$src_per_wp[0]}, checking for match" );
$fullsize_url_path = $this->parse_url( $src_per_wp[0], PHP_URL_PATH );
if ( \is_null( $fullsize_url_path ) ) {
$src_per_wp = false;
} elseif ( $fullsize_url_path ) {
$fullsize_url_basename = \pathinfo( $fullsize_url_path, PATHINFO_FILENAME );
$this->debug_message( "looking for $fullsize_url_basename in $src" );
if ( \strpos( \wp_basename( $src ), $fullsize_url_basename ) === false ) {
$this->debug_message( 'fullsize url does not match' );
$src_per_wp = false;
}
} else {
$src_per_wp = false;
}
}
if ( $src_per_wp && $this->validate_image_url( $src_per_wp[0] ) ) {
$this->debug_message( "detected $width filenamew $filename_width" );
if ( $resize_existing || ( $width && (int) $filename_width !== (int) $width ) ) {
$this->debug_message( 'resizing existing or width does not match' );
$src = $src_per_wp[0];
}
$fullsize_url = true;
// Prevent image distortion if a detected dimension exceeds the image's natural dimensions.
if ( ( false !== $width && $width > $src_per_wp[1] ) || ( false !== $height && $height > $src_per_wp[2] ) ) {
$width = false === $width ? false : min( $width, $src_per_wp[1] );
$height = false === $height ? false : min( $height, $src_per_wp[2] );
$this->debug_message( "constrained to attachment dims, w=$width and h=$height" );
}
// If no width and height are found, max out at source image's natural dimensions.
// Otherwise, respect registered image sizes' cropping setting.
if ( false === $width && false === $height ) {
$width = $src_per_wp[1];
$height = $src_per_wp[2];
$transform = 'fit';
$this->debug_message( "no dims, using attachment dims, w=$width and h=$height" );
} elseif ( isset( $size ) && \array_key_exists( $size, $image_sizes ) && isset( $image_sizes[ $size ]['crop'] ) ) {
$transform = (bool) $image_sizes[ $size ]['crop'] ? 'resize' : 'fit';
$this->debug_message( 'attachment size set to crop' );
}
}
} else {
unset( $attachment_id );
unset( $attachment );
}
}
}
$constrain_width = (int) $content_width;
if ( ! empty( $images['figure_class'][ $index ] ) && false !== \strpos( $images['figure_class'][ $index ], 'alignfull' ) && \current_theme_supports( 'align-wide' ) ) {
$constrain_width = (int) \apply_filters( 'exactdn_full_align_image_width', \max( 1920, $content_width ) );
} elseif ( ! empty( $images['figure_class'][ $index ] ) && false !== \strpos( $images['figure_class'][ $index ], 'alignwide' ) && \current_theme_supports( 'align-wide' ) ) {
$constrain_width = (int) \apply_filters( 'exactdn_wide_align_image_width', \max( 1500, $content_width ) );
}
if ( ! empty( $images['div_class'][ $index ] ) && false !== \strpos( $images['div_class'][ $index ], 'alignfull' ) && \current_theme_supports( 'align-wide' ) ) {
$constrain_width = (int) \apply_filters( 'exactdn_full_align_image_width', \max( 1920, $content_width ) );
} elseif ( ! empty( $images['div_class'][ $index ] ) && false !== \strpos( $images['div_class'][ $index ], 'alignwide' ) && \current_theme_supports( 'align-wide' ) ) {
$constrain_width = (int) \apply_filters( 'exactdn_wide_align_image_width', \max( 1500, $content_width ) );
}
// If width is available, constrain to $content_width.
if ( false !== $width && false === \strpos( $width, '%' ) && \is_numeric( $constrain_width ) ) {
if ( $width > $constrain_width && false !== $height && false === \strpos( $height, '%' ) ) {
$this->debug_message( 'constraining to content width' );
$height = \round( ( $constrain_width * $height ) / $width );
$width = $constrain_width;
} elseif ( $width > $constrain_width ) {
$this->debug_message( 'constraining to content width' );
$width = $constrain_width;
}
}
// Set a width if none is found and $content_width is available.
// If width is set in this manner and height is available, use `fit` instead of `resize` to prevent skewing.
if ( false === $width && \is_numeric( $constrain_width ) ) {
$width = (int) $constrain_width;
if ( false !== $height ) {
$transform = 'fit';
}
}
// Override fit by class/id/attr 'img-crop'.
if ( 'fit' === $transform && \strpos( $images['img_tag'][ $index ], 'img-crop' ) ) {
$transform = 'resize';
}
// Detect if image source is for a custom-cropped thumbnail and prevent further URL manipulation.
if ( ! $fullsize_url && \preg_match_all( '#-e[a-z0-9]+(-\d+x\d+)?\.(' . \implode( '|', $this->extensions ) . '){1}$#i', \wp_basename( $src ), $filename ) ) {
$fullsize_url = true;
}
// Build array of ExactDN args and expose to filter before passing to ExactDN URL function.
$args = array();
if ( false !== $width && false !== $height && false === \strpos( $width, '%' ) && false === \strpos( $height, '%' ) ) {
$args[ $transform ] = $width . ',' . $height;
} elseif ( false !== $width ) {
$args['w'] = $width;
} elseif ( false !== $height ) {
$args['h'] = $height;
}
if ( ! $resize_existing && ( ! $width || (int) $filename_width === (int) $width ) ) {
$this->debug_message( 'preventing resize' );
$args = array();
} elseif ( ! $fullsize_url ) {
// Build URL, first maybe removing WP's resized string so we pass the original image to ExactDN (for higher quality).
$src = $this->strip_image_dimensions_maybe( $src );
}
if ( ! $this->get_option( 'exactdn_prevent_db_queries' ) && ! empty( $attachment_id ) ) {
$this->debug_message( 'using attachment id to check smart crop' );
$args = $this->maybe_smart_crop( $args, $attachment_id );
}
/**
* Filter the array of ExactDN arguments added to an image.
* By default, only includes width and height values.
*
* @param array $args Array of ExactDN Arguments.
* @param array $args {
* Array of image details.
*
* @type $tag Image tag (Image HTML output).
* @type $src Image URL.
* @type $src_orig Original Image URL.
* @type $width Image width.
* @type $height Image height.
* }
*/
$args = \apply_filters( 'exactdn_post_image_args', $args, \compact( 'tag', 'src', 'src_orig', 'width', 'height' ) );
$this->debug_message( "width $width" );
$this->debug_message( "height $height" );
$this->debug_message( "transform $transform" );
$exactdn_url = $this->generate_url( $src, $args );
$this->debug_message( "new url $exactdn_url" );
// Modify image tag if ExactDN function provides a URL
// Ensure changes are only applied to the current image by copying and modifying the matched tag, then replacing the entire tag with our modified version.
if ( $src !== $exactdn_url ) {
$new_tag = $tag;
// If present, replace the link href with an ExactDN URL for the full-size image.
if ( \defined( 'EIO_PRESERVE_LINKED_IMAGES' ) && EIO_PRESERVE_LINKED_IMAGES && ! empty( $images['link_url'][ $index ] ) && $this->validate_image_url( $images['link_url'][ $index ] ) ) {
$new_tag = \preg_replace(
'#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i',
'\1' . $this->generate_url(
$images['link_url'][ $index ],
array(
'lossy' => 0,
'strip' => 'none',
)
) . '\2',
$new_tag,
1
);
} elseif ( ! empty( $images['link_url'][ $index ] ) && $this->validate_image_url( $images['link_url'][ $index ] ) ) {
$new_tag = \preg_replace(
'#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i',
'\1' . $this->generate_url( $images['link_url'][ $index ], array( 'w' => 2560 ) ) . '\2',
$new_tag,
1
);
}
$srcset_url = false;
// Insert new image src into the srcset as well, if we have a width.
if ( false !== $width && false === \strpos( $width, '%' ) && $width ) {
$srcset_url = $exactdn_url . ' ' . (int) $width . 'w, ';
}
$srcset_attr = $this->get_attribute( $new_tag, $this->srcset_attr );
if ( $srcset_attr ) {
$new_srcset_attr = $srcset_attr;
if ( $srcset_url && false === \strpos( $srcset_attr, ' ' . (int) $width . 'w' ) && ! \preg_match( '/\s(1|2|3)x/', $srcset_attr ) ) {
$this->debug_message( 'src not in srcset, adding' );
$new_srcset_attr = $srcset_url . $new_srcset_attr;
}
$new_srcset_attr = $this->srcset_replace( $new_srcset_attr );
if ( $new_srcset_attr && $new_srcset_attr !== $srcset_attr ) {
$this->set_attribute( $new_tag, $this->srcset_attr, $new_srcset_attr, true );
}
}
// Check if content width pushed the respimg sizes attribute too far down.
if ( ! empty( $constrain_width ) && (int) $constrain_width !== (int) $content_width ) {
$sizes_attr = $this->get_attribute( $new_tag, 'sizes' );
$new_sizes_attr = \str_replace( ' ' . $content_width . 'px', ' ' . $constrain_width . 'px', $sizes_attr );
if ( $sizes_attr !== $new_sizes_attr ) {
$new_tag = \str_replace( $sizes_attr, $new_sizes_attr, $new_tag );
}
}
// Cleanup ExactDN URL.
$exactdn_url = \str_replace( '&', '&', \esc_url( \trim( $exactdn_url ) ) );
// Supplant the original source value with our ExactDN URL.
$this->debug_message( "replacing $src_orig with $exactdn_url" );
if ( $is_relative ) {
$this->set_attribute( $new_tag, 'src', $exactdn_url, true );
} else {
$new_tag = \str_replace( $src_orig, $exactdn_url, $new_tag );
}
// If Lazy Load is in use, pass placeholder image through ExactDN.
if ( isset( $placeholder_src ) && $this->validate_image_url( $placeholder_src ) ) {
$placeholder_src = $this->generate_url( $placeholder_src );
if ( $placeholder_src !== $placeholder_src_orig ) {
$new_tag = \str_replace( $placeholder_src_orig, \str_replace( '&', '&', \esc_url( \trim( $placeholder_src ) ) ), $new_tag );
}
unset( $placeholder_src );
}
if ( $insert_dimensions && $filename_width > 0 && $filename_height > 0 ) {
$this->debug_message( "filling in width = $filename_width and height = $filename_height" );
$this->set_attribute( $new_tag, 'width', $filename_width, true );
$this->set_attribute( $new_tag, 'height', $filename_height, true );
}
// Replace original tag with modified version.
$content = \str_replace( $tag, $new_tag, $content );
}
} elseif ( ! $lazy && $this->validate_image_url( $src, true ) ) {
$this->debug_message( "found a potential exactdn src url to wrangle, and maybe insert into srcset: $src" );
$args = array();
$new_tag = $tag;
$width = $this->get_attribute( $images['img_tag'][ $index ], 'width' );
$height = $this->get_attribute( $images['img_tag'][ $index ], 'height' );
// Making sure the width/height are numeric.
if ( false === \strpos( $new_tag, 'srcset' ) && \strpos( $src, '?' ) && (int) $width > 2 && (int) $height > 2 ) {
$url_params = \urldecode( $this->parse_url( $src, PHP_URL_QUERY ) );
if ( $url_params && false !== \strpos( $url_params, 'resize=' ) ) {
$this->debug_message( 'existing resize param' );
} elseif ( $url_params && false !== \strpos( $url_params, 'fit=' ) ) {
$this->debug_message( 'existing fit param' );
} elseif ( $url_params && false === \strpos( $url_params, 'w=' ) && false === \strpos( $url_params, 'h=' ) && false === \strpos( $url_params, 'crop=' ) ) {
$this->debug_message( 'no size params, so add the width/height' );
$args = array();
$transform = 'fit';
// Or optionally as crop/resize.
if ( \strpos( $new_tag, 'img-crop' ) ) {
$transform = 'resize';
}
$args[ $transform ] = $width . ',' . $height;
}
}
if ( $args ) {
$args = \apply_filters( 'exactdn_post_image_args', $args, \compact( 'new_tag', 'src', 'src', 'width', 'height' ) );
$new_src = $this->generate_url( $src, $args );
if ( $new_src && $src !== $new_src ) {
$new_tag = \str_replace( $src, $new_src, $new_tag );
}
}
$srcset_url = false;
if ( $width ) {
$this->debug_message( 'found the width' );
// Insert new image src into the srcset as well, if we have a width.
if (
false !== $width &&
false === \strpos( $width, '%' ) &&
false !== \strpos( $src, $width ) &&
false !== \strpos( $src, $this->exactdn_domain )
) {
$exactdn_url = $src;
$srcset_url = $exactdn_url . ' ' . (int) $width . 'w, ';
}
}
$srcset_attr = $this->get_attribute( $new_tag, $this->srcset_attr );
if ( $srcset_attr ) {
$new_srcset_attr = $srcset_attr;
if ( $srcset_url && false === \strpos( $srcset_attr, ' ' . (int) $width . 'w' ) && ! \preg_match( '/\s(1|2|3)x/', $srcset_attr ) ) {
$this->debug_message( 'src not in srcset, adding' );
$new_srcset_attr = $srcset_url . $new_srcset_attr;
}
$new_srcset_attr = $this->srcset_replace( $new_srcset_attr );
if ( $new_srcset_attr && $new_srcset_attr !== $srcset_attr ) {
$this->set_attribute( $new_tag, $this->srcset_attr, $new_srcset_attr, true );
}
}
if ( $new_tag && $new_tag !== $tag ) {
// Replace original tag with modified version.
$content = \str_replace( $tag, $new_tag, $content );
}
} elseif ( $lazy && ! empty( $placeholder_src ) && $this->validate_image_url( $placeholder_src ) ) {
$this->debug_message( "parsing $placeholder_src for $src" );
$new_tag = $tag;
// If Lazy Load is in use, pass placeholder image through ExactDN.
$placeholder_src = $this->generate_url( $placeholder_src );
if ( $placeholder_src !== $placeholder_src_orig ) {
$new_tag = \str_replace( $placeholder_src_orig, \str_replace( '&', '&', \esc_url( \trim( $placeholder_src ) ) ), $new_tag );
// Replace original tag with modified version.
$content = \str_replace( $tag, $new_tag, $content );
}
unset( $placeholder_src );
} else {
$this->debug_message( "unparsed $src, srcset fill coming up" );
} // End if().
// At this point, we discard the original src in favor of the ExactDN url.
if ( ! empty( $exactdn_url ) ) {
$src = $exactdn_url;
}
// This is disabled by default, not much reason to do this with the lazy loader auto-scaling.
if ( ! \is_feed() && $srcset_fill && \defined( 'EIO_SRCSET_FILL' ) && EIO_SRCSET_FILL && false !== \strpos( $src, $this->exactdn_domain ) ) {
if ( ! $this->get_attribute( $images['img_tag'][ $index ], $this->srcset_attr ) && ! $this->get_attribute( $images['img_tag'][ $index ], 'sizes' ) ) {
$this->debug_message( "srcset filling with $src" );
$zoom = false;
// If $width is empty, we'll search the url for a width param, then we try searching the img element, with fall back to the filename.
if ( empty( $width ) || ! \is_numeric( $width ) ) {
// This only searches for w, resize, or fit flags, others are ignored.
$width = $this->get_exactdn_width_from_url( $src );
if ( $width ) {
$zoom = true;
}
}
$width_attr = $this->get_attribute( $images['img_tag'][ $index ], 'width' );
// Get width/height attributes from the URL/file if they are missing.
$insert_dimensions = false;
if ( \apply_filters( 'eio_add_missing_width_height_attrs', $this->get_option( $this->prefix . 'add_missing_dims' ) ) && empty( $width_attr ) ) {
$this->debug_message( 'missing width attr or height attr' );
$insert_dimensions = true;
}
if ( empty( $width ) || ! \is_numeric( $width ) ) {
$width = $width_attr;
}
list( $filename_width, $filename_height ) = $this->get_dimensions_from_filename( $src );
if ( empty( $width ) || ! \is_numeric( $width ) ) {
$width = $filename_width;
}
if ( empty( $width ) || ! \is_numeric( $width ) ) {
$width = $this->get_attribute( $images['img_tag'][ $index ], 'data-actual-width' );
}
if ( false !== \strpos( $src, 'crop=' ) || false !== \strpos( $src, '&h=' ) || false !== \strpos( $src, '?h=' ) ) {
$width = false;
}
$new_tag = $images['img_tag'][ $index ];
// Then add a srcset and sizes.
if ( $width ) {
$srcset = $this->generate_image_srcset( $src, $width, $zoom, $filename_width );
if ( $srcset ) {
$this->set_attribute( $new_tag, $this->srcset_attr, $srcset );
$this->set_attribute( $new_tag, 'sizes', \sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $width ) );
}
}
if ( $insert_dimensions && $filename_width > 0 && $filename_height > 0 ) {
$this->debug_message( "filling in width = $filename_width and height = $filename_height" );
$this->set_attribute( $new_tag, 'width', $filename_width, true );
$this->set_attribute( $new_tag, 'height', $filename_height, true );
}
if ( $new_tag !== $images['img_tag'][ $index ] ) {
// Replace original tag with modified version.
$content = \str_replace( $images['img_tag'][ $index ], $new_tag, $content );
}
} // End if() -- no srcset or sizes attributes found.
} // End if() -- not a feed and EIO_SRCSET_FILL enabled.
} // End foreach() -- of all images found in the page.
} // End if() -- we found images in the page at all.
// Process elements in the page for image URLs.
$content = $this->filter_image_links( $content );
// Process