Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

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)

オープン 初心者向け
#1,202 コメント 3 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@girishpanchal30 がすでに取り組んでいます。

2026年10月29日 から。

  • #1213 @girishpanchal30 による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
88/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
javascript, php

調査の方向性

LCP の imageId が sanitize_text_field() を介して保存される inc/rest.php の 1032 行目付近から始め、次に is_lcp_image_in_all_viewports() が厳密な === を使用している Profile.php:385-393 を確認してください。WP-CLI スニペット、または現在 REST パスをバイパスしている既存の test_lazyload_viewport_with_lcp_data テストで再現してください。完了条件は、保存された ID が int であること(または比較前にキャストされること)、メソッドが両方の viewport プロファイルで true を返すこと、そして tag_replacer.php の LCP 分岐が fetchpriority="high" を追加することです。

索引モデルが issue の本文から書いたものです。

説明

bug
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.php LCP 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
  1. 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:1032 does):
    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 ) );
    '
    
  2. Expected bool(true). Actual bool(false).
  3. Clean up: wp transient delete _oprof_bugcheck_1 && wp transient delete _oprof_bugcheck_2 (or flush the optimole_page_profiler cache 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:

$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 master and 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.

主要言語
PHP
スター
74
フォーク
14
平均マージ
1日 21時間
マージ済み PR(30日)
30

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Codeinwp/optimole-wp のほかの issue

Codeinwp/optimole-wp の issue をすべて見る

似ている issue

PHP の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。