Summary
Viewport-based lazy-load processing can terminate a frontend request when stored page-profile data is object-shaped rather than array-shaped.
Expected behavior: Invalid or unavailable profile data is treated as unavailable, allowing image processing to continue without a fatal error.
Actual behavior: The page-profiler lookup accesses the object as an array and throws Cannot use object of type stdClass as array.
Impact: Affected frontend page requests can fail during Optimole image replacement.
Customer context
- Product / area: Optimole WordPress plugin, page profiler and viewport lazy loading
- Version: 4.2.11
- Environment: WordPress 7.1, PHP 8.3.31
- Integration / third party: An external object cache may be in use; this is inferred from the plugin's storage selection and is not confirmed by telemetry.
- Reported error / symptom:
Cannot use object of type stdClass as array at inc/v2/PageProfiler/Profile.php:369
- Impact: 10 telemetry occurrences across 2 production sites between 2026-08-27 and 2026-08-28 during frontend requests.
Reproduction notes
- Enable the viewport-based lazy-load workflow so
Optml_Lazyload_Replacer::can_lazyload_for() consults page-profile data.
- Arrange for the current profile's mobile or desktop storage value, or its
af member, to be object-shaped rather than an array.
- Process frontend HTML containing an eligible image.
Observed in production: the lookup throws Cannot use object of type stdClass as array from the reported Profile.php location. The exact storage backend and data origin are unavailable.
Diagnosis
Conclusion
Telemetry directly records the exception in the viewport-profile lookup. In v4.2.11, that lookup assumes each stored device profile and its af value are arrays, while the selected storage reader returns its value without runtime shape validation. An object-shaped value therefore reaches array access and produces the captured error. The internal REST write path constructs arrays, so the source of the object-shaped stored value remains unconfirmed.
Where this likely occurs
inc/manager.php — Optml_Manager::replace_content() lines 446-482 enables page profiling, loads current profile data, then processes frontend image content.
inc/lazyload_replacer.php — Optml_Lazyload_Replacer::can_lazyload_for() lines 461-478 calls Profile::is_in_all_viewports() for viewport lazy loading before deciding whether an image is lazy-loaded.
inc/v2/PageProfiler/Profile.php — Profile::set_current_profile_data() lines 336-347 copies each storage value into the current profile without type normalization; Profile::is_in_all_viewports() lines 362-375 then accesses ['af'][$image_id].
inc/v2/PageProfiler/Storage/ObjectCache.php — ObjectCache::get() lines 54-62 returns wp_cache_get() directly despite the documented array|false return contract.
v4.2.11 is release commit e9ee20c5a5e10565d2f8ffc56aa77fe76b202332; the failing is_in_all_viewports() access is present there and unchanged through the inspected HEAD. The lookup originated in d096d4bb28e01ddc56c5fce937adbd20d4b0fa63.
Engineering notes
- The profiler selects
Storage\ObjectCache when WordPress reports an external object cache, or Storage\Transients otherwise, via Profile::__construct() in inc/v2/PageProfiler/Profile.php lines 89-106.
Profile::store() writes the expected device data as arrays containing af, bg, and lcp in inc/v2/PageProfiler/Profile.php lines 183-218. Optml_Rest::optimizations() also normalizes its collected payload before calling that method at inc/rest.php lines 935-1046.
- The profiler storage class is filterable through
optml_page_profiler_storage; a custom storage implementation is another possible source of an object-shaped value. Neither cache backend behavior nor a custom storage integration was available for inspection.
- The failure occurs before the existing
is_data_available() result is used in can_lazyload_for(), so that later availability check cannot prevent this exception for malformed non-empty device values.
Test coverage status
tests/test-lazyload-viewport.php covers normal array-backed profile lookup in test_is_in_viewport_detection() lines 179-203, stored structure in test_profile_data_storage_and_retrieval() lines 211-247, and missing or partial device data in lines 302-343. No relevant coverage was found during inspection for object-shaped cache values, malformed profile storage, or custom page-profiler storage. No test suite was run.
What to verify or explore next
- Reproduce with a page-profile storage value whose device entry or
af member is an object, under viewport lazy loading.
- Check affected sites' active object-cache drop-ins and whether the
optml_page_profiler_storage filter is present.
- Run the page-profiler viewport test suite with array, false, and object-shaped stored values.
- Confirm whether transient-backed storage can encounter the same serialized data shape.
Unknowns / follow-up
- Telemetry does not identify which stored profile field was object-shaped or how it entered storage.
- The affected sites' cache backend, installed extensions, and profiler configuration are unavailable.
- Git history establishes that the unsafe access predates 4.2.11 but does not establish a previously working release boundary.
Confidence
Confidence: 94/100
Production telemetry records 10 frontend crashes on two sites in Optimole 4.2.11, and the reported source line directly indexes a stored profile value as an array without validating its documented array|false storage contract. The relevant call path is reachable during frontend viewport lazy-loading.
Crash telemetry
|
|
| Occurrences |
10 |
| Distinct sites |
2 |
| First seen |
2026-08-27 10:11 UTC |
| Last seen |
2026-08-28 17:29 UTC |
| Crash location |
product:inc/v2/PageProfiler/Profile.php:369 |
| Request context |
frontend |
| Inside Themeisle SDK |
no |
| Product versions |
4.2.11 |
| WP versions |
7.1 |
| PHP versions |
8.3.31 |
| SDK versions |
3.3.58 |
Source: automated crash report — optimole-wp, fingerprint 3025c25bb349ed9cf9cdbed87b66cec3
Generated by bug-report-triage (ID: bug-report-triage_6a9275be3e9dc7.49048068)
Summary
Viewport-based lazy-load processing can terminate a frontend request when stored page-profile data is object-shaped rather than array-shaped.
Expected behavior: Invalid or unavailable profile data is treated as unavailable, allowing image processing to continue without a fatal error.
Actual behavior: The page-profiler lookup accesses the object as an array and throws
Cannot use object of type stdClass as array.Impact: Affected frontend page requests can fail during Optimole image replacement.
Customer context
Cannot use object of type stdClass as arrayatinc/v2/PageProfiler/Profile.php:369Reproduction notes
Optml_Lazyload_Replacer::can_lazyload_for()consults page-profile data.afmember, to be object-shaped rather than an array.Observed in production: the lookup throws
Cannot use object of type stdClass as arrayfrom the reported Profile.php location. The exact storage backend and data origin are unavailable.Diagnosis
Conclusion
Telemetry directly records the exception in the viewport-profile lookup. In
v4.2.11, that lookup assumes each stored device profile and itsafvalue are arrays, while the selected storage reader returns its value without runtime shape validation. An object-shaped value therefore reaches array access and produces the captured error. The internal REST write path constructs arrays, so the source of the object-shaped stored value remains unconfirmed.Where this likely occurs
inc/manager.php—Optml_Manager::replace_content()lines 446-482 enables page profiling, loads current profile data, then processes frontend image content.inc/lazyload_replacer.php—Optml_Lazyload_Replacer::can_lazyload_for()lines 461-478 callsProfile::is_in_all_viewports()for viewport lazy loading before deciding whether an image is lazy-loaded.inc/v2/PageProfiler/Profile.php—Profile::set_current_profile_data()lines 336-347 copies each storage value into the current profile without type normalization;Profile::is_in_all_viewports()lines 362-375 then accesses['af'][$image_id].inc/v2/PageProfiler/Storage/ObjectCache.php—ObjectCache::get()lines 54-62 returnswp_cache_get()directly despite the documentedarray|falsereturn contract.v4.2.11is release commite9ee20c5a5e10565d2f8ffc56aa77fe76b202332; the failingis_in_all_viewports()access is present there and unchanged through the inspected HEAD. The lookup originated ind096d4bb28e01ddc56c5fce937adbd20d4b0fa63.Engineering notes
Storage\ObjectCachewhen WordPress reports an external object cache, orStorage\Transientsotherwise, viaProfile::__construct()ininc/v2/PageProfiler/Profile.phplines 89-106.Profile::store()writes the expected device data as arrays containingaf,bg, andlcpininc/v2/PageProfiler/Profile.phplines 183-218.Optml_Rest::optimizations()also normalizes its collected payload before calling that method atinc/rest.phplines 935-1046.optml_page_profiler_storage; a custom storage implementation is another possible source of an object-shaped value. Neither cache backend behavior nor a custom storage integration was available for inspection.is_data_available()result is used incan_lazyload_for(), so that later availability check cannot prevent this exception for malformed non-empty device values.Test coverage status
tests/test-lazyload-viewport.phpcovers normal array-backed profile lookup intest_is_in_viewport_detection()lines 179-203, stored structure intest_profile_data_storage_and_retrieval()lines 211-247, and missing or partial device data in lines 302-343. No relevant coverage was found during inspection for object-shaped cache values, malformed profile storage, or custom page-profiler storage. No test suite was run.What to verify or explore next
afmember is an object, under viewport lazy loading.optml_page_profiler_storagefilter is present.Unknowns / follow-up
Confidence
Confidence: 94/100
Production telemetry records 10 frontend crashes on two sites in Optimole 4.2.11, and the reported source line directly indexes a stored profile value as an array without validating its documented
array|falsestorage contract. The relevant call path is reachable during frontend viewport lazy-loading.Crash telemetry
product:inc/v2/PageProfiler/Profile.php:369Source: automated crash report — optimole-wp, fingerprint
3025c25bb349ed9cf9cdbed87b66cec3Generated by bug-report-triage (ID: bug-report-triage_6a9275be3e9dc7.49048068)