2
2
3
3
namespace ImLiam \EnvironmentSetCommand ;
4
4
5
+ use Illuminate \Console \Command ;
6
+ use Illuminate \Support \Facades \App ;
5
7
use Illuminate \Support \Str ;
6
8
use InvalidArgumentException ;
7
- use Illuminate \Console \Command ;
8
9
9
10
class EnvironmentSetCommand extends Command
10
11
{
12
+ public const COMMAND_NAME = 'env:set ' ;
13
+ public const ARGUMENT_KEY = 'key ' ;
14
+ public const ARGUMENT_VALUE = 'value ' ;
15
+ public const ARGUMENT_ENV_FILE = 'env_file ' ;
16
+
11
17
/**
12
18
* The name and signature of the console command.
13
19
*
14
20
* @var string
15
21
*/
16
- protected $ signature = 'env:set {key} {value?} ' ;
22
+ protected $ signature
23
+ = self ::COMMAND_NAME
24
+ . '{ ' . self ::ARGUMENT_KEY . ' : Key or key=value pair} '
25
+ . '{ ' . self ::ARGUMENT_VALUE . '? : Value} '
26
+ . '{ ' . self ::ARGUMENT_ENV_FILE . '? : Optional path to the .env file} ' ;
17
27
18
28
/**
19
29
* The console command description.
@@ -22,127 +32,139 @@ class EnvironmentSetCommand extends Command
22
32
*/
23
33
protected $ description = 'Set and save an environment variable in the .env file ' ;
24
34
25
- /**
26
- * Create a new command instance.
27
- *
28
- * @return void
29
- */
30
- public function __construct ()
31
- {
32
- parent ::__construct ();
33
- }
34
-
35
35
/**
36
36
* Execute the console command.
37
- *
38
- * @return mixed
39
37
*/
40
- public function handle ()
38
+ public function handle (): void
41
39
{
42
40
try {
43
- [$ key , $ value ] = $ this ->getKeyValue ();
44
- } catch (\InvalidArgumentException $ e ) {
45
- return $ this ->error ($ e ->getMessage ());
41
+ // Parse key and value arguments.
42
+ [$ key , $ value ] = $ this ->parseKeyValueArguments (
43
+ $ this ->argument (self ::ARGUMENT_KEY ),
44
+ $ this ->argument (self ::ARGUMENT_VALUE )
45
+ );
46
+ } catch (InvalidArgumentException $ e ) {
47
+ $ this ->error ($ e ->getMessage ());
48
+ return ;
46
49
}
47
50
48
- $ envFilePath = app ()-> environmentFilePath ();
49
- $ contents = file_get_contents ($ envFilePath );
51
+ $ envFilePath = realpath ( $ this -> argument ( self :: ARGUMENT_ENV_FILE ) ?? App:: environmentFilePath () );
52
+ $ content = file_get_contents ($ envFilePath );
50
53
51
- if ( $ oldValue = $ this ->getOldValue ( $ contents , $ key)) {
52
- $ contents = str_replace ( "{ $ oldValue }" , "{ $ key } = { $ value }" , $ contents );
53
- $ this ->writeFile ( $ envFilePath , $ contents );
54
-
55
- return $ this ->info ("Environment variable with key ' {$ key }' has been updated to ' {$ value }' " );
54
+ [ $ newEnvFileContent , $ isNewVariableSet ] = $ this ->setEnvVariable ( $ content , $ key, $ value );
55
+ if ( $ isNewVariableSet ) {
56
+ $ this ->info ( " A new environment variable with key ' { $ key } ' has been set to ' { $ value } ' " );
57
+ } else {
58
+ $ this ->info ("Environment variable with key ' {$ key }' has been updated to ' {$ value }' " );
56
59
}
57
60
58
- $ contents = $ contents . "\n{$ key }= {$ value }\n" ;
59
- $ this ->writeFile ($ envFilePath , $ contents );
60
-
61
- return $ this ->info ("A new environment variable with key ' {$ key }' has been set to ' {$ value }' " );
61
+ $ this ->writeFile ($ envFilePath , $ newEnvFileContent );
62
62
}
63
63
64
64
/**
65
- * Overwrite the contents of a file .
65
+ * Set or update env-variable .
66
66
*
67
- * @param string $path
68
- * @param string $contents
69
- * @return boolean
67
+ * @param string $envFileContent Content of the .env file.
68
+ * @param string $key Name of the variable.
69
+ * @param string $value Value of the variable.
70
+ *
71
+ * @return array [string newEnvFileContent, bool isNewVariableSet].
70
72
*/
71
- protected function writeFile (string $ path , string $ contents ): bool
73
+ public function setEnvVariable (string $ envFileContent , string $ key , string $ value ): array
72
74
{
73
- $ file = fopen ($ path , 'w ' );
74
- fwrite ($ file , $ contents );
75
+ // For existed key.
76
+ $ oldKeyValuePair = $ this ->readKeyValuePair ($ envFileContent , $ key );
77
+ if ($ oldKeyValuePair !== null ) {
78
+ return [str_replace ($ oldKeyValuePair , $ key . '= ' . $ value , $ envFileContent ), false ];
79
+ }
75
80
76
- return fclose ($ file );
81
+ // For a new key.
82
+ return [$ envFileContent . "\n" . $ key . '= ' . $ value . "\n" , true ];
77
83
}
78
84
79
85
/**
80
- * Get the old value of a given key from an environment file.
86
+ * Read the "key=value" string of a given key from an environment file.
87
+ * This function returns original "key=value" string and doesn't modify it.
81
88
*
82
- * @param string $envFile
89
+ * @param string $envFileContent
83
90
* @param string $key
84
- * @return string
91
+ *
92
+ * @return string|null Key=value string or null if the key is not exists.
85
93
*/
86
- protected function getOldValue (string $ envFile , string $ key ): string
94
+ public function readKeyValuePair (string $ envFileContent , string $ key ): ? string
87
95
{
88
96
// Match the given key at the beginning of a line
89
- preg_match ("/^ {$ key }=[^ \r\n]*/m " , $ envFile , $ matches );
90
-
91
- if (count ($ matches )) {
97
+ if (preg_match ("#^ * {$ key } *= *[^ \r\n]*$#imu " , $ envFileContent , $ matches )) {
92
98
return $ matches [0 ];
93
99
}
94
100
95
- return '' ;
101
+ return null ;
96
102
}
97
103
98
104
/**
99
105
* Determine what the supplied key and value is from the current command.
100
106
*
101
- * @return array
107
+ * @param string $key
108
+ * @param string|null $value
109
+ *
110
+ * @return string[]
111
+ * @throws InvalidArgumentException
102
112
*/
103
- protected function getKeyValue ( ): array
113
+ public function parseKeyValueArguments ( string $ key , ? string $ value ): array
104
114
{
105
- $ key = $ this ->argument ('key ' );
106
- $ value = $ this ->argument ('value ' );
107
-
108
- if (! $ value ) {
115
+ // Parse "key=value" key argument.
116
+ if ($ value === null ) {
109
117
$ parts = explode ('= ' , $ key , 2 );
110
-
111
118
if (count ($ parts ) !== 2 ) {
112
119
$ key = $ parts [0 ];
120
+ $ value = '' ;
113
121
} else {
114
- $ key = $ parts [0 ];
115
- $ value = $ parts [1 ];
122
+ [$ key , $ value ] = $ parts ;
116
123
}
117
124
}
118
125
119
- if (! $ this ->isValidKey ($ key )) {
120
- throw new InvalidArgumentException ('Invalid argument key ' );
121
- }
126
+ $ this ->assertKeyIsValid ($ key );
122
127
123
- if (! is_bool (strpos ($ value , ' ' ))) {
128
+ // If the value contains spaces but not is not enclosed in quotes.
129
+ if (preg_match ('#^[^ \'"].*\s+.*[^ \'"]$#um ' , $ value )) {
124
130
$ value = '" ' . $ value . '" ' ;
125
131
}
126
132
127
133
return [strtoupper ($ key ), $ value ];
128
134
}
129
135
130
136
/**
131
- * Check if a given string is valid as an environment variable key.
137
+ * Assert a given string is valid as an environment variable key.
132
138
*
133
139
* @param string $key
134
- * @return boolean
140
+ *
141
+ * @return bool Is key is valid.
135
142
*/
136
- protected function isValidKey (string $ key ): bool
143
+ public function assertKeyIsValid (string $ key ): bool
137
144
{
138
145
if (Str::contains ($ key , '= ' )) {
139
- throw new InvalidArgumentException ("Environment key should not contain '=' " );
146
+ throw new InvalidArgumentException ('Invalid environment key ' . $ key
147
+ . "! Environment key should not contain '=' " );
140
148
}
141
149
142
150
if (!preg_match ('/^[a-zA-Z_]+$/ ' , $ key )) {
143
- throw new InvalidArgumentException ('Invalid environment key. Only use letters and underscores ' );
151
+ throw new InvalidArgumentException ('Invalid environment key ' . $ key
152
+ . '! Only use letters and underscores ' );
144
153
}
145
154
146
155
return true ;
147
156
}
157
+
158
+ /**
159
+ * Overwrite the contents of a file.
160
+ *
161
+ * @param string $path
162
+ * @param string $contents
163
+ *
164
+ * @return boolean
165
+ */
166
+ protected function writeFile (string $ path , string $ contents ): bool
167
+ {
168
+ return (bool )file_put_contents ($ path , $ contents , LOCK_EX );
169
+ }
148
170
}
0 commit comments