magento2: Grouped product with tax throws exception with PHP 8.1

Preconditions and environment

  • Magento 2.4.4
  • PHP 8.1 (though 8.0 is probably the same)
  • Tax applied (in my case a blanket 20% tax)

Steps to reproduce

  1. Create a simple product
  2. Create a grouped product and add the simple product to it, add the grouped product to a category
  3. Ensure tax will be applied (in my case, I added a new tax zone for the UK, all regions and zip codes, at a rate of 20%. I applied this to the Retail Customer class, and set all customer groups to use it)
  4. Set both options to “Including and Excluding Tax” under Store > Configuration > Sales > Tax > Price Display Settings
  5. Run bin/magento setup:upgrade (not entirely sure this is needed), bin/magento c:f and bin/magento index:reindex
  6. Navigate to the category the grouped product belongs to on frontend

Expected result

A list of products belonging to the category should be displayed.

Actual result

An exception is thrown:

main.CRITICAL: Exception: Deprecated Functionality: ucfirst(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/m24/vendor/magento/module-tax/Pricing/Render/Adjustment.php on line 188 in /var/www/m24/vendor/magento/framework/App/ErrorHandler.php:61 Stack trace: #0 [internal function]: Magento\Framework\App\ErrorHandler->handler() #1 /var/www/m24/vendor/magento/module-tax/Pricing/Render/Adjustment.php(188): ucfirst() #2 /var/www/m24/vendor/magento/module-tax/view/base/templates/pricing/adjustment.phtml(15): Magento\Tax\Pricing\Render\Adjustment->getDataPriceType() #3 /var/www/m24/vendor/magento/framework/View/TemplateEngine/Php.php(71): include('...') #4 /var/www/m24/vendor/magento/framework/View/Element/Template.php(263): Magento\Framework\View\TemplateEngine\Php->render() #5 /var/www/m24/vendor/magento/framework/View/Element/Template.php(293): Magento\Framework\View\Element\Template->fetchView() #6 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1095): Magento\Framework\View\Element\Template->_toHtml() #7 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1099): Magento\Framework\View\Element\AbstractBlock->Magento\Framework\View\Element\{closure}() #8 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(660): Magento\Framework\View\Element\AbstractBlock->_loadCache() #9 /var/www/m24/vendor/magento/module-tax/Pricing/Render/Adjustment.php(64): Magento\Framework\View\Element\AbstractBlock->toHtml() #10 /var/www/m24/vendor/magento/framework/Pricing/Render/AbstractAdjustment.php(57): Magento\Tax\Pricing\Render\Adjustment->apply() #11 /var/www/m24/vendor/magento/framework/Pricing/Render/Amount.php(205): Magento\Framework\Pricing\Render\AbstractAdjustment->render() #12 /var/www/m24/vendor/magento/framework/Pricing/Render/Amount.php(176): Magento\Framework\Pricing\Render\Amount->getAdjustments() #13 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1095): Magento\Framework\Pricing\Render\Amount->_toHtml() #14 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1099): Magento\Framework\View\Element\AbstractBlock->Magento\Framework\View\Element\{closure}() #15 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(660): Magento\Framework\View\Element\AbstractBlock->_loadCache() #16 /var/www/m24/vendor/magento/module-grouped-product/view/base/templates/product/price/final_price.phtml(30): Magento\Framework\View\Element\AbstractBlock->toHtml() #17 /var/www/m24/vendor/magento/framework/View/TemplateEngine/Php.php(71): include('...') #18 /var/www/m24/vendor/magento/framework/View/Element/Template.php(263): Magento\Framework\View\TemplateEngine\Php->render() #19 /var/www/m24/generated/code/Magento/Catalog/Pricing/Render/FinalPriceBox/Interceptor.php(194): Magento\Framework\View\Element\Template->fetchView() #20 /var/www/m24/vendor/magento/framework/View/Element/Template.php(293): Magento\Catalog\Pricing\Render\FinalPriceBox\Interceptor->fetchView() #21 /var/www/m24/vendor/magento/framework/Pricing/Render/PriceBox.php(69): Magento\Framework\View\Element\Template->_toHtml() #22 /var/www/m24/vendor/magento/module-catalog/Pricing/Render/FinalPriceBox.php(71): Magento\Framework\Pricing\Render\PriceBox->_toHtml() #23 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1095): Magento\Catalog\Pricing\Render\FinalPriceBox->_toHtml() #24 /var/www/m24/vendor/magento/framework/Cache/LockGuardedCacheLoader.php(136): Magento\Framework\View\Element\AbstractBlock->Magento\Framework\View\Element\{closure}() #25 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1117): Magento\Framework\Cache\LockGuardedCacheLoader->lockedLoadData() #26 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(660): Magento\Framework\View\Element\AbstractBlock->_loadCache() #27 /var/www/m24/generated/code/Magento/Catalog/Pricing/Render/FinalPriceBox/Interceptor.php(410): Magento\Framework\View\Element\AbstractBlock->toHtml() #28 /var/www/m24/vendor/magento/framework/Pricing/Render.php(97): Magento\Catalog\Pricing\Render\FinalPriceBox\Interceptor->toHtml() #29 /var/www/m24/vendor/magento/module-catalog/Block/Product/ListProduct.php(411): Magento\Framework\Pricing\Render->render() #30 /var/www/m24/generated/code/Magento/Catalog/Block/Product/ListProduct/Interceptor.php(131): Magento\Catalog\Block\Product\ListProduct->getProductPrice() #31 /var/www/m24/vendor/magento/module-catalog/view/frontend/templates/product/list.phtml(77): Magento\Catalog\Block\Product\ListProduct\Interceptor->getProductPrice() #32 /var/www/m24/vendor/magento/framework/View/TemplateEngine/Php.php(71): include('...') #33 /var/www/m24/vendor/magento/framework/View/Element/Template.php(263): Magento\Framework\View\TemplateEngine\Php->render() #34 /var/www/m24/generated/code/Magento/Catalog/Block/Product/ListProduct/Interceptor.php(392): Magento\Framework\View\Element\Template->fetchView() #35 /var/www/m24/vendor/magento/framework/View/Element/Template.php(293): Magento\Catalog\Block\Product\ListProduct\Interceptor->fetchView() #36 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1095): Magento\Framework\View\Element\Template->_toHtml() #37 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1099): Magento\Framework\View\Element\AbstractBlock->Magento\Framework\View\Element\{closure}() #38 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(660): Magento\Framework\View\Element\AbstractBlock->_loadCache() #39 /var/www/m24/generated/code/Magento/Catalog/Block/Product/ListProduct/Interceptor.php(617): Magento\Framework\View\Element\AbstractBlock->toHtml() #40 /var/www/m24/vendor/magento/framework/View/Layout.php(578): Magento\Catalog\Block\Product\ListProduct\Interceptor->toHtml() #41 /var/www/m24/vendor/magento/framework/View/Layout.php(555): Magento\Framework\View\Layout->_renderBlock() #42 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #43 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #44 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #45 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(507): Magento\Framework\View\Layout\Interceptor->renderElement() #46 /var/www/m24/vendor/magento/module-catalog/Block/Category/View.php(100): Magento\Framework\View\Element\AbstractBlock->getChildHtml() #47 /var/www/m24/vendor/magento/module-catalog/view/frontend/templates/category/products.phtml(15): Magento\Catalog\Block\Category\View->getProductListHtml() #48 /var/www/m24/vendor/magento/framework/View/TemplateEngine/Php.php(71): include('...') #49 /var/www/m24/vendor/magento/framework/View/Element/Template.php(263): Magento\Framework\View\TemplateEngine\Php->render() #50 /var/www/m24/vendor/magento/framework/View/Element/Template.php(293): Magento\Framework\View\Element\Template->fetchView() #51 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1095): Magento\Framework\View\Element\Template->_toHtml() #52 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(1099): Magento\Framework\View\Element\AbstractBlock->Magento\Framework\View\Element\{closure}() #53 /var/www/m24/vendor/magento/framework/View/Element/AbstractBlock.php(660): Magento\Framework\View\Element\AbstractBlock->_loadCache() #54 /var/www/m24/vendor/magento/framework/View/Layout.php(578): Magento\Framework\View\Element\AbstractBlock->toHtml() #55 /var/www/m24/vendor/magento/framework/View/Layout.php(555): Magento\Framework\View\Layout->_renderBlock() #56 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #57 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #58 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #59 /var/www/m24/vendor/magento/framework/View/Layout.php(606): Magento\Framework\View\Layout\Interceptor->renderElement() #60 /var/www/m24/vendor/magento/framework/View/Layout.php(557): Magento\Framework\View\Layout->_renderContainer() #61 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #62 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #63 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #64 /var/www/m24/vendor/magento/framework/View/Layout.php(606): Magento\Framework\View\Layout\Interceptor->renderElement() #65 /var/www/m24/vendor/magento/framework/View/Layout.php(557): Magento\Framework\View\Layout->_renderContainer() #66 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #67 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #68 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #69 /var/www/m24/vendor/magento/framework/View/Layout.php(606): Magento\Framework\View\Layout\Interceptor->renderElement() #70 /var/www/m24/vendor/magento/framework/View/Layout.php(557): Magento\Framework\View\Layout->_renderContainer() #71 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #72 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #73 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #74 /var/www/m24/vendor/magento/framework/View/Layout.php(606): Magento\Framework\View\Layout\Interceptor->renderElement() #75 /var/www/m24/vendor/magento/framework/View/Layout.php(557): Magento\Framework\View\Layout->_renderContainer() #76 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #77 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #78 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #79 /var/www/m24/vendor/magento/framework/View/Layout.php(606): Magento\Framework\View\Layout\Interceptor->renderElement() #80 /var/www/m24/vendor/magento/framework/View/Layout.php(557): Magento\Framework\View\Layout->_renderContainer() #81 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #82 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #83 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #84 /var/www/m24/vendor/magento/framework/View/Layout.php(606): Magento\Framework\View\Layout\Interceptor->renderElement() #85 /var/www/m24/vendor/magento/framework/View/Layout.php(557): Magento\Framework\View\Layout->_renderContainer() #86 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(149): Magento\Framework\View\Layout->renderNonCachedElement() #87 /var/www/m24/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout\Interceptor->renderNonCachedElement() #88 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(140): Magento\Framework\View\Layout->renderElement() #89 /var/www/m24/vendor/magento/framework/View/Layout.php(975): Magento\Framework\View\Layout\Interceptor->renderElement() #90 /var/www/m24/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\View\Layout->getOutput() #91 /var/www/m24/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\View\Layout\Interceptor->___callParent() #92 /var/www/m24/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\View\Layout\Interceptor->Magento\Framework\Interception\{closure}() #93 /var/www/m24/generated/code/Magento/Framework/View/Layout/Interceptor.php(347): Magento\Framework\View\Layout\Interceptor->___callPlugins() #94 /var/www/m24/vendor/magento/framework/View/Result/Page.php(260): Magento\Framework\View\Layout\Interceptor->getOutput() #95 /var/www/m24/vendor/magento/framework/View/Result/Layout.php(171): Magento\Framework\View\Result\Page->render() #96 /var/www/m24/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\View\Result\Layout->renderResult() #97 /var/www/m24/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\View\Result\Page\Interceptor->___callParent() #98 /var/www/m24/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\View\Result\Page\Interceptor->Magento\Framework\Interception\{closure}() #99 /var/www/m24/generated/code/Magento/Framework/View/Result/Page/Interceptor.php(95): Magento\Framework\View\Result\Page\Interceptor->___callPlugins() #100 /var/www/m24/vendor/magento/framework/App/Http.php(120): Magento\Framework\View\Result\Page\Interceptor->renderResult() #101 /var/www/m24/generated/code/Magento/Framework/App/Http/Interceptor.php(23): Magento\Framework\App\Http->launch() #102 /var/www/m24/vendor/magento/framework/App/Bootstrap.php(264): Magento\Framework\App\Http\Interceptor->launch() #103 /var/www/m24/pub/index.php(30): Magento\Framework\App\Bootstrap->run() #104 {main} [] []

Additional information

For whatever reason the priceType returned by amountRender is null, which is not a valid parameter for ucfirst in PHP 8. I suspect this has something to do with how grouped products calculate their display price but haven’t tracked it down yet.

vendor/magento/module-tax/Pricing/Render/Adjustment.php line 184:

public function getDataPriceType(): string { return $this->amountRender->getPriceType() === 'finalPrice' ? 'basePrice' : 'base' . ucfirst($this->amountRender->getPriceType()); }

Release note

No response

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 21 (11 by maintainers)

Most upvoted comments

Patch for Magento 2.4.5-p1 to use with vaimo/composer-patches;

@package magento/module-tax
@link https://github.com/magento/magento2/issues/35500
@level 0

diff --git Pricing/Render/Adjustment.php Pricing/Render/Adjustment.php
index 0e5c619..06f4e02 100644
--- Pricing/Render/Adjustment.php
+++ Pricing/Render/Adjustment.php
@@ -185,6 +185,6 @@ class Adjustment extends AbstractAdjustment
     {
         return $this->amountRender->getPriceType() === 'finalPrice'
             ? 'basePrice'
-            : 'base' . ucfirst($this->amountRender->getPriceType());
+            : 'base' . ucfirst($this->amountRender->getPriceType() ?? '');
     }
 }

Workaround:

change vendor/magento/module-tax/Pricing/Render/Adjustment.php:188

From : 'base' . ucfirst($this->amountRender->getPriceType()); To : 'base' . ucfirst($this->amountRender->getPriceType() ?: '');

Hi @craig-bartlett ,

Thank you so much for sharing the detailed steps and it is reproducible on Magento 2.4.4 instance with PHP 8.1 . Hence marking the ticket issue is reproducible.

  1. created 2 simple products and 1 grouped product, all in the same category and set to in-stock. I assigned the two simple products to the grouped product. At this point they display fine.
Screenshot 2022-05-24 at 3 12 40 PM Screenshot 2022-05-24 at 5 35 51 PM
  1. then created the tax rates as before and ensured it applied to all customers.
Screenshot 2022-05-24 at 5 49 57 PM
  1. Tax rules
Screenshot 2022-05-24 at 6 00 37 PM
  1. Changed the price settings to display catalog prices with and without tax.
Screenshot 2022-05-24 at 6 11 20 PM
  1. I wiped generated/ again, ran setup:upgrade, cache:clear, and ran a full reindex. This resulted in the page displaying incorrectly.
Screenshot 2022-05-24 at 9 03 03 PM

Thanks