88
99class Env
1010{
11- private $ fileData = [];
12- private $ data = [];
13- private $ set = [];
14- private $ drop = [];
15-
16- public function __construct (?string $ file = null )
17- {
18- if ($ file !== null && is_file ($ file )) {
19- $ this ->loadEnvFile ($ file );
20- }
21- }
22-
23- public function loadEnvFile (string $ file ): void
24- {
25- $ this ->fileData = parse_ini_file ($ file );
26- }
27-
28- public function hasEnv (string $ key ): ?string
29- {
30- $ key = $ this ->formatKey ($ key );
31- return (isset ($ this ->fileData [$ key ])) ? $ key : null ;
32- }
33-
34- public function set (string $ key , string $ value ): string
35- {
36- if ($ keyB = $ this ->hasEnv ($ key )) {
37- $ this ->fileData [$ keyB ] = $ value ;
38- } else {
39- $ key = $ this ->formatKey ($ key );
40- $ this ->set [$ key ] = $ value ;
41- }
42- return "{$ key }= {$ value }" ;
43- }
44-
45- public function get (string $ key ): string
46- {
47- $ key = $ this ->formatKey ($ key );
48- return ($ this ->fileData [$ key ] ?? "" );
49- }
50-
51- public function drop (string $ key ): void
52- {
53- $ key = $ this ->formatKey ($ key );
54- $ this ->drop [$ key ] = $ key ;
55- }
56-
57- public function formatKey ($ key )
58- {
59- return Format \Str::value ($ key )->clearBreaks ()->trim ()->normalizeAccents ()
60- ->normalizeSeparators ()->replaceSpaces ("- " )->toUpper ()->get ();
61- }
62-
63- public function generateOutput (array $ fromArr = ["data " , "fileData " , "set " ])
64- {
65- $ out = "" ;
66-
67- $ data = [];
68- $ validData = ["data " , "fileData " , "set " ];
69- foreach ($ validData as $ d ) {
70- if (in_array ($ d , $ fromArr )) {
71- $ data += $ this ->{$ d };
72- }
73- }
74-
75- $ length = count ($ data );
76- foreach ($ data as $ key => $ val ) {
77- if (empty ($ this ->drop [$ key ])) {
78- $ key = $ this ->formatKey ($ key );
79- $ val = trim ($ val );
80- if (!is_numeric ($ val ) && ($ val !== "true " || $ val !== false )) {
81- $ val = "' {$ val }' " ;
82- }
83- $ out .= "{$ key }= {$ val }" ;
84- if ($ length > 1 ) {
85- $ out .= "\n" ;
86- }
87- }
88- }
89- return $ out ;
90- }
91-
92- public function putenv ($ key , $ value ): self
93- {
94- $ this ->data [$ key ] = $ value ;
95- return $ this ;
96- }
97-
98- public function putenvArray (array $ array ): self
99- {
100- foreach ($ array as $ prefix => $ val ) {
101- $ prefix = strtoupper ($ prefix );
102- if (is_array ($ val )) {
103- foreach ($ val as $ k1 => $ v1 ) {
104- foreach ($ v1 as $ k2 => $ v2 ) {
105- $ newKey = strtoupper ("{$ k1 }_ {$ k2 }" );
106- if (!isset ($ this ->fileData [$ newKey ])) {
107- $ this ->data [$ newKey ] = $ v2 ;
108- }
109- }
110- }
111- } else {
112- $ this ->data [$ prefix ] = $ val ;
113- }
114- }
115- $ this ->data = array_merge ($ this ->data , $ array );
116- return $ this ;
117- }
118-
119- private function put (array $ data , bool $ overwrite = false )
120- {
121- foreach ($ data as $ key => $ value ) {
122- /*
123- if (!$overwrite && getenv($key) !== false) {
124- throw new InvalidArgumentException("The Environmental variable \"{$key}\" already exists. " .
125- "It's recommended to make every variable unique.", 1);
126- }
127- */
128- $ _ENV [$ key ] = $ value ;
129- if (is_array ($ value )) {
130- $ value = json_encode ($ value );
131- }
132- putenv ("{$ key }= {$ value }" );
133- }
134- }
135-
136- public function execute (bool $ overwrite = false ): void
137- {
138- if ($ this ->fileData ) {
139- $ this ->put ($ this ->fileData , $ overwrite );
140- }
141- if ($ this ->data ) {
142- $ this ->put ($ this ->data , $ overwrite );
143- }
144- }
145-
146- public function getData ()
147- {
148- return $ this ->data + $ this ->fileData + $ this ->set ;
149- }
150- }
11+ private static array $ registry = []; // Static registry, survives reloads correctly
12+ private array $ fileData = [];
13+ private array $ data = [];
14+ private array $ set = [];
15+ private array $ drop = [];
16+
17+ public function __construct (?string $ file = null )
18+ {
19+ if ($ file !== null && is_file ($ file )) {
20+ $ this ->loadEnvFile ($ file );
21+ }
22+ }
23+
24+ public function loadEnvFile (string $ file ): void
25+ {
26+ if (!is_file ($ file )) {
27+ throw new \RuntimeException ("Environment file not found: $ file " );
28+ }
29+
30+ $ lines = file ($ file , FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
31+ $ cleaned = array_filter ($ lines , fn ($ line ) => !str_starts_with (trim ($ line ), '# ' ));
32+ $ data = parse_ini_string (implode ("\n" , $ cleaned ), false , INI_SCANNER_RAW );
33+
34+ if ($ data === false ) {
35+ throw new \RuntimeException (
36+ "Failed to parse .env file: $ file \n" .
37+ "Hint: wrap values containing special characters (;, |, $) in double quotes. "
38+ );
39+ }
40+
41+ $ this ->fileData = $ data ;
42+ }
43+
44+ public function hasEnv (string $ key ): ?string
45+ {
46+ $ key = $ this ->formatKey ($ key );
47+ return isset ($ this ->fileData [$ key ]) ? $ key : null ;
48+ }
49+
50+ public function set (string $ key , string $ value ): string
51+ {
52+ if ($ keyB = $ this ->hasEnv ($ key )) {
53+ $ this ->fileData [$ keyB ] = $ value ;
54+ } else {
55+ $ key = $ this ->formatKey ($ key );
56+ $ this ->set [$ key ] = $ value ;
57+ }
58+ return "{$ key }= {$ value }" ;
59+ }
60+
61+ public function get (string $ key ): string
62+ {
63+ $ key = $ this ->formatKey ($ key );
64+ return self ::$ registry [$ key ] ?? "" ;
65+ }
66+
67+ public function drop (string $ key ): void
68+ {
69+ $ key = $ this ->formatKey ($ key );
70+ $ this ->drop [$ key ] = $ key ;
71+ }
72+
73+ public function formatKey (string $ key ): string
74+ {
75+ return Format \Str::value ($ key )->clearBreaks ()->trim ()->normalizeAccents ()
76+ ->normalizeSeparators ()->replaceSpaces ("- " )->toUpper ()->get ();
77+ }
78+
79+ public function generateOutput (array $ fromArr = ["data " , "fileData " , "set " ]): string
80+ {
81+ $ out = "" ;
82+ $ data = [];
83+ $ validData = ["data " , "fileData " , "set " ];
84+
85+ foreach ($ validData as $ d ) {
86+ if (in_array ($ d , $ fromArr )) {
87+ $ data += $ this ->{$ d };
88+ }
89+ }
90+
91+ $ length = count ($ data );
92+ foreach ($ data as $ key => $ val ) {
93+ if (empty ($ this ->drop [$ key ])) {
94+ $ key = $ this ->formatKey ($ key );
95+ $ val = trim ($ val );
96+ if (!is_numeric ($ val ) && ($ val !== "true " && $ val !== "false " )) {
97+ $ val = "' {$ val }' " ;
98+ }
99+ $ out .= "{$ key }= {$ val }" ;
100+ if ($ length > 1 ) {
101+ $ out .= "\n" ;
102+ }
103+ }
104+ }
105+ return $ out ;
106+ }
107+
108+ public function putenv (string $ key , string $ value ): self
109+ {
110+ $ this ->data [$ key ] = $ value ;
111+ return $ this ;
112+ }
113+
114+ public function putenvArray (array $ array ): self
115+ {
116+ foreach ($ array as $ prefix => $ val ) {
117+ $ prefix = strtoupper ($ prefix );
118+ if (is_array ($ val )) {
119+ foreach ($ val as $ k1 => $ v1 ) {
120+ foreach ($ v1 as $ k2 => $ v2 ) {
121+ $ newKey = strtoupper ("{$ k1 }_ {$ k2 }" );
122+ if (!isset ($ this ->fileData [$ newKey ])) {
123+ $ this ->data [$ newKey ] = $ v2 ;
124+ }
125+ }
126+ }
127+ } else {
128+ $ this ->data [$ prefix ] = $ val ;
129+ }
130+ }
131+ $ this ->data = array_merge ($ this ->data , $ array );
132+ return $ this ;
133+ }
134+
135+ public function execute (bool $ overwrite = false ): void
136+ {
137+ $ merged = $ this ->getData ();
138+
139+ // Fully replace the registry on every execute — no bleed-through
140+ self ::$ registry = [];
141+
142+ foreach ($ merged as $ key => $ value ) {
143+ if (isset ($ this ->drop [$ key ])) {
144+ continue ;
145+ }
146+ if (!$ overwrite && isset (self ::$ registry [$ key ])) {
147+ continue ;
148+ }
149+ self ::$ registry [$ key ] = $ value ;
150+ }
151+ }
152+
153+ public static function getFromRegistry (string $ key ): mixed
154+ {
155+ return self ::$ registry [$ key ] ?? null ;
156+ }
157+
158+ public function getData (): array
159+ {
160+ return $ this ->data + $ this ->fileData + $ this ->set ;
161+ }
162+ }
0 commit comments