Skip to content

Commit c8ba046

Browse files
Merge pull request #5 from lamoda/feature/FileBasedStrategy
Add new FileBasedEnvResolvingStrategy
2 parents 3a646ed + c06bcd4 commit c8ba046

36 files changed

+1724
-103
lines changed

.scrutinizer.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ build:
88

99
filter:
1010
excluded_paths:
11+
- src/FileReader/Exception/*
1112
- tests/*

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ php:
44
- 7.1
55
- 7.2
66
- 7.3
7-
- 7.4snapshot
87
# php8 is not supported yet
98
#- nightly
109

README.md

Lines changed: 110 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ composer require lamoda/multi-env
1919

2020
## Usage
2121

22-
### Library usage to work in not multitenant environment (could be useful in development mode)
22+
### Library usage examples to work in not multitenant environment (could be useful in development mode)
2323

2424
```php
2525
<?php
@@ -35,12 +35,13 @@ EnvProviderDecorator::init($strategy);
3535
EnvProviderDecorator::getEnv('TEST_ENV');
3636
```
3737

38-
### Library usage to work in multitenant environment
38+
### Library usage examples to work in multitenant environment
3939

4040
```php
4141
<?php
4242

4343
use \Lamoda\MultiEnv\Strategy\HostBasedEnvResolvingStrategy;
44+
use \Lamoda\MultiEnv\Strategy\FileBasedEnvResolvingStrategy;
4445
use \Lamoda\MultiEnv\Decorator\EnvProviderDecorator;
4546

4647
/*
@@ -51,10 +52,57 @@ $strategy = new HostBasedEnvResolvingStrategy($hostDetector, $envFormatter);
5152
EnvProviderDecorator::init($strategy);
5253

5354
/*
54-
* Will search env with some specific prefix/postfix resolved by HostDetectorInterface
55+
* Will search env with some specific prefix/suffix resolved by HostDetectorInterface
5556
* For example host_id__TEST_ENV
5657
*/
5758
EnvProviderDecorator::getEnv('TEST_ENV');
59+
60+
/*
61+
* Pass as first param one of available HostDetectorInterface implementations which detect HostId for current request
62+
* Pass as second param one of available EnvFileReaderInterface (for now available only DotEnvFileReaderAdapter) which load specific env file
63+
* Pass as third param another EnvResolvingStrategy which find specific env variable from loaded envs
64+
*/
65+
$strategy = new FileBasedEnvResolvingStrategy($hostDetector, $envFileReader, $envResolvingStrategy);
66+
EnvProviderDecorator::init($strategy);
67+
EnvProviderDecorator::getEnv('TEST_ENV');
68+
```
69+
70+
### Library set up by build in factory
71+
72+
1. Library set up for work with env in file stored in different directories
73+
```php
74+
<?php
75+
76+
use \Lamoda\MultiEnv\Builder\FileBasedEnvResolvingStrategyBuilder;
77+
use \Lamoda\MultiEnv\Decorator\EnvProviderDecorator;
78+
79+
/*
80+
* Result strategy resolve hostId from server headers or cli args
81+
* Then load all envs from .env file stored by path /var/envs/*hostId*
82+
* Read TEST_ENV env variable
83+
*/
84+
$strategy = FileBasedEnvResolvingStrategyBuilder::buildStrategy('HTTP_X_HOST_ID', 'host_id', '.env', '/var/envs');
85+
EnvProviderDecorator::init($strategy);
86+
87+
EnvProviderDecorator::getEnv('TEST_ENV');
88+
```
89+
2. Library set up for work with env with prefixes
90+
```php
91+
<?php
92+
93+
use \Lamoda\MultiEnv\Builder\HostBasedEnvResolvingStrategyBuilder;
94+
use \Lamoda\MultiEnv\Decorator\EnvProviderDecorator;
95+
96+
/*
97+
* Result strategy resolve hostId from server headers or cli args
98+
* Add prefix to original env name. Result be like *hostId*___TEST_ENV
99+
* Replace all '-' char to '_' case '-' illegal in env variable name
100+
* Read *host_id*___TEST_ENV env variable
101+
*/
102+
$strategy = HostBasedEnvResolvingStrategyBuilder::buildStrategy('HTTP_X_HOST_ID', 'host_id', '___');
103+
EnvProviderDecorator::init($strategy);
104+
105+
EnvProviderDecorator::getEnv('TEST_ENV');
58106
```
59107

60108
### Library set up with multiple strategy
@@ -67,7 +115,7 @@ use \Lamoda\MultiEnv\Strategy\HostBasedEnvResolvingStrategy;
67115
use \Lamoda\MultiEnv\HostDetector\FirstSuccessfulHostDetector;
68116
use \Lamoda\MultiEnv\HostDetector\CliArgsBasedHostDetector;
69117
use \Lamoda\MultiEnv\HostDetector\ServerHeadersBasedHostDetector;
70-
use \Lamoda\MultiEnv\Formatter\PrefixEnvNameFormatter;
118+
use \Lamoda\MultiEnv\Formatter\PrefixAppendFormatter;
71119
use \Lamoda\MultiEnv\HostDetector\Factory\GetOptAdapterFactory;
72120
use \Lamoda\MultiEnv\Decorator\EnvProviderDecorator;
73121

@@ -77,7 +125,7 @@ $hostBasedEnvResolvingStrategy = new HostBasedEnvResolvingStrategy(
77125
new ServerHeadersBasedHostDetector('HTTP_X_TEST_HEADER'),
78126
new CliArgsBasedHostDetector('host_id', GetOptAdapterFactory::build())
79127
]),
80-
new PrefixEnvNameFormatter('___')
128+
new PrefixAppendFormatter('___')
81129
);
82130

83131
$firstSuccessfulStrategy = new FirstSuccessfulEnvResolvingStrategy([
@@ -88,7 +136,7 @@ EnvProviderDecorator::init($firstSuccessfulStrategy);
88136

89137
/*
90138
* Try find original env 'TEST_ENV' first.
91-
* If original env not found than try to find env with some specific prefix/postfix resolved by HostDetectorInterface.
139+
* If original env not found than try to find env with some specific prefix/suffix resolved by HostDetectorInterface.
92140
* For example host_id__TEST_ENV
93141
*/
94142
EnvProviderDecorator::getEnv('TEST_ENV');
@@ -143,17 +191,66 @@ EnvProviderDecorator::getEnv('TEST_ENV');
143191
$hostId = $firstSuccessfulHostDetector->getCurrentHost();
144192
```
145193

146-
## Available EnvNameFormatterInterface implementations
147-
1. __\Lamoda\MultiEnv\Formatter\PrefixEnvNameFormatter__ - format env name. Combine __original env name__, __delimiter__,
148-
__host id__ in order \*__host id__\*, \*__delimiter__\*, \*__original env name__\*
194+
## Available FormatterInterface implementations
195+
1. __\Lamoda\MultiEnv\Formatter\PrefixAppendFormatter__ - append prefix and delimiter to original string.
196+
Combine __original string__, __delimiter__,__host id__ in order
197+
\*__host id__\*, \*__delimiter__\*, \*__original string__\*
149198
```php
150199
<?php
151200

152-
use \Lamoda\MultiEnv\Formatter\PrefixEnvNameFormatter;
201+
use \Lamoda\MultiEnv\Formatter\PrefixAppendFormatter;
202+
use \Lamoda\MultiEnv\Model\HostId;
203+
use \Lamoda\MultiEnv\Formatter\Exception\FormatterException;
204+
205+
$formatter = new PrefixAppendFormatter('__');
206+
// Throw FormatterException if passed empty originalName
207+
$formatterName = $formatter->formatName('originalEnvName', new HostId('test_host'));
208+
```
209+
2. __\Lamoda\MultiEnv\Formatter\SuffixAppendFormatter__ - append suffix and delimiter to original string.
210+
Combine __original string__, __delimiter__,__host id__ in order
211+
\*__original string__\*, \*__delimiter__\*, \*__host id__\*
212+
```php
213+
<?php
214+
215+
use \Lamoda\MultiEnv\Formatter\SuffixAppendFormatter;
216+
use \Lamoda\MultiEnv\Model\HostId;
217+
use \Lamoda\MultiEnv\Formatter\Exception\FormatterException;
218+
219+
$formatter = new SuffixAppendFormatter('___');
220+
// Throw FormatterException if passed empty originalName
221+
$formattedName = $formatter->formatName('originalEnvName', new HostId('test_host'));
222+
```
223+
3. __\Lamoda\MultiEnv\Formatter\CharReplaceFormatter__ - act like __str_replace__ in PHP. Could be useful to replace illegal
224+
char __'-'__ to __'\_\'__ when you access to env variable
225+
```php
226+
<?php
227+
228+
use \Lamoda\MultiEnv\Formatter\CharReplaceFormatter;
153229
use \Lamoda\MultiEnv\Model\HostId;
154230
use \Lamoda\MultiEnv\Formatter\Exception\FormatterException;
155231

156-
$formatter = new PrefixEnvNameFormatter('__');
157-
// Throw FormatterException if passed empty originalEnvName
158-
$formatterName = $formatter->formatEnvName('originalEnvName', new HostId('test_host'));
232+
$formatter = new CharReplaceFormatter('-', '_');
233+
/*
234+
* Throw FormatterException if passed empty originalName
235+
* return 'original_env_name'
236+
*/
237+
$formattedName = $formatter->formatName('original-env-name', new HostId('testHost'));
159238
```
239+
240+
4. __\Lamoda\MultiEnv\Formatter\FormatterPipeline__ - aggregate few formatters. Iterate through them and apply each to original stirng
241+
```php
242+
<?php
243+
244+
use \Lamoda\MultiEnv\Formatter\FormatterPipeline;
245+
use \Lamoda\MultiEnv\Formatter\SuffixAppendFormatter;
246+
use \Lamoda\MultiEnv\Formatter\CharReplaceFormatter;
247+
use \Lamoda\MultiEnv\Model\HostId;
248+
249+
$formatter = new FormatterPipeline([
250+
new SuffixAppendFormatter('-'),
251+
new CharReplaceFormatter('-', '_')
252+
]);
253+
254+
// return 'originalEnvName_test_host_id'
255+
$formattedName = $formatter->formatName('originalEnvName', new HostId('test-host-id'));
256+
```

composer.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
"ulrichsg/getopt-php": "^3.2"
99
},
1010
"require-dev": {
11+
"friendsofphp/php-cs-fixer": "^2.14",
1112
"phpunit/phpunit": "^7.5",
12-
"friendsofphp/php-cs-fixer": "^2.14"
13+
"vlucas/phpdotenv": "^3.4"
14+
},
15+
"suggest": {
16+
"vlucas/phpdotenv": "Required for DotEnvFileReaderAdapter"
1317
},
1418
"autoload": {
1519
"psr-4": {

src/Decorator/EnvProviderDecorator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
final class EnvProviderDecorator
1111
{
1212
/**
13-
* @var ?EnvResolvingStrategyInterface $resolvingStrategy
13+
* @var EnvResolvingStrategyInterface|null $resolvingStrategy
1414
*/
1515
private static $resolvingStrategy;
1616

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lamoda\MultiEnv\Factory;
6+
7+
use Lamoda\MultiEnv\FileReader\DotEnvFileReaderAdapter;
8+
use Lamoda\MultiEnv\FileReader\FileNameResolver\FileNameResolver;
9+
use Lamoda\MultiEnv\FileReader\PathResolver\PathResolver;
10+
use Lamoda\MultiEnv\Formatter\SuffixAppendFormatter;
11+
use Lamoda\MultiEnv\HostDetector\CliArgsBasedHostDetector;
12+
use Lamoda\MultiEnv\HostDetector\Factory\GetOptAdapterFactory;
13+
use Lamoda\MultiEnv\HostDetector\FirstSuccessfulHostDetector;
14+
use Lamoda\MultiEnv\HostDetector\ServerHeadersBasedHostDetector;
15+
use Lamoda\MultiEnv\Strategy\FileBasedEnvResolvingStrategy;
16+
use Lamoda\MultiEnv\Strategy\RawEnvResolvingStrategy;
17+
18+
class FileBasedEnvResolvingStrategyFactory
19+
{
20+
public static function createStrategy(
21+
string $serverHeaderToSearch,
22+
string $cliArgToSearch,
23+
string $envFileName,
24+
string $basePathToEnvFile
25+
): FileBasedEnvResolvingStrategy {
26+
return new FileBasedEnvResolvingStrategy(
27+
new FirstSuccessfulHostDetector([
28+
new ServerHeadersBasedHostDetector($serverHeaderToSearch),
29+
new CliArgsBasedHostDetector($cliArgToSearch, GetOptAdapterFactory::build())
30+
]),
31+
new DotEnvFileReaderAdapter(
32+
new PathResolver($basePathToEnvFile, new SuffixAppendFormatter(DIRECTORY_SEPARATOR)),
33+
new FileNameResolver($envFileName)
34+
),
35+
new RawEnvResolvingStrategy()
36+
);
37+
}
38+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lamoda\MultiEnv\Factory;
6+
7+
use Lamoda\MultiEnv\Formatter\CharReplaceFormatter;
8+
use Lamoda\MultiEnv\Formatter\FormatterPipeline;
9+
use Lamoda\MultiEnv\Formatter\PrefixAppendFormatter;
10+
use Lamoda\MultiEnv\HostDetector\CliArgsBasedHostDetector;
11+
use Lamoda\MultiEnv\HostDetector\Factory\GetOptAdapterFactory;
12+
use Lamoda\MultiEnv\HostDetector\FirstSuccessfulHostDetector;
13+
use Lamoda\MultiEnv\HostDetector\ServerHeadersBasedHostDetector;
14+
use Lamoda\MultiEnv\Strategy\HostBasedEnvResolvingStrategy;
15+
16+
class HostBasedEnvResolvingStrategyFactory
17+
{
18+
public static function createStrategy(
19+
string $serverHeaderToSearch,
20+
string $cliArgToSearch,
21+
string $delimiter
22+
): HostBasedEnvResolvingStrategy {
23+
return new HostBasedEnvResolvingStrategy(
24+
new FirstSuccessfulHostDetector([
25+
new ServerHeadersBasedHostDetector($serverHeaderToSearch),
26+
new CliArgsBasedHostDetector($cliArgToSearch, GetOptAdapterFactory::build())
27+
]),
28+
new FormatterPipeline([
29+
new PrefixAppendFormatter($delimiter),
30+
new CharReplaceFormatter('-', '_')
31+
])
32+
);
33+
}
34+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lamoda\MultiEnv\FileReader;
6+
7+
use Lamoda\MultiEnv\FileReader\Exception\EnvFileReaderException;
8+
use Lamoda\MultiEnv\FileReader\FileNameResolver\FileNameResolverInterface;
9+
use Lamoda\MultiEnv\FileReader\PathResolver\PathResolverInterface;
10+
use Lamoda\MultiEnv\Model\HostId;
11+
12+
final class DotEnvFileReaderAdapter implements EnvFileReaderInterface
13+
{
14+
/**
15+
* @var PathResolverInterface
16+
*/
17+
private $pathResolver;
18+
19+
/**
20+
* @var FileNameResolverInterface
21+
*/
22+
private $fileNameResolver;
23+
24+
/**
25+
* @var bool
26+
*/
27+
private $isEnvFileAlreadyLoaded;
28+
29+
public function __construct(PathResolverInterface $pathResolver, FileNameResolverInterface $fileNameResolver)
30+
{
31+
// @codeCoverageIgnoreStart
32+
if (!class_exists(\Dotenv\Dotenv::class)) {
33+
throw EnvFileReaderException::becauseAdapterCanNotBeCreated(__CLASS__, \Dotenv\Dotenv::class);
34+
}
35+
// @codeCoverageIgnoreEnd
36+
$this->pathResolver = $pathResolver;
37+
$this->fileNameResolver = $fileNameResolver;
38+
$this->isEnvFileAlreadyLoaded = false;
39+
}
40+
41+
public function readEnvFile(HostId $hostId): void
42+
{
43+
if ($this->isEnvFileAlreadyLoaded) {
44+
return;
45+
}
46+
47+
try {
48+
$dotEnv = \Dotenv\Dotenv::create(
49+
$this->pathResolver->resolvePathToEnvFile($hostId),
50+
$this->fileNameResolver->resolveEnvFileName($hostId)
51+
);
52+
$dotEnv->overload();
53+
$this->isEnvFileAlreadyLoaded = true;
54+
} catch (\Dotenv\Exception\ExceptionInterface $exception) {
55+
throw EnvFileReaderException::becauseEnvFileCanNotBeProcessed($exception);
56+
}
57+
}
58+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lamoda\MultiEnv\FileReader;
6+
7+
use Lamoda\MultiEnv\Model\HostId;
8+
9+
interface EnvFileReaderInterface
10+
{
11+
public function readEnvFile(HostId $hostId): void;
12+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lamoda\MultiEnv\FileReader\Exception;
6+
7+
use Lamoda\MultiEnv\Exception\EnvProviderExceptionInterface;
8+
use Throwable;
9+
10+
final class EnvFileReaderException extends \RuntimeException implements EnvProviderExceptionInterface
11+
{
12+
public static function becauseEnvFileCanNotBeProcessed(?Throwable $previous): self
13+
{
14+
return new self('Can\'t process file with env', 0, $previous);
15+
}
16+
17+
public static function becauseAdapterCanNotBeCreated(string $adapterName, string $required)
18+
{
19+
return new self('Can\t create adapter "' . $adapterName . '". It\'s require "' . $required . '"');
20+
}
21+
}

0 commit comments

Comments
 (0)