2222/**
2323 * ⚙️ **Class EnvironmentLoader**
2424 *
25- * 🧩 **Purpose:**
26- * Provides a unified, consistent, and secure method for loading environment
27- * variables across all **Maatify** projects and libraries.
28- * Ensures correct prioritization between `.env` variants, safe immutability,
29- * and automatic timezone setup post-load.
25+ * 🎯 **Purpose:**
26+ * Provides a robust, unified mechanism for loading environment variables across
27+ * all **Maatify** libraries and applications, ensuring consistency, safety, and
28+ * predictable configuration behavior.
3029 *
31- * 🧠 **Priority Loading Order :**
32- * 1️⃣ `.env.local` — Local developer overrides
33- * 2️⃣ `.env.testing` — Test environment variables
34- * 3️⃣ `.env` — Default fallback for production or staging
35- * 4️⃣ `.env.example` — Used as a last-resort fallback for validation or defaults
30+ * 🧠 **Core Responsibilities :**
31+ * - Load environment variables from the correct file ( `.env.local`, `.env.testing`, `.env`, or `.env.example`).
32+ * - Prevent overriding system or CI-defined environment variables (immutable mode).
33+ * - Apply the application timezone automatically after loading.
34+ * - Throw explicit errors when no `.env` file is found.
3635 *
37- * ✅ **Key Features :**
38- * - Loads the first available file in the priority list.
39- * - Prevents overwriting of already defined environment variables (immutable mode).
40- * - Automatically applies application timezone via `date_default_timezone_set()`.
41- * - Throws clear exception if no `.env` file is found.
36+ * 🧩 **Priority Load Order :**
37+ * 1️⃣ `.env.local` — local developer overrides
38+ * 2️⃣ `.env.testing` — test environment variables
39+ * 3️⃣ `.env` — default environment file
40+ * 4️⃣ `.env.example` — fallback for defaults or CI builds
4241 *
43- * ⚙️ **Example Usage:**
42+ * ✅ **Example Usage:**
4443 * ```php
4544 * use Maatify\Bootstrap\Core\EnvironmentLoader;
4645 *
47- * // Initialize loader at the project root
46+ * // Initialize and load environment variables
4847 * $env = new EnvironmentLoader(__DIR__ . '/../');
4948 * $env->load();
5049 *
51- * echo 'Environment: ' . $_ENV['APP_ENV'] ?? 'unknown';
50+ * echo 'Environment: ' . ( $_ENV['APP_ENV'] ?? 'unknown') ;
5251 * ```
5352 *
5453 * @package Maatify\Bootstrap\Core
5554 */
5655final class EnvironmentLoader
5756{
5857 /**
59- * 📂 Base directory containing environment files.
58+ * 📂 ** Base Directory Path**
6059 *
61- * Typically this is the project root directory .
60+ * The directory that contains the environment files. Typically the project root.
6261 *
6362 * @var string
6463 */
@@ -67,46 +66,55 @@ public function __construct(private readonly string $basePath)
6766 }
6867
6968 /**
70- * 🎯 Load the appropriate `.env` file based on Maatify priority rules.
69+ * 🚀 ** Load Environment Variables**
7170 *
72- * The loader iterates over `.env.local`, `.env.testing`, `.env`, and `.env.example`
73- * in order, and loads the first existing file it encounters.
74- * It uses **immutable mode** to ensure that previously defined environment
75- * variables (via system or CI/CD) are not overridden.
71+ * Loads environment variables based on the Maatify priority rules.
72+ * The first matching file among `.env.local`, `.env.testing`, `.env`, or `.env.example`
73+ * is loaded using **immutable mode**, meaning system-level environment variables
74+ * (e.g., from Docker, CI/CD, or OS) will not be overridden.
7675 *
77- * 🧠 After successful loading, the application timezone is automatically
78- * set using the value of `APP_TIMEZONE` or defaults to **Africa/Cairo**.
76+ * 🧠 **Post-Load Behavior:**
77+ * - Ensures system-defined variables remain intact.
78+ * - Automatically applies timezone via `date_default_timezone_set()`.
79+ * - Throws clear exception if no `.env` file exists.
7980 *
80- * 🚫 Throws:
81- * - `Exception` when no environment file is found in the provided base path.
81+ * @throws Exception When no valid environment file is found.
8282 *
83- * @throws Exception If no `.env` file exists in the given directory.
8483 * @return void
8584 */
8685 public function load (): void
8786 {
88- // 🔍 Check environment files in order of precedence
87+ // 🔍 Priority order for environment files
8988 $ envFiles = ['.env.local ' , '.env.testing ' , '.env ' , '.env.example ' ];
9089 $ loaded = false ;
9190
91+ // Preserve pre-existing environment variables
92+ $ existing = $ _ENV ;
93+
9294 foreach ($ envFiles as $ file ) {
9395 $ path = $ this ->basePath . DIRECTORY_SEPARATOR . $ file ;
9496
95- // ✅ Load the first available file and stop checking further
97+ // ✅ Load first available file, stop after success
9698 if (is_file ($ path )) {
97- // 🧩 Use Dotenv's immutable mode for safety ( prevents accidental overrides)
99+ // 🧩 Load in immutable mode — prevents overriding pre-defined variables
98100 Dotenv::createImmutable ($ this ->basePath , $ file )->load ();
99101 $ loaded = true ;
100102 break ;
101103 }
102104 }
103105
104- // 🚫 Throw if no valid environment file was found
106+ // 🚫 Throw if no valid .env file found
105107 if (! $ loaded ) {
106108 throw new Exception ('No .env file found in ' . $ this ->basePath );
107109 }
108110
109- // 🕒 Set timezone from environment or default to Africa/Cairo
111+ // ♻️ Restore original environment variables (maintain immutability guarantee)
112+ foreach ($ existing as $ key => $ value ) {
113+ $ _ENV [$ key ] = $ value ;
114+ putenv ("$ key= $ value " );
115+ }
116+
117+ // 🕒 Apply timezone setting (default: Africa/Cairo)
110118 $ timezone = $ _ENV ['APP_TIMEZONE ' ]
111119 ?? $ _SERVER ['APP_TIMEZONE ' ]
112120 ?? 'Africa/Cairo ' ;
0 commit comments