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)
Les mainteneurs répondent en général sous 1 jour
@girishpanchal30 y travaille déjà.
Depuis le 29/10/2026.
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 88/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- Active
- Stack technique
- javascript, php
- Domaine
- backend, performance
Piste de recherche
Commencez à inc/rest.php vers la ligne 1032, où l’imageId LCP est stocké via sanitize_text_field(), puis inspectez Profile.php:385-393 où is_lcp_image_in_all_viewports() utilise === strict. Reproduisez via l’extrait WP-CLI ou le test existant test_lazyload_viewport_with_lcp_data, qui contourne actuellement le chemin REST. Terminé signifie que l’ID stocké est un int (ou converti avant la comparaison), que la méthode renvoie true pour les deux profils de viewport, et que la branche LCP de tag_replacer.php ajoute fetchpriority="high".
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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.
- Langage dominant
- PHP
- Étoiles
- 74
- Forks
- 14
- Merge moyen
- 1 j 23 h
- PR mergées (30 j)
- 31
Préparer son environnement
- Fournit un Dockerfile ou un fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de Codeinwp/optimole-wp
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 77/100
Codeinwp/optimole-wp#1216 ·
Les mainteneurs répondent en général sous 1 jour
-
Page profiler endpoint fatals on PHP 8 when the request has no `b` (background selectors) fieldPeut-être pris @girishpanchal30 l’a pris il y a 2 jours. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 88/100
Codeinwp/optimole-wp#1204 · 4 commentaires · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
-
Background image with a gradient is not lazy-loaded on WordPress 7.1 (Quote, Pullquote, Verse, Post Content, Accordion)Peut-être pris @girishpanchal30 l’a pris il y a 2 jours. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Codeinwp/optimole-wp#1203 · 3 commentaires · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
-
Account sign-up fatals when the Optimole API returns an empty or non-JSON responsePeut-être pris @girishpanchal30 l’a pris il y a 2 jours. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
Codeinwp/optimole-wp#1200 · 3 commentaires · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
-
Renaming an image leaves the old URL in comments and term descriptions, with "Unknown column 'ID'" database errorsPeut-être pris @girishpanchal30 l’a pris il y a 2 jours. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
Codeinwp/optimole-wp#1199 · 3 commentaires · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de Codeinwp/optimole-wp
Issues similaires
-
bug webbackend
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
DOMjudge/domjudge#3886 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
opensourcepos/opensourcepos#4794 ·
Les mainteneurs répondent en général sous 1 jour
-
repo:cms
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
craftcms/cms#20235 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
Les mainteneurs répondent en général sous 1 jour
-
REST API: decimal ids like 1.5 give 500 on folder and report endpointsPeut-être pris @console-log-life l’a pris aujourd’hui. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
Les mainteneurs répondent en général sous 3 jours