Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion inc/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,9 @@ public static function parse_images_from_html( $content ) {
if ( OPTML_DEBUG ) {
do_action( 'optml_log', 'Content to parse images from: ' . $content );
}
$regex = '/(?:<a[^>]+?href=["|\'](?P<link_url>[^\s]+?)["|\'][^>]*?>\s*)?(?P<img_tag>(?:<noscript\s*>\s*)?<img[^>]*?\s?(?:' . implode( '|', array_merge( [ 'src' ], Optml_Tag_Replacer::possible_src_attributes() ) ) . ')=["\'\\\\]*?(?P<img_url>[' . Optml_Config::$chars . ']{10,}).*?>(?:\s*<\/noscript\s*>)?){1}(?:\s*<\/a>)?/ismu';
// A lazy-load attribute (e.g. data-src) holding a data: placeholder is skipped, so the real src is used.
// A data: src is still matched: the image then belongs to another lazy-load script.
$regex = '/(?:<a[^>]+?href=["|\'](?P<link_url>[^\s]+?)["|\'][^>]*?>\s*)?(?P<img_tag>(?:<noscript\s*>\s*)?<img[^>]*?(?:\ssrc=["\'\\\\]*?|\s?(?:' . implode( '|', array_merge( [ 'src' ], Optml_Tag_Replacer::possible_src_attributes() ) ) . ')=["\'\\\\]*?(?!data:))(?P<img_url>[' . Optml_Config::$chars . ']{10,}).*?>(?:\s*<\/noscript\s*>)?){1}(?:\s*<\/a>)?/ismu';
Comment thread
pirate-bot marked this conversation as resolved.

if ( preg_match_all( $regex, $content, $images, PREG_OFFSET_CAPTURE ) ) {
if ( OPTML_DEBUG ) {
Expand Down
11 changes: 11 additions & 0 deletions tests/test-dimension-replacement.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ public function test_existing_attributes_preserved() {
}


/**
* A data: placeholder in data-src before src does not stop the real image from being processed.
*/
public function test_dimensions_injected_when_data_src_placeholder_comes_first() {
$html_input = '<img data-src="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" src="' . self::IMAGE_URL . '" alt="Test image">';

$result = $this->tag_replacer->process_image_tags( $html_input, $this->extract_images_from_html( $html_input ) );

$this->assertMatchesRegularExpression( '/width="1920".*height="1080".*data-opt-id=' . $this->tag_replacer->get_id_by_url( self::IMAGE_URL ) . '/', $result );
}

/**
* Helper method to extract images from HTML for testing.
* Uses the actual manager method to ensure correct structure.
Expand Down
30 changes: 30 additions & 0 deletions tests/test-lazyload.php
Original file line number Diff line number Diff line change
Expand Up @@ -553,4 +553,34 @@ public function test_dam_lazyloading_no_wh_attributes() {
$this->assertStringContainsString( $svg, $replaced_content );
remove_filter('optml_lazyload_images_skip','__return_zero');
}

/**
* A tag whose data-src holds a data: placeholder is lazy-loaded from its real URL.
*/
public function test_lazyload_tag_with_data_uri_data_src() {
$settings = new Optml_Settings();
$settings->update( 'skip_lazyload_images', 0 );
// The skip limit is cached in a static on first use, so an earlier test may have cached another value.
$skip_limit = new ReflectionProperty( Optml_Lazyload_Replacer::class, 'skip_lazyload_images' );
$skip_limit->setAccessible( true );
$skip_limit->setValue( null, null );
Optml_Tag_Replacer::$lazyload_skipped_images = 0;
Optml_Manager::instance()->init();

$placeholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
$tags = [
'<img data-src="' . $placeholder . '" src="https://example.com/wp-content/uploads/2026/10/photo.jpg" alt="Test" />',
'<img data-src="' . $placeholder . '" data-lazy-src="https://example.com/wp-content/uploads/2026/10/photo.jpg" alt="Test" />',
];

foreach ( $tags as $tag ) {
$replaced_content = Optml_Manager::instance()->replace_content( $tag );

$this->assertStringContainsString( 'data-opt-src="https://test123.i.optimole.com/', $replaced_content, $tag );
$this->assertStringContainsString( '/https://example.com/wp-content/uploads/2026/10/photo.jpg"', $replaced_content, $tag );
$this->assertStringContainsString( 'data-src="' . $placeholder . '"', $replaced_content, $tag );
}

$skip_limit->setValue( null, null );
}
}
38 changes: 38 additions & 0 deletions tests/test-replacer.php
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,44 @@ public function test_dam_flag_removal() {
$this->assertStringContainsString('q:mauto', $replaced_content);
}

/**
* A data: placeholder in a lazy-load attribute before src is not taken as the image URL.
*/
public function test_parse_images_skips_data_uri_in_lazy_attribute() {
$placeholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
$real_src = 'https://example.com/wp-content/uploads/2026/10/photo.jpg';

$tags = [
'<img data-src="' . $placeholder . '" src="' . $real_src . '" alt="Test" />',
"<img data-src='" . $placeholder . "' src='" . $real_src . "' alt='Test' />",
'<img data-lazy-src="' . $placeholder . '" alt="Test" src="' . $real_src . '" />',
'<img src="' . $real_src . '" data-src="' . $placeholder . '" alt="Test" />',
'<img DATA-SRC="DATA:image/gif;base64,R0lGODlh" src="' . $real_src . '" />',
];

foreach ( $tags as $tag ) {
$images = Optml_Manager::parse_images_from_html( $tag );
$this->assertSame( [ $real_src ], $images['img_url'], $tag );
$this->assertSame( [ $tag ], $images['img_tag'], $tag );
Comment thread
pirate-bot marked this conversation as resolved.
}

$placeholder_only = '<img data-src="' . $placeholder . '" alt="Test" />';
$this->assertSame( [], Optml_Manager::parse_images_from_html( $placeholder_only ) );

$placeholder_and_lazy_src = '<img data-src="' . $placeholder . '" data-lazy-src="' . $real_src . '" alt="Test" />';
$this->assertSame( [ $real_src ], Optml_Manager::parse_images_from_html( $placeholder_and_lazy_src )['img_url'] );
}

/**
* A data: src is still matched, so an image owned by another lazy-load script is handled as before.
*/
public function test_parse_images_keeps_data_uri_src() {
$tag = '<img src="data:image/gif;base64,R0lGODdhAQABAPAAAP///wAAACwAAAAAAQABAEACAkQBADs=" data-lazy-src="https://example.com/wp-content/uploads/2026/10/photo.jpg" />';
$images = Optml_Manager::parse_images_from_html( $tag );

$this->assertSame( [ 'data:image/gif' ], $images['img_url'] );
}

/**
* Individual cache tokens are stored as the API returned them, which can be a number.
* The SDK only accepts a string cache buster.
Expand Down
Loading