Magento 2.4.9 was released in May 2025 alongside official PHP 8.5 support. This is the first Magento release where PHP 8.5’s pipe operator is available out of the box. Beyond PHP 8.5, the release brings performance improvements, updated dependencies, and deprecation cleanup. I show what actually changed, how to migrate, and which PHP 8.5 features are worth using in modules immediately.
What’s new in Magento 2.4.9
| Area | Change | Impact |
|---|---|---|
| PHP support | PHP 8.5 fully supported, PHP 8.2 dropped | High – upgrade PHP runtime |
| MariaDB | 10.11+ recommended (10.6 still supported) | Low-Medium |
| OpenSearch | OpenSearch 2.x supported, 3.x experimental | Low |
| Performance | Improved DI compilation, faster cache operations | Medium |
| GraphQL | Additional batch resolvers in core | Low for most |
| Hyvä | Not part of Magento core, but 2.4.9 tested and compatible | Low |
| Security | CSP improvements, updated JWT handling | Medium |
PHP 8.5 features to use immediately in modules
<?php
declare(strict_types=1);
// 1. PIPE OPERATOR - most impactful for collection processing
class ProductExportService
{
public function getExportData(int $storeId): array
{
$sc = $this->searchCriteriaBuilder
->addFilter('status', 1)
->setPageSize(1000)
->create();
return $this->productRepository->getList($sc)->getItems()
|> array_filter(..., fn($p) => (float)$p->getPrice() > 0)
|> array_map(..., fn($p) => $this->toExportRow($p, $storeId))
|> array_values(...);
}
}
// 2. array_first() and array_last() - replace clunky idioms
class OrderProcessor
{
public function getLatestPendingOrder(int $customerId): ?OrderInterface
{
$orders = $this->getOrdersByCustomer($customerId);
return array_first(
$orders,
fn($o) => $o->getStatus() === 'pending'
);
}
public function getMostRecentOrder(int $customerId): ?OrderInterface
{
return array_last($this->getOrdersByCustomer($customerId));
}
}
// 3. Readonly property inheritance - cleaner event hierarchies
readonly class MagentoEvent
{
public function __construct(
public int $storeId,
public \DateTimeImmutable $occurredAt,
) {}
}
readonly class ProductSavedEvent extends MagentoEvent
{
public function __construct(
int $storeId,
\DateTimeImmutable $occurredAt,
public int $productId,
public string $sku,
) {
parent::__construct($storeId, $occurredAt);
}
}
Migration checklist: Magento 2.4.8 to 2.4.9
#!/bin/bash
# migration-check.sh
echo "=== Magento 2.4.9 migration pre-check ==="
# 1. Check PHP compatibility
echo "PHP compatibility check..."
vendor/bin/phpcs app/code/ --standard=PHPCompatibility \
--runtime-set testVersion 8.5 --report=summary
# 2. PHPStan with PHP 8.5 baseline
echo "PHPStan PHP 8.5 check..."
vendor/bin/phpstan analyse app/code/ --php-version=8.5 --level=5 \
--error-format=table | tail -20
# 3. Check for removed functions
echo "Checking deprecated function usage..."
grep -rn "utf8_encode\|utf8_decode\|money_format\|each(" app/code/ --include="*.php" | head -20
# 4. Check implicit nullable parameters
grep -rn "function.*string \$[a-z]* = null\|function.*int \$[a-z]* = null\|function.*float \$[a-z]* = null" \
app/code/ --include="*.php" | grep -v "?string\|?int\|?float" | head -20
# 5. Run existing tests
echo "Running test suite..."
vendor/bin/phpunit app/code/ --testsuite=Unit 2>&1 | tail -20
echo "=== Checks complete ==="
# Full upgrade process
# On staging environment first!
# 1. Backup
mysqldump -u magento -p magento > backup_before_249.sql
# 2. Enable maintenance
bin/magento maintenance:enable
# 3. Update Magento
composer require magento/product-community-edition:2.4.9 \
--no-update --no-interaction
composer update --no-interaction
# 4. Upgrade PHP runtime to 8.5 (if not already done)
# In DDEV:
# ddev config --php-version=8.5
# ddev restart
# 5. Run setup
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy pl_PL en_US -f
# 6. Cache flush and disable maintenance
bin/magento cache:flush
bin/magento maintenance:disable
# 7. Run smoke tests
curl -sI https://shop.ddev.site/ | head -5
curl -sI https://shop.ddev.site/admin/ | head -5
bin/magento indexer:status
PHP 8.5 pipe operator in Magento codebase – style guide
<?php
declare(strict_types=1);
// When to use pipe operator in Magento modules:
// YES - sequential data transformations
$processedItems = $quote->getItems()
|> array_filter(..., fn($i) => !$i->isDeleted())
|> array_map(..., fn($i) => $this->mapToDto($i))
|> array_values(...);
// YES - string normalisation
$slug = $product->getName()
|> trim(...)
|> strtolower(...)
|> fn($s) => preg_replace('/[^a-z0-9\s-]/', '', $s)
|> fn($s) => preg_replace('/\s+/', '-', $s);
// NO - side effects or mutations (use regular code)
// array_walk has side effects - do not use in pipe
// foreach loops - do not use in pipe
// NO - when the chain is two steps or fewer
// $result = trim(strtolower($name)); // fine as nested
// $result = $name |> strtolower(...) |> trim(...); // unnecessary for 2 steps
// Style: align |> operators for readability
$result = $orders
|> array_filter(..., fn($o) => $o->getStatus() === 'complete')
|> array_map(..., fn($o) => $o->getGrandTotal())
|> array_sum(...);
Summary
Magento 2.4.9 + PHP 8.5 is the current recommended stack for new projects. The upgrade from 2.4.8 is smooth – no major architectural changes, mostly dependency updates and deprecation cleanup. PHP 8.5’s pipe operator is immediately useful in module code for collection transformations. The migration checklist catches the common issues before they become problems in production. For existing projects on 2.4.8 with PHP 8.4 – no rush, both are supported until 2027. For new projects – start on 2.4.9 with PHP 8.5.
