elcommerce / magento2-app-emulation
A drop-in replacement for Magento App Emulation that supports nested environment emulation
Package info
github.com/elcommerce/magento2-app-emulation
Type:magento2-module
pkg:composer/elcommerce/magento2-app-emulation
Requires
- php: >=7.4
- magento/module-store: >=101.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A drop-in replacement for Magento\Store\Model\App\Emulation that supports nested environment emulation.
Magento's standard implementation allows only one active emulation. If code that is already emulating a store calls another service that starts emulation, the inner call is ignored and its matching stop can restore the environment too early. This module keeps a stack of environment states so every start can be paired with a stop safely.
Features
- Supports nested store-environment emulation.
- Restores the preceding environment when each nested call finishes.
- Preserves store, area, theme, locale, inline-translation state, and phrase renderer.
- Replaces the Magento service through dependency injection, so existing consumers do not need to change their injected class.
- Retains the public
startEnvironmentEmulation()andstopEnvironmentEmulation()API.
Requirements
- Magento 2 with
magento/module-store - PHP 7.4 or later
Installation
Install the Composer package from a repository that provides it:
composer require elcommerce/magento2-app-emulation bin/magento module:enable Elcommerce_AppEmulation bin/magento setup:upgrade bin/magento cache:clean
In production mode, regenerate dependency-injection code after installation:
bin/magento setup:di:compile
Confirm that the module is enabled:
bin/magento module:status Elcommerce_AppEmulation
Usage
Continue to inject Magento's standard emulation class. The module's DI preference supplies the nested-capable implementation automatically.
<?php declare(strict_types=1); namespace Vendor\Module\Model; use Magento\Framework\App\Area; use Magento\Store\Model\App\Emulation; class StorefrontOperation { private Emulation $appEmulation; public function __construct(Emulation $appEmulation) { $this->appEmulation = $appEmulation; } public function execute(int $storeId): void { $this->appEmulation->startEnvironmentEmulation( $storeId, Area::AREA_FRONTEND, true ); try { // Run code that needs the selected store's design and locale. // Services called here may start their own emulation safely. } finally { $this->appEmulation->stopEnvironmentEmulation(); } } }
Always place the stop call in a finally block. Every call to startEnvironmentEmulation() creates a stack entry and must have exactly one matching call to stopEnvironmentEmulation().
Nested behavior
Given the following sequence:
$emulation->startEnvironmentEmulation($storeA); $emulation->startEnvironmentEmulation($storeB); $emulation->stopEnvironmentEmulation(); // Restores store A. $emulation->stopEnvironmentEmulation(); // Restores the original environment.
The inner stop restores the outer emulated environment instead of ending all emulation. Calls may use different stores or areas.
The $force argument retains Magento's standard behavior. When it is false and the requested store and area are already active, the module records a balanced no-op frame. Passing true applies the requested environment unconditionally. In both cases, start and stop calls must remain balanced.
How it works
The module declares a DI preference from Magento\Store\Model\App\Emulation to Elcommerce\AppEmulation\Model\Emulation. Before applying a requested environment, it pushes the current state onto an in-process stack. Stopping emulation pops and restores the most recent state.
No configuration is required.
License
Licensed under the GNU General Public License v3.0.