diff --git a/.gitignore b/.gitignore
index b799514c857..04e077425c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,3 +126,6 @@ cypress/videos/
# rector
.rector.result.cache
+
+#dotenv
+.env
diff --git a/app/Mage.php b/app/Mage.php
index fb3f012b9e0..34320067648 100644
--- a/app/Mage.php
+++ b/app/Mage.php
@@ -19,13 +19,6 @@
Mage::register('original_include_path', get_include_path());
-if (!empty($_SERVER['MAGE_IS_DEVELOPER_MODE']) || !empty($_ENV['MAGE_IS_DEVELOPER_MODE'])) {
- Mage::setIsDeveloperMode(true);
- ini_set('display_errors', '1');
- ini_set('error_prepend_string', '
');
- ini_set('error_append_string', '');
-}
-
/**
* Set include path
*/
@@ -59,6 +52,17 @@
include_once $path;
}
+$dotenv = Dotenv\Dotenv::createImmutable(BP);
+$dotenv->safeLoad();
+$dotenv->ifPresent(['MAGE_IS_DEVELOPER_MODE', 'OPENMAGE_CONFIG_OVERRIDE_ALLOWED'])->isInteger();
+
+if (!empty($_SERVER['MAGE_IS_DEVELOPER_MODE']) || !empty($_ENV['MAGE_IS_DEVELOPER_MODE'])) {
+ Mage::setIsDeveloperMode(true);
+ ini_set('display_errors', '1');
+ ini_set('error_prepend_string', '');
+ ini_set('error_append_string', '');
+}
+
/**
* Main Mage hub class
*/
diff --git a/app/code/core/Mage/Core/Helper/EnvironmentConfigLoader.php b/app/code/core/Mage/Core/Helper/EnvironmentConfigLoader.php
index 771d0cb6938..a676ec705f5 100644
--- a/app/code/core/Mage/Core/Helper/EnvironmentConfigLoader.php
+++ b/app/code/core/Mage/Core/Helper/EnvironmentConfigLoader.php
@@ -302,12 +302,15 @@ public function setEnvStore(array $envStorage): void
/**
* @return array
+ * @SuppressWarnings("PHPMD.Superglobals")
*/
public function getEnv(): array
{
if (empty($this->envStore)) {
- $env = getenv();
- $env = array_filter($env, function ($key) {
+ // Use $_ENV instead of getenv() because phpdotenv populates $_ENV with both system environment variables
+ // and variables from the .env file. This ensures that configuration overrides from .env are respected.
+ // getenv() would only return system environment variables, not those loaded from .env.
+ $env = array_filter($_ENV, function ($key) {
return str_starts_with($key, self::ENV_STARTS_WITH);
}, ARRAY_FILTER_USE_KEY);
$this->envStore = $env;
diff --git a/composer.json b/composer.json
index 915bc5b3d70..132b711d0ed 100644
--- a/composer.json
+++ b/composer.json
@@ -54,7 +54,8 @@
"symfony/string": "^6.4",
"symfony/translation-contracts": "^3.5",
"symfony/validator": "^6.4",
- "tinymce/tinymce": "^8.0"
+ "tinymce/tinymce": "^8.0",
+ "vlucas/phpdotenv": "^5.6"
},
"require-dev": {
"ext-xmlreader": "*",
diff --git a/composer.lock b/composer.lock
index 51e404f59e4..ada5b0542ce 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "7ed5900b3fbb9f7ac765188353c3faf1",
+ "content-hash": "e1cdedd0b0abe19a9af9e069e9d346a5",
"packages": [
{
"name": "carbonphp/carbon-doctrine-types",
@@ -687,6 +687,68 @@
},
"time": "2020-11-08T09:03:06+00:00"
},
+ {
+ "name": "graham-campbell/result-type",
+ "version": "v1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/GrahamCampbell/Result-Type.git",
+ "reference": "3ba905c11371512af9d9bdd27d99b782216b6945"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945",
+ "reference": "3ba905c11371512af9d9bdd27d99b782216b6945",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "GrahamCampbell\\ResultType\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "An Implementation Of The Result Type",
+ "keywords": [
+ "Graham Campbell",
+ "GrahamCampbell",
+ "Result Type",
+ "Result-Type",
+ "result"
+ ],
+ "support": {
+ "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-07-20T21:45:45+00:00"
+ },
{
"name": "illuminate/collections",
"version": "v10.49.0",
@@ -1787,6 +1849,81 @@
},
"time": "2025-04-30T23:02:43+00:00"
},
+ {
+ "name": "phpoption/phpoption",
+ "version": "1.9.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/schmittjoh/php-option.git",
+ "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
+ "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2.5 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpOption\\": "src/PhpOption/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Johannes M. Schmitt",
+ "email": "schmittjoh@gmail.com",
+ "homepage": "https://github.com/schmittjoh"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ }
+ ],
+ "description": "Option Type for PHP",
+ "keywords": [
+ "language",
+ "option",
+ "php",
+ "type"
+ ],
+ "support": {
+ "issues": "https://github.com/schmittjoh/php-option/issues",
+ "source": "https://github.com/schmittjoh/php-option/tree/1.9.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-21T11:53:16+00:00"
+ },
{
"name": "phpseclib/mcrypt_compat",
"version": "2.0.6",
@@ -4934,6 +5071,90 @@
},
"time": "2025-11-17T03:22:11+00:00"
},
+ {
+ "name": "vlucas/phpdotenv",
+ "version": "v5.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
+ "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
+ "shasum": ""
+ },
+ "require": {
+ "ext-pcre": "*",
+ "graham-campbell/result-type": "^1.1.3",
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.3",
+ "symfony/polyfill-ctype": "^1.24",
+ "symfony/polyfill-mbstring": "^1.24",
+ "symfony/polyfill-php80": "^1.24"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.8.2",
+ "ext-filter": "*",
+ "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
+ },
+ "suggest": {
+ "ext-filter": "Required to use the boolean validator."
+ },
+ "type": "library",
+ "extra": {
+ "bamarni-bin": {
+ "bin-links": true,
+ "forward-command": false
+ },
+ "branch-alias": {
+ "dev-master": "5.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Graham Campbell",
+ "email": "hello@gjcampbell.co.uk",
+ "homepage": "https://github.com/GrahamCampbell"
+ },
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com",
+ "homepage": "https://github.com/vlucas"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "support": {
+ "issues": "https://github.com/vlucas/phpdotenv/issues",
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/GrahamCampbell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-04-30T23:37:27+00:00"
+ },
{
"name": "voku/portable-ascii",
"version": "2.0.3",
diff --git a/docs/content/developers/samples/env.md b/docs/content/developers/samples/env.md
new file mode 100644
index 00000000000..fb9f352b670
--- /dev/null
+++ b/docs/content/developers/samples/env.md
@@ -0,0 +1,29 @@
+---
+hide:
+ - toc
+---
+
+# `.env`
+
+Load environment variables from a `.env` file in the project root.
+
+### Supported variables:
+
+- `MAGE_IS_DEVELOPER_MODE` (integer: 0 or 1): Enables developer mode if set to 1.
+- `OPENMAGE_CONFIG_OVERRIDE_ALLOWED` (integer: 0 or 1): Allows config override if set to 1.
+- `OPENMAGE_CONFIG__*` (string): Any variable prefixed with `OPENMAGE_CONFIG__` will be used as a config override.
+
+### Validation:
+
+- `MAGE_IS_DEVELOPER_MODE` and `OPENMAGE_CONFIG_OVERRIDE_ALLOWED` must be integers if present.
+
+### Integration:
+
+- These variables can be set in the `.env` file, or via environment variables (`$_SERVER`/`$_ENV`).
+- `.env` values are loaded first, but can be overridden by actual environment variables.
+
+```ini
+MAGE_IS_DEVELOPER_MODE=1
+OPENMAGE_CONFIG_OVERRIDE_ALLOWED=1
+OPENMAGE_CONFIG__DEFAULT__GENERAL__STORE_INFORMATION__NAME="My OpenMage Store"
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index 91ce8c67e7d..3103c3aa8d5 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -216,6 +216,7 @@ nav:
- 'Multistore':
- developers/error-pages.md
- 'Samples':
+ - developers/samples/env.md
- developers/samples/php-ini.md
- developers/samples/robots-txt.md
- 'Guides':