├── .gitignore ├── banner.png ├── .env ├── src ├── Exceptions │ ├── KeyNotFoundException.php │ └── KeyAlreadyExistsException.php ├── Facades │ ├── Env.php │ └── EnvLine.php ├── Listeners │ └── EnvFileChanged.php ├── Providers │ └── EventServiceProvider.php ├── Events │ └── EnvFileChangedEvent.php ├── EnvServiceProvider.php ├── Line.php └── Env.php ├── .editorconfig ├── canvas.yaml ├── tests ├── TestCase.php └── Unit │ └── EnvTest.php ├── phpunit.xml ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebuglab/laravel-env/HEAD/banner.png -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | APP_KEY="base64:2fl+Ktvkfl+Fuz4Qp/A75G2RTiWVA/ZoKZvp6fiiM10=" 2 | DB_CONNECTION="1623755670" 3 | APP_URL=domain.test 4 | -------------------------------------------------------------------------------- /src/Exceptions/KeyNotFoundException.php: -------------------------------------------------------------------------------- 1 | line; 13 | 14 | return Artisan::call('config:clear'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 13 | EnvFileChanged::class 14 | ], 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /canvas.yaml: -------------------------------------------------------------------------------- 1 | preset: package 2 | 3 | namespace: CodeBugLab\Env 4 | user-auth-provider: App\User 5 | 6 | paths: 7 | src: src 8 | resource: resources 9 | 10 | factory: 11 | path: database/factories 12 | 13 | migration: 14 | path: database/migrations 15 | prefix: '' 16 | 17 | console: 18 | namespace: CodeBugLab\Env\Console 19 | 20 | model: 21 | namespace: CodeBugLab\Env 22 | 23 | provider: 24 | namespace: CodeBugLab\Env\Providers 25 | 26 | testing: 27 | namespace: CodeBugLab\Env\Tests 28 | -------------------------------------------------------------------------------- /src/Events/EnvFileChangedEvent.php: -------------------------------------------------------------------------------- 1 | line = $line; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/EnvServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->register(EventServiceProvider::class); 13 | 14 | $this->app->bind('env', function ($app) { 15 | return new Env(); 16 | }); 17 | 18 | $this->app->bind('envLine', function ($app) { 19 | return new Line(app('Env')); 20 | }); 21 | } 22 | 23 | public function boot() 24 | { 25 | // 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | useEnvironmentPath(__DIR__ . '/..'); 21 | $app->bootstrapWith([LoadEnvironmentVariables::class]); 22 | 23 | parent::getEnvironmentSetUp($app); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | src/ 19 | 20 | 21 | 22 | 23 | ./tests/Unit 24 | 25 | 26 | ./tests/Feature 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codebuglab/laravel-env", 3 | "description": "Create and update .env file pacakge", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Amro Khaled", 9 | "email": "i@akhaled.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^7.0", 14 | "illuminate/support": "^6.0|^7.0|^8.0" 15 | }, 16 | "require-dev": { 17 | "orchestra/testbench": "^4.0", 18 | "orchestra/canvas": "^4.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "CodeBugLab\\Env\\": "src" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "CodeBugLab\\Env\\Tests\\": "tests" 28 | } 29 | }, 30 | "extra": { 31 | "laravel": { 32 | "providers": [ 33 | "CodeBugLab\\Env\\EnvServiceProvider" 34 | ], 35 | "aliases": { 36 | "Env": "CodeBugLab\\Env\\Facades\\Env", 37 | "EnvLine": "CodeBugLab\\Env\\Facades\\EnvLine" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CodeBugLab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Env 2 | 3 | Laravel small package to locate, append and update `.env` keys. 4 | 5 | ![Laravel env](banner.png) 6 | 7 | ## Disclaimer 8 | 9 | **TO BE ABLE TO UPDATE `.env` FILE THROUGH BROWSER YOU MAY NEED TO CHANGE `.env` FILE PERMISSIONS TO _755_. THIS WILL MAKE YOUR FILE WRITABLE VIA BROWSER.** 10 | 11 | **YOU SHOULD NOT HAVE `.env` IN YOUR PUBLIC ROOT, EX: `public_html`** 12 | 13 | 14 | ## Table of contents 15 | - [Setup](#setup) 16 | - [Installation](#installation) 17 | - [Available methods](#available-methods) 18 | - [get](#get) 19 | - [append](#append) 20 | - [replace](#replace) 21 | - [delete](#delete) 22 | - [locate](#locate) 23 | - [reset](#reset) 24 | 25 | ## Setup 26 | ### Installation 27 | 28 | To install this package through composer run the following command in the terminal 29 | 30 | ```bash 31 | composer require codebuglab/laravel-env 32 | ``` 33 | 34 | ## Available methods 35 | 36 | ### get 37 | 38 | ```php 39 | \CodeBugLab\Env::get('APP_KEY', 'default'); 40 | ``` 41 | 42 | ### append 43 | 44 | ```php 45 | \CodeBugLab\Env::append('LOG_CHANNEL', 'daily'); 46 | ``` 47 | 48 | ### replace 49 | 50 | ```php 51 | \CodeBugLab\Env::replace('APP_KEY', 'another key'); 52 | ``` 53 | 54 | ### delete 55 | 56 | ```php 57 | \CodeBugLab\Env::delete('FOO'); 58 | ``` 59 | 60 | ### locate 61 | 62 | Returns a `CodeBugLab\Env\Line` object 63 | 64 | ```php 65 | $line = \CodeBugLab\Env::locate('APP_KEY'); 66 | ``` 67 | 68 | ### reset 69 | 70 | Set to empty value 71 | 72 | ```php 73 | \CodeBugLab\Env::reset('APP_KEY'); // APP_KEY="" 74 | ``` 75 | -------------------------------------------------------------------------------- /tests/Unit/EnvTest.php: -------------------------------------------------------------------------------- 1 | environmentFilePath(), 19 | preg_replace( 20 | "/FOO.*\n/", 21 | "", 22 | file_get_contents(app()->environmentFilePath()) 23 | ) 24 | ); 25 | } 26 | 27 | public function test_it_reads_a_value_from_env_file() 28 | { 29 | $app_key = Env::get("APP_KEY"); 30 | 31 | $this->assertEquals($app_key, "base64:2fl+Ktvkfl+Fuz4Qp/A75G2RTiWVA/ZoKZvp6fiiM10="); 32 | } 33 | 34 | public function test_it_gets_null_for_line_number_if_key_not_found() 35 | { 36 | $line = Env::locate("KEY_NOT_EXISTS"); 37 | 38 | $this->assertNull($line); 39 | } 40 | 41 | public function test_it_gets_line_number_when_key_passed() 42 | { 43 | $line = Env::locate("DB_CONNECTION"); 44 | 45 | $this->assertInstanceOf(Line::class, $line); 46 | $this->assertEquals(2, $line->getLineNumber()); 47 | $this->assertEquals('DB_CONNECTION', $line->getKey()); 48 | } 49 | 50 | public function test_it_throws_an_exception_if_appended_key_exists() 51 | { 52 | $this->expectException(KeyAlreadyExistsException::class); 53 | 54 | Env::append("DB_CONNECTION", time()); 55 | } 56 | 57 | public function test_it_returns_success_if_append_key_is_done() 58 | { 59 | $this->deleteFooKey(); 60 | 61 | Event::fake(); 62 | 63 | $append = Env::append("FOO", time()); 64 | 65 | $this->assertTrue($append); 66 | Event::assertDispatched(EnvFileChangedEvent::class); 67 | 68 | $this->deleteFooKey(); 69 | } 70 | 71 | public function test_it_throws_key_not_found_exception_when_replacing() 72 | { 73 | $this->expectException(KeyNotFoundException::class); 74 | 75 | Env::replace("KEY_NOT_EXISTS", time()); 76 | } 77 | 78 | public function test_it_replaces_given_text() 79 | { 80 | Event::fake(); 81 | $replaced = Env::replace("DB_CONNECTION", time()); 82 | 83 | $this->assertTrue($replaced); 84 | Event::assertDispatched(EnvFileChangedEvent::class); 85 | } 86 | 87 | public function test_it_deletes_a_key() 88 | { 89 | $this->deleteFooKey(); 90 | 91 | Event::fake(); 92 | Env::append("FOO", time()); 93 | 94 | $deleted = Env::delete("FOO"); 95 | 96 | $this->assertTrue($deleted); 97 | Event::assertDispatched(EnvFileChangedEvent::class); 98 | } 99 | 100 | public function test_it_resets_a_value_for_a_line() 101 | { 102 | $this->deleteFooKey(); 103 | Env::append("FOO", time()); 104 | 105 | Env::reset("FOO"); 106 | 107 | $this->assertEmpty(Env::get("FOO")); 108 | 109 | $this->deleteFooKey(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Line.php: -------------------------------------------------------------------------------- 1 | Env = $Env; 19 | } 20 | 21 | public function getLineNumber() 22 | { 23 | return $this->lineNumber; 24 | } 25 | 26 | public function setLineNumber(int $lineNumber) 27 | { 28 | $this->lineNumber = $lineNumber; 29 | 30 | return $this; 31 | } 32 | 33 | public function getKey() 34 | { 35 | return $this->key; 36 | } 37 | 38 | public function setKey(string $key) 39 | { 40 | $this->key = $key; 41 | 42 | return $this; 43 | } 44 | 45 | public function getValue() 46 | { 47 | return $this->value; 48 | } 49 | 50 | public function setValue($value) 51 | { 52 | $this->value = $value; 53 | 54 | return $this; 55 | } 56 | 57 | /** 58 | * Get full line value 59 | * 60 | * @return string 61 | */ 62 | public function getFullLine() 63 | { 64 | if (strlen($this->getKey()) > 0) { 65 | $this->setFullLine( 66 | sprintf('%s="%s"', $this->getKey(), $this->getValue()) 67 | ); 68 | } 69 | 70 | return $this->fullLine; 71 | } 72 | 73 | /** 74 | * Set full line form key and value 75 | * 76 | * @param string $fullLine 77 | * @return self 78 | */ 79 | public function setFullLine(string $fullLine) 80 | { 81 | $this->fullLine = $fullLine; 82 | $exploded = explode("=", $fullLine); 83 | 84 | $value = $exploded[1]; 85 | if (preg_match('/^(["\']).*\1$/m', $value)) { 86 | $value = substr($value, 1, -1); 87 | } 88 | 89 | return $this 90 | ->setKey($exploded[0]) 91 | ->setValue($value); 92 | } 93 | 94 | /** 95 | * Creates a new line 96 | * 97 | * @return boolean 98 | */ 99 | public function create() 100 | { 101 | $appended_position = file_put_contents( 102 | $this->Env->getPath(), 103 | $this->getFullLine() . "\n", 104 | FILE_APPEND | LOCK_EX 105 | ); 106 | 107 | Event::dispatch(new EnvFileChangedEvent($this)); 108 | 109 | return is_int($appended_position); 110 | } 111 | 112 | /** 113 | * Update a line 114 | * 115 | * @return boolean 116 | */ 117 | public function update() 118 | { 119 | $replaced_position = file_put_contents( 120 | $this->Env->getPath(), 121 | preg_replace( 122 | sprintf("/%s.*\n/", $this->getKey()), 123 | $this->getFullLine() . "\n", 124 | file_get_contents($this->Env->getPath()) 125 | ) 126 | ); 127 | 128 | Event::dispatch(new EnvFileChangedEvent($this)); 129 | 130 | return is_int($replaced_position); 131 | } 132 | 133 | /** 134 | * Delete a line 135 | * 136 | * @return boolean 137 | */ 138 | public function delete() 139 | { 140 | $deleted_position = file_put_contents( 141 | $this->Env->getPath(), 142 | preg_replace( 143 | sprintf("/%s.*\n/", $this->getKey()), 144 | "", 145 | file_get_contents($this->Env->getPath()) 146 | ) 147 | ); 148 | 149 | Event::dispatch(new EnvFileChangedEvent($this)); 150 | 151 | return is_int($deleted_position); 152 | } 153 | 154 | /** 155 | * Reset a value 156 | * 157 | * @return self 158 | */ 159 | public function reset() 160 | { 161 | $this->setValue(null)->update(); 162 | 163 | return $this; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/Env.php: -------------------------------------------------------------------------------- 1 | environmentFilePath(); 26 | } 27 | 28 | /** 29 | * Set .env file content 30 | * 31 | * @return string 32 | */ 33 | private function setEnvContent() 34 | { 35 | $this->envContent = file_get_contents(self::getPath()); 36 | 37 | return $this; 38 | } 39 | 40 | /** 41 | * Get .env file content 42 | * 43 | * @return string 44 | */ 45 | public function getEnvContent(bool $forceUpdate = false) 46 | { 47 | if (!$this->envContent || $forceUpdate === true) { 48 | $this->setEnvContent(); 49 | } 50 | 51 | return $this->envContent; 52 | } 53 | 54 | /** 55 | * Get line number for passed key 56 | * 57 | * @param string $key 58 | * @return Line|null 59 | */ 60 | public function locate(string $key, bool $forceUpdate = true) 61 | { 62 | return $this->createNewLineObject( 63 | array_filter( 64 | preg_split("/\n/", $this->getEnvContent($forceUpdate)), 65 | function ($line) use ($key) { 66 | preg_match(sprintf("/^%s/m", $key), $line, $key_exist); 67 | 68 | return is_array($key_exist) && count($key_exist) > 0; 69 | } 70 | ) 71 | ); 72 | } 73 | 74 | /** 75 | * Append new line to .env file 76 | * 77 | * @param string $key 78 | * @param mixed $value 79 | * @return boolean 80 | * @throws KeyAlreadyExistsException 81 | */ 82 | public function append(string $key, $value) 83 | { 84 | $line = $this->locate($key, true); 85 | 86 | if ($line instanceof Line) { 87 | throw new KeyAlreadyExistsException; 88 | } 89 | 90 | return (new Line($this)) 91 | ->setFullLine(sprintf('%s="%s"', $key, $value)) 92 | ->create(); 93 | } 94 | 95 | /** 96 | * Replace key with given value 97 | * 98 | * @param string $key 99 | * @param mixed $value 100 | * @return boolean 101 | */ 102 | public function replace(string $key, $value) 103 | { 104 | $line = $this->locate($key, true); 105 | 106 | if (!$line instanceof Line) { 107 | throw new KeyNotFoundException; 108 | } 109 | 110 | return $line->setValue($value)->update(); 111 | } 112 | 113 | /** 114 | * Delete line start with given key 115 | * 116 | * @param string $key 117 | * @return boolean 118 | * @throws KeyNotFoundException 119 | */ 120 | public function delete(string $key) 121 | { 122 | $line = $this->locate($key, true); 123 | 124 | if (!$line instanceof Line) { 125 | throw new KeyNotFoundException(); 126 | } 127 | 128 | return $line->delete(); 129 | } 130 | 131 | /** 132 | * Reset a value for a given key 133 | * 134 | * @param string $key 135 | * @return boolean 136 | * @throws KeyNotFoundException 137 | */ 138 | public function reset(string $key) 139 | { 140 | $line = $this->locate($key, true); 141 | 142 | if (!$line instanceof Line) { 143 | throw new KeyNotFoundException(); 144 | } 145 | 146 | return $line->reset(); 147 | } 148 | 149 | /** 150 | * Create new Line object 151 | * 152 | * @param array $matched_line 153 | * @return Line|null 154 | */ 155 | private function createNewLineObject(array $matched_line) 156 | { 157 | $key = key($matched_line); 158 | 159 | if (is_null($key)) { 160 | return; 161 | } 162 | 163 | return (new Line($this)) 164 | ->setLineNumber($key + 1) 165 | ->setFullLine($matched_line[$key]); 166 | } 167 | } 168 | --------------------------------------------------------------------------------