Skip to content

Prevent fatal error when product template variable is a Product object - #181

Merged
Hlavtox merged 1 commit into
PrestaShop:devfrom
boo-code:fix/footer-product-fatal-on-product-object
Oct 24, 2026
Merged

Hlavtox merged 1 commit into
PrestaShop:devfrom
boo-code:fix/footer-product-fatal-on-product-object

Conversation

@boo-code

Copy link
Copy Markdown
Contributor
Questions Answers
Description? HookDisplayFooterProduct::run() reads the product Smarty variable and passes it straight to ProductWrapper::prepareItemFromProduct(), which consumes it with array keys ($product['id_product'], $product['name'], …). That variable is normally a ProductLazyArray (array-accessible), but some themes/modules assign a raw Product object to it, producing a fatal error Cannot use object of type Product as array on the product page (reported on 5.0.3 / PS 8.2.4). This PR guards the hook: if the product is not array-accessible, it skips tracking instead of breaking the page. ProductLazyArray implements ArrayAccess, so the normal flow is unaffected.
Type? bug fix
BC breaks? no
Deprecations? no
Fixed ticket? Fixes #179.
How to test? On a product page where the product Smarty variable is a raw Product object, before this PR the page fatals with Cannot use object of type Product as array (ProductWrapper.php line ~136). Deterministic check: prepareItemFromProduct(new Product(1)) throws that error; with the guard, HookDisplayFooterProduct::run() returns without a fatal. With the standard ProductLazyArray (which implements ArrayAccess), tracking continues to work as before.
Sponsor company

HookDisplayFooterProduct::run() passes the 'product' Smarty variable straight
to ProductWrapper::prepareItemFromProduct(), which accesses it with array keys.
The variable is normally a ProductLazyArray (array-accessible), but some themes
or modules assign a raw Product object, causing a fatal error
"Cannot use object of type Product as array" on the product page.

Guard against a non-array-accessible product and skip tracking in that case
instead of breaking the whole product page.
@ps-jarvis

Copy link
Copy Markdown

Hello @boo-code!

This is your first pull request on ps_googleanalytics repository of the PrestaShop project.

Thank you, and welcome to this Open Source community!

@github-project-automation github-project-automation Bot moved this to Ready for review in PR Dashboard Jun 27, 2026

@Hlavtox Hlavtox left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@boo-code I am don't have a great feeling from this change. I think we should not add checks for bad modules overwriting system variables..

Cc @kpodemski @Codencode

@kpodemski kpodemski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me the change is totally fine, and I'm always recommending to check whether the input data is what we expect.

@ps-jarvis ps-jarvis added the Waiting for QA Status: Action required, Waiting for test feedback label Jul 2, 2026
@ps-jarvis ps-jarvis moved this from Ready for review to To be tested in PR Dashboard Jul 2, 2026
@boo-code

boo-code commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Hlavtox, that is a fair principle and I do not want this to become a pattern of guarding against every misbehaving module either. Let me explain why I think this specific case is worth it, and offer a middle ground.

The distinction I would draw: product here is not this module's own state, it is a shared Smarty template variable that the hook reads but does not set. It is populated by the theme, core, or another module, so from this hook's point of view its type is genuinely external input crossing a trust boundary, not a value we control. The guard validates input at that boundary rather than compensating for one known-buggy module.

The failure modes are also very asymmetric. Without the guard, a third party assigning a raw Product object produces an uncaught fatal that takes down the whole product page (issue #179 is exactly this, on a live shop), not just GA tracking. With the guard, the only consequence is that GA tracking is skipped for that one request. Degrading the analytics is proportionate; crashing the storefront is not.

It is also deliberately minimal and does not touch the normal path: ProductLazyArray implements ArrayAccess, so the standard flow is unaffected and only the anomalous type returns early.

On your underlying concern, that we should not silently mask a bad module: I agree, and I am happy to add a debug-level log when the guard trips, so the misconfiguration stays visible and diagnosable instead of being swallowed. If you would prefer that, I will push it; otherwise I am glad to leave the guard as it is. Either way, thanks for the careful read.

@Codencode

Copy link
Copy Markdown
Member

Indeed, I also think it's better to avoid the page crashing.

@alicjacichocka-bitbag

Copy link
Copy Markdown

QA OK ✅

Tested on: PrestaShop 9.2.x d1106c4 (9.2.1), Docker, PHP 8.1.33, debug mode, theme Classic, ps_googleanalytics 6.0.0.
For "before", I used classes/Hook/HookDisplayFooterProduct.php from dev 64a6b1c. For "after", I used the same file from the PR head 025cf42, which is the only file the PR changes. The rest of the module is the same as dev. I cleared the cache after each switch.
I set a test GA_ACCOUNT_ID (G-QA181TEST) so that the events are rendered in the page source.

Setup (to reproduce #179): a small QA module hooked on displayFooterProduct before ps_googleanalytics assigns new Product($id_product, true, $id_lang) to the product Smarty variable. This is what a theme or module that assigns a raw Product object does. A second QA module hooked after ps_googleanalytics puts the original ProductLazyArray back, so that the rest of the template is not affected. Both modules are active only with ?qa_raw_product=1.

Steps:

  1. Open the Clothes category, then the "Hummingbird printed t-shirt" product page (normal case).
  2. Open the same product page with ?qa_raw_product=1 (raw Product object).
Before After
Product page, normal case (ProductLazyArray) view_item + select_item same, and the payload is identical before and after ✅
Product page with a raw Product object fatal error Cannot use object of type Product as array in ProductWrapper.php:136 (the same stack trace as in #179), and the page doesn't render the page renders normally. There is no view_item/select_item for this page, because tracking is skipped as described in the PR ✅

Other checks:

  • Deterministic check from "How to test", run from PHP: prepareItemFromProduct(new Product(1)) still throws the error before and after, as expected, because the PR doesn't change the wrapper. HookDisplayFooterProduct::run() with a raw Product in the product variable throws the fatal error before. After, it returns NULL without an error ✅
  • HookDisplayFooterProduct is the only hook of the module that reads the product template variable. The cart hooks (actionCartUpdateQuantityBefore, actionObjectProductInCartDeleteBefore) build their own product arrays, so they are not affected.
  • The PR checks (PHPStan on 8.2.x / 9.0.3 / 9.1.x / develop, PHP-CS-Fixer, syntax) are green.
before_after_product_object_fatal.mp4

@Codencode

Copy link
Copy Markdown
Member

@alicjacichocka-bitbag
Thanks for the QA and for confirming everything works as expected!

Can I add the QA ✔️ label to this PR?

@Hlavtox
Hlavtox merged commit 0538714 into PrestaShop:dev Oct 24, 2026
12 checks passed
@github-project-automation github-project-automation Bot moved this from To be tested to Merged in PR Dashboard Oct 24, 2026
@ps-jarvis

Copy link
Copy Markdown

PR merged, well done!

Message to @PrestaShop/committers: do not forget to milestone it before the merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Waiting for QA Status: Action required, Waiting for test feedback

Projects

Status: Merged

Development

Successfully merging this pull request may close these issues.

Fatal error

7 participants