Skip to content

Commit

Permalink
Merge pull request #220 from aedart/add-env-file-component
Browse files Browse the repository at this point in the history
Add env file component
  • Loading branch information
aedart authored Feb 25, 2025
2 parents 67222ff + 0274c21 commit c3faf89
Show file tree
Hide file tree
Showing 11 changed files with 665 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG_v9x.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ TODO: Temporary changelog file for the upcoming major version `9.x`.
* `Json::isValid()` now accepts `$depth` and `$options` parameters.
* Custom `FailedPasswordResetLinkApiResponse` in `aedart/athenaeum-auth` package.
* `FailedLoginAttempt` and `PasswordResetLinkFailure` exceptions in `aedart/athenaeum-auth` package.
* `EnvFile` component, in `Aedart\Support`. [#219](https://github.com/aedart/athenaeum/issues/219).
* `ConcurrencyManager`, `LogContextRepository`, `DateFactory`, `ExceptionHandler`, `ParallelTesting`, `ProcessFactory`, `RateLimiter`, `Schedule` and `Vite` aware-of helpers, in `Aedart\Support\Helpers`.
* Configuration for `composer-bin-plugin` (_in root `composer.json`_).

Expand Down
68 changes: 68 additions & 0 deletions packages/Support/src/Env/Concerns/EnvironmentFilePath.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Aedart\Support\Env\Concerns;

use Illuminate\Support\Facades\App;

/**
* Concerns Environment File Path
*
* @author Alin Eugen Deac <[email protected]>
* @package Aedart\Support\Env\Concerns
*/
trait EnvironmentFilePath
{
/**
* Path to environment file
*
* @var string|null
*/
protected string|null $environmentFilePath = null;

/**
* Set environment file path
*
* @param string|null $path Path to environment file
*
* @return self
*/
public function setEnvironmentFilePath(string|null $path): static
{
$this->environmentFilePath = $path;

return $this;
}

/**
* Get environment file path
*
* @return string|null
*/
public function getEnvironmentFilePath(): string|null
{
if (!$this->hasEnvironmentFilePath()) {
$this->setEnvironmentFilePath($this->getDefaultEnvironmentFilePath());
}
return $this->environmentFilePath;
}

/**
* Check if environment file path has been set
*
* @return bool
*/
public function hasEnvironmentFilePath(): bool
{
return isset($this->environmentFilePath);
}

/**
* Get a default environment file path value, if any is available
*
* @return string|null
*/
public function getDefaultEnvironmentFilePath(): string|null
{
return App::environmentFilePath();
}
}
15 changes: 15 additions & 0 deletions packages/Support/src/Env/Exceptions/EnvironmentFileException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Aedart\Support\Env\Exceptions;

use RuntimeException;

/**
* Environment File Exception
*
* @author Alin Eugen Deac <[email protected]>
* @package Aedart\Support\Env\Exceptions
*/
class EnvironmentFileException extends RuntimeException
{
}
15 changes: 15 additions & 0 deletions packages/Support/src/Env/Exceptions/FileNotFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Aedart\Support\Env\Exceptions;

/**
* Environment File Not Found Exception
*
* @see \Aedart\Support\Env\Exceptions\EnvironmentFileException
*
* @author Alin Eugen Deac <[email protected]>
* @package Aedart\Support\Env\Exceptions
*/
class FileNotFound extends EnvironmentFileException
{
}
13 changes: 13 additions & 0 deletions packages/Support/src/Env/Exceptions/KeyNotFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Aedart\Support\Env\Exceptions;

/**
* Key Not Found Exception
*
* @author Alin Eugen Deac <[email protected]>
* @package Aedart\Support\Env\Exceptions
*/
class KeyNotFound extends EnvironmentFileException
{
}
13 changes: 13 additions & 0 deletions packages/Support/src/Env/Exceptions/UnableToReadContents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Aedart\Support\Env\Exceptions;

/**
* Unable To Read Contents Exception
*
* @author Alin Eugen Deac <[email protected]>
* @package Aedart\Support\Env\Exceptions
*/
class UnableToReadContents extends EnvironmentFileException
{
}
13 changes: 13 additions & 0 deletions packages/Support/src/Env/Exceptions/UnableToWriteContents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Aedart\Support\Env\Exceptions;

/**
* Unable To Write Contents Exception
*
* @author Alin Eugen Deac <[email protected]>
* @package Aedart\Support\Env\Exceptions
*/
class UnableToWriteContents extends EnvironmentFileException
{
}
203 changes: 203 additions & 0 deletions packages/Support/src/EnvFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<?php

namespace Aedart\Support;

use Aedart\Support\Env\Concerns\EnvironmentFilePath;
use Aedart\Support\Env\Exceptions\EnvironmentFileException;
use Aedart\Support\Env\Exceptions\FileNotFound;
use Aedart\Support\Env\Exceptions\KeyNotFound;
use Aedart\Support\Env\Exceptions\UnableToReadContents;

/**
* Env File
*
* @author Alin Eugen Deac <[email protected]>
* @package Aedart\Support
*/
class EnvFile
{
use EnvironmentFilePath;

/**
* The contents of the environment file
*
* @var string
*/
protected string $contents = '';

/**
* Create a new Environment File instance
*
* @param string|null $path [optional] Path to env file. Defaults to Laravel's environment file path.
* @param bool $load [optional] If true, the contents of the environment file is loaded into memory.
*
* @throws EnvironmentFileException
*/
public function __construct(string|null $path = null, bool $load = false)
{
$this->setEnvironmentFilePath($path);

if ($load) {
$this->refresh();
}
}

/**
* Load environment file content into memory
*
* @param string|null $path [optional] Path to env file. Defaults to Laravel's environment file path.
*
* @return static
*
* @throws EnvironmentFileException
*/
public static function load(string|null $path = null): static
{
return new static($path, true);
}

/**
* Reloads the contents of the environment file.
*
* @return self
*
* @throws EnvironmentFileException
*/
public function refresh(): static
{
$path = $this->getEnvironmentFilePath();

if (!file_exists($path)) {
throw new FileNotFound("Environment file '{$path}' does not exist.");
}

$contents = file_get_contents($path);
if ($contents === false) {
throw new UnableToReadContents("Unable to read contents of '{$path}'.");
}

$this->contents = $contents;

return $this;
}

/**
* Write the contents into the environment file
*
* @return self
*/
public function save(): static
{
$path = $this->getEnvironmentFilePath();

$bytes = file_put_contents($path, $this->contents);
if ($bytes === false) {
throw new UnableToReadContents("Unable to write contents for '{$path}'.");
}

return $this;
}

/**
* Determine if key exists in environment file
*
* @param string $key
*
* @return bool
*/
public function has(string $key): bool
{
$key = $this->resolveKey($key);

return str_contains($this->contents, "\n{$key}=");
}

/**
* Append a new key-value pair
*
* @param string $key
* @param string|int $value
* @param string|null $comment [optional] Evt. comment for the key-value pair.
*
* @return self
*/
public function append(string $key, string|int $value, string|null $comment = null): static
{
$key = $this->resolveKey($key);

$prefix = '';
if ($comment) {
$prefix = "\n# {$comment}";
}

$this->contents .= "{$prefix}\n{$key}={$value}";

return $this;
}

/**
* Replace the value of an existing key in the environment file
*
* @param string $key
* @param string|int $value
*
* @return self
*
* @throws EnvironmentFileException
*/
public function replace(string $key, string|int $value): static
{
if (!$this->has($key)) {
throw new KeyNotFound(sprintf('Key %s does not exist, in %s', $key, $this->getEnvironmentFilePath()));
}

$key = $this->resolveKey($key);

$this->contents = preg_replace(
pattern: $this->makeKeySearchPattern($key),
replacement: "{$key}={$value}",
subject: $this->contents,
limit: 1,
);

return $this;
}

/**
* Returns the loaded environment file's contents
*
* @return string
*/
public function contents(): string
{
return $this->contents;
}

/*****************************************************************
* Internals
****************************************************************/

/**
* Creates a RegEx search pattern for a key inside the environment file
*
* @param string $key
*
* @return string
*/
protected function makeKeySearchPattern(string $key): string
{
return "/^{$key}=(.)*/m";
}

/**
* Resolves given key
*
* @param string $key
*
* @return string
*/
protected function resolveKey(string $key): string
{
return strtoupper($key);
}
}
Loading

0 comments on commit c3faf89

Please sign in to comment.