Page profiler never treats the detected LCP image as LCP, so it gets no fetchpriority="high" (stored ID is a string, compared with === to an int)
Los mantenedores suelen responder en 1 día
@girishpanchal30 ya está trabajando en esto.
Desde el 29/10/2026.
- #1213 de @girishpanchal30 — abierto
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 88/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- javascript, php
- Área
- backend, performance
Línea de trabajo
Comienza en inc/rest.php alrededor de la línea 1032, donde el imageId del LCP se almacena mediante sanitize_text_field(), y luego inspecciona Profile.php:385-393, donde is_lcp_image_in_all_viewports() usa === estricto. Reproduce con el fragmento de WP-CLI o la prueba existente test_lazyload_viewport_with_lcp_data, que actualmente omite la ruta REST. Terminado significa que el ID almacenado es un int (o se convierte antes de la comparación), el método devuelve true para ambos perfiles de viewport, y la rama LCP de tag_replacer.php añade fetchpriority="high".
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Description
The page profiler detects the LCP image in the browser and sends its data-opt-id to /optimizations as an integer. The REST handler stores it with sanitize_text_field(), which always returns a string. Profile::is_lcp_image_in_all_viewports( int $image_id ) then compares that string to the int ID with ===, so it is always false.
Result, with the default "fixed|viewport" lazy load type and profile data stored for both devices:
- Optimole never adds
fetchpriority="high"to the LCP image (tag_replacer.phpLCP branch is dead, and the fixed-count fallback is skipped because profile data exists). - If the LCP image is not in the above-the-fold list for both devices, it is lazy-loaded anyway (and any
fetchpriority="high"on it is stripped).
Expected: the stored LCP image gets fetchpriority="high" and is never lazy-loaded. Likely fix: store absint( $lcp_data['i'] ?? 0 ) in inc/rest.php (or cast before comparing in Profile.php). Existing profiles in transients/object cache keep the string until they expire, so casting at compare time also covers those.
The unit test test_lazyload_viewport_with_lcp_data stores imageId as an int directly through Profile::store(), so it does not cover the REST path.
Step-by-step reproduction instructions
- On a test site with Optimole 4.2.17 connected and default Lazy Load settings, run this with WP-CLI (it stores LCP data the same way
inc/rest.php:1032does):wp eval ' $p = new OptimoleWP\PageProfiler\Profile(); $img = 1578474861; $lcp = [ "imageId" => sanitize_text_field( $img ), "type" => "img" ]; foreach ( [ 1, 2 ] as $d ) { $p->store( "bugcheck", $d, [], [], $lcp ); } OptimoleWP\PageProfiler\Profile::set_current_profile_id( "bugcheck" ); $p->set_current_profile_data(); var_dump( $p->is_lcp_image_in_all_viewports( $img ) ); ' - Expected
bool(true). Actualbool(false). - Clean up:
wp transient delete _oprof_bugcheck_1 && wp transient delete _oprof_bugcheck_2(or flush theoptimole_page_profilercache group if an object cache is used).
End to end check on a real page: open a page with a large hero image once on desktop and once in mobile emulation so both profiles are stored, then reload and view source. The hero <img data-opt-id=...> has no fetchpriority="high" added by Optimole, and wp transient get _oprof_<profile id>_2 --format=json shows "imageId":"<id>" as a string.
Pure PHP reproduction of the same path (json_decode of the REST body, core _sanitize_text_fields cast, transient serialize round trip, the Profile.php:387 comparison), PHP 8.4:
REST param l.i type: integer
[transient] stored imageId='1578474861' (string), page image_id=1578474861 (integer) -> is_lcp_image_in_all_viewports=false; loose == would be true
[object-cache-json] stored imageId='1578474861' (string), page image_id=1578474861 (integer) -> is_lcp_image_in_all_viewports=false; loose == would be true
Screenshots, screen recording, code snippet or Help Scout ticket
Browser sends an int: https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/assets/js/modules/lcp.js#L88-L92
lcpData.imageId = parseInt(id, 10);
REST handler stores a string: https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/rest.php#L1032
$sanitized_lcp_data['imageId'] = sanitize_text_field( $lcp_data['i'] ?? '' );
Strict compare with the int data-opt-id: https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/v2/PageProfiler/Profile.php#L385-L393
public function is_lcp_image_in_all_viewports( int $image_id ): bool {
...
( ( self::$current_profile_data[ $device ]['lcp']['imageId'] ?? null ) === $image_id )
Callers that never fire:
- https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/tag_replacer.php#L434-L445 (adds
fetchpriority="high") - https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/lazyload_replacer.php#L464-L480 (skips lazy load for the LCP image)
$image_id comes from get_id_by_url(), which returns rand() (an int): https://github.com/Codeinwp/optimole-wp/blob/v4.2.17/inc/app_replacer.php#L691-L697
Core: _sanitize_text_fields() casts to string: https://github.com/WordPress/WordPress/blob/7.1.3/wp-includes/formatting.php#L5708-L5713
Environment info
- Optimole 4.2.17 (same code on
masterand back to 4.0.0) - WordPress 5.5 to 7.1 (
_sanitize_text_fields()casts to string in 5.5, 6.8.3 and 7.1.3) - PHP 7.4 to 8.4 (strict
===behaves the same on all) - Lazy load type "fixed|viewport" (default), page profiler enabled (default)
Is the issue you are reporting a regression
No. The string store and the strict compare are both present in 4.0.0.
Found while writing the WordPress compatibility checks for Optimole.
- Lenguaje dominante
- PHP
- Estrellas
- 74
- Forks
- 14
- Merge medio
- 1 d 22 h
- PR fusionados (30 d)
- 29
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de Codeinwp/optimole-wp
-
Page profiler endpoint fatals on PHP 8 when the request has no `b` (background selectors) fieldPosiblemente ocupada @girishpanchal30 la tomó hace 1 día. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Codeinwp/optimole-wp#1204 · 4 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Background image with a gradient is not lazy-loaded on WordPress 7.1 (Quote, Pullquote, Verse, Post Content, Accordion)Posiblemente ocupada @girishpanchal30 la tomó hace 1 día. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Codeinwp/optimole-wp#1203 · 3 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Account sign-up fatals when the Optimole API returns an empty or non-JSON responsePosiblemente ocupada @girishpanchal30 la tomó hace 2 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
Codeinwp/optimole-wp#1200 · 3 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Renaming an image leaves the old URL in comments and term descriptions, with "Unknown column 'ID'" database errorsPosiblemente ocupada @girishpanchal30 la tomó hace 2 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
Codeinwp/optimole-wp#1199 · 3 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
bug-report customer report
Dificultad 4/5 3-5 días Aptitud para principiantes 56/100
Codeinwp/optimole-wp#1211 ·
Los mantenedores suelen responder en 1 día
Todos los issues de Codeinwp/optimole-wp
Issues similares
-
repo:cms
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
craftcms/cms#20235 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
0. Needs triage bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
nextcloud/fulltextsearch#1061 ·
Los mantenedores suelen responder en 1 día
-
PHP 8.6 compatibilityAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100