Skip to content

Commit bd69615

Browse files
Now you can set and update empty env-variables. Now you can specify external .env-file as the third optional argument. Now you can set the value with equals sign ("="). Added a lot of unit-tests. Added travis-ci integration. composer.json now includes needed laravel components. Fixed compatibility with Laravel 6+.
1 parent 0dffddd commit bd69615

10 files changed

+544
-74
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ trim_trailing_whitespace = true
1313

1414
[*.md]
1515
trim_trailing_whitespace = false
16+
17+
[*.{yml,yaml}]
18+
indent_size = 2

.gitignore

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
build
2-
composer.lock
3-
docs
4-
vendor
5-
coverage
6-
SCRATCH.md
1+
/.idea/
2+
/nbproject/
3+
/build
4+
/composer.lock
5+
/docs
6+
/vendor
7+
/coverage
8+
/SCRATCH.md
9+
/REFACTORING.md

.travis.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
language: php
2+
3+
dist: bionic
4+
5+
branches:
6+
only:
7+
- master
8+
- v1.x
9+
10+
cache:
11+
directories:
12+
- $HOME/.composer/cache
13+
- $HOME/.cache
14+
env:
15+
- LARAVEL_VERSION="~5.3"
16+
- LARAVEL_VERSION="~5.4"
17+
- LARAVEL_VERSION="~5.5"
18+
- LARAVEL_VERSION="~5.6"
19+
- LARAVEL_VERSION="~5.7"
20+
- LARAVEL_VERSION="~5.8"
21+
- LARAVEL_VERSION="~6.0"
22+
- LARAVEL_VERSION="~7.0"
23+
24+
matrix:
25+
include:
26+
- php: 7.1
27+
- php: 7.2
28+
- php: 7.3
29+
- php: 7.4
30+
31+
before_install:
32+
- composer self-update
33+
34+
install:
35+
- composer require laravel/framework:${LARAVEL_VERSION}
36+
37+
script:
38+
- vendor/bin/phpunit --verbose

CHANGELOG.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
# Changelog
2+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
3+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
24

3-
All notable changes to `laravel-env-set-command` will be documented in this file
5+
## [Unreleased]
6+
### Added
7+
### Changed
8+
### Deprecated
9+
### Removed
10+
### Fixed
11+
### Security
412

5-
## 1.0.0 - 2018-07-13
613

14+
## [1.1.0] - 2020-05-12
15+
### Added
16+
- Now you can set and update empty env-variables.
17+
- Now you can specify external .env-file as the third optional argument.
18+
- Now you can set the value with equals sign ("=").
19+
- Added a lot of unit-tests.
20+
- Added travis-ci integration.
21+
### Changed
22+
- composer.json now includes needed laravel components.
23+
### Fixed
24+
- Fixed compatibility with Laravel 6+.
25+
26+
## [1.0.0] - 2018-07-13
27+
### Added
728
- Initial release
29+
30+
[1.1.0]: https://github.com/imliam/laravel-env-set-command/compare/1.0.0...1.1.0

composer.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,24 @@
2020
}
2121
],
2222
"require": {
23-
"php": "^7.1"
23+
"php": "^7.1",
24+
"illuminate/support": "*",
25+
"illuminate/console": "*"
26+
},
27+
"require-dev": {
28+
"phpunit/phpunit": "^7.5",
29+
"roave/security-advisories": "dev-master"
2430
},
2531
"autoload": {
2632
"psr-4": {
2733
"ImLiam\\EnvironmentSetCommand\\": "src"
2834
}
2935
},
36+
"autoload-dev": {
37+
"psr-4": {
38+
"Tests\\": "tests/"
39+
}
40+
},
3041
"config": {
3142
"sort-packages": true
3243
},

phpunit.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
>
7+
<testsuites>
8+
<testsuite name="Unit">
9+
<directory suffix="Test.php">./tests/Unit</directory>
10+
</testsuite>
11+
<testsuite name="Feature">
12+
<directory suffix="Test.php">./tests/Feature</directory>
13+
</testsuite>
14+
</testsuites>
15+
<filter>
16+
<whitelist processUncoveredFilesFromWhitelist="true">
17+
<directory suffix=".php">./app</directory>
18+
</whitelist>
19+
</filter>
20+
<php>
21+
<ini name="display_errors" value="true"/>
22+
</php>
23+
</phpunit>

src/EnvironmentSetCommand.php

Lines changed: 85 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@
22

33
namespace ImLiam\EnvironmentSetCommand;
44

5+
use Illuminate\Console\Command;
6+
use Illuminate\Support\Facades\App;
57
use Illuminate\Support\Str;
68
use InvalidArgumentException;
7-
use Illuminate\Console\Command;
89

910
class EnvironmentSetCommand extends Command
1011
{
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+
1117
/**
1218
* The name and signature of the console command.
1319
*
1420
* @var string
1521
*/
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}';
1727

1828
/**
1929
* The console command description.
@@ -22,127 +32,139 @@ class EnvironmentSetCommand extends Command
2232
*/
2333
protected $description = 'Set and save an environment variable in the .env file';
2434

25-
/**
26-
* Create a new command instance.
27-
*
28-
* @return void
29-
*/
30-
public function __construct()
31-
{
32-
parent::__construct();
33-
}
34-
3535
/**
3636
* Execute the console command.
37-
*
38-
* @return mixed
3937
*/
40-
public function handle()
38+
public function handle(): void
4139
{
4240
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;
4649
}
4750

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);
5053

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}'");
5659
}
5760

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);
6262
}
6363

6464
/**
65-
* Overwrite the contents of a file.
65+
* Set or update env-variable.
6666
*
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].
7072
*/
71-
protected function writeFile(string $path, string $contents): bool
73+
public function setEnvVariable(string $envFileContent, string $key, string $value): array
7274
{
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+
}
7580

76-
return fclose($file);
81+
// For a new key.
82+
return [$envFileContent . "\n" . $key . '=' . $value . "\n", true];
7783
}
7884

7985
/**
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.
8188
*
82-
* @param string $envFile
89+
* @param string $envFileContent
8390
* @param string $key
84-
* @return string
91+
*
92+
* @return string|null Key=value string or null if the key is not exists.
8593
*/
86-
protected function getOldValue(string $envFile, string $key): string
94+
public function readKeyValuePair(string $envFileContent, string $key): ?string
8795
{
8896
// 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)) {
9298
return $matches[0];
9399
}
94100

95-
return '';
101+
return null;
96102
}
97103

98104
/**
99105
* Determine what the supplied key and value is from the current command.
100106
*
101-
* @return array
107+
* @param string $key
108+
* @param string|null $value
109+
*
110+
* @return string[]
111+
* @throws InvalidArgumentException
102112
*/
103-
protected function getKeyValue(): array
113+
public function parseKeyValueArguments(string $key, ?string $value): array
104114
{
105-
$key = $this->argument('key');
106-
$value = $this->argument('value');
107-
108-
if (! $value) {
115+
// Parse "key=value" key argument.
116+
if ($value === null) {
109117
$parts = explode('=', $key, 2);
110-
111118
if (count($parts) !== 2) {
112119
$key = $parts[0];
120+
$value = '';
113121
} else {
114-
$key = $parts[0];
115-
$value = $parts[1];
122+
[$key, $value] = $parts;
116123
}
117124
}
118125

119-
if (! $this->isValidKey($key)) {
120-
throw new InvalidArgumentException('Invalid argument key');
121-
}
126+
$this->assertKeyIsValid($key);
122127

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)) {
124130
$value = '"' . $value . '"';
125131
}
126132

127133
return [strtoupper($key), $value];
128134
}
129135

130136
/**
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.
132138
*
133139
* @param string $key
134-
* @return boolean
140+
*
141+
* @return bool Is key is valid.
135142
*/
136-
protected function isValidKey(string $key): bool
143+
public function assertKeyIsValid(string $key): bool
137144
{
138145
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 '='");
140148
}
141149

142150
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');
144153
}
145154

146155
return true;
147156
}
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+
}
148170
}

src/EnvironmentSetCommandServiceProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ class EnvironmentSetCommandServiceProvider extends ServiceProvider
99
/**
1010
* Bootstrap the application services.
1111
*/
12-
public function boot()
12+
public function boot(): void
1313
{
1414
// ...
1515
}
1616

1717
/**
1818
* Register the application services.
1919
*/
20-
public function register()
20+
public function register(): void
2121
{
2222
$this->app->bind('command.env:set', EnvironmentSetCommand::class);
2323

0 commit comments

Comments
 (0)