Skip to content

Commit b382689

Browse files
committed
add EnvironmentDetector utility class
1 parent 2db0ed7 commit b382689

3 files changed

Lines changed: 218 additions & 0 deletions

File tree

system/Config/Services.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use CodeIgniter\Email\Email;
2929
use CodeIgniter\Encryption\EncrypterInterface;
3030
use CodeIgniter\Encryption\Encryption;
31+
use CodeIgniter\EnvironmentDetector;
3132
use CodeIgniter\Filters\Filters;
3233
use CodeIgniter\Format\Format;
3334
use CodeIgniter\Honeypot\Honeypot;
@@ -259,6 +260,21 @@ public static function encrypter(?EncryptionConfig $config = null, $getShared =
259260
return $encryption->initialize($config);
260261
}
261262

263+
/**
264+
* Provides a simple way to determine the current environment
265+
* of the application.
266+
*
267+
* @return EnvironmentDetector
268+
*/
269+
public static function environmentdetector(?string $environment = null, bool $getShared = true)
270+
{
271+
if ($getShared) {
272+
return static::getSharedInstance('environmentdetector', $environment);
273+
}
274+
275+
return new EnvironmentDetector($environment);
276+
}
277+
262278
/**
263279
* The Exceptions class holds the methods that handle:
264280
*

system/EnvironmentDetector.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter;
15+
16+
use CodeIgniter\Exceptions\InvalidArgumentException;
17+
18+
/**
19+
* Provides a simple way to determine the current environment of the application.
20+
*
21+
* For custom environment names beyond the built-in production/development/testing,
22+
* use {@see self::is()}.
23+
*
24+
* @see \CodeIgniter\EnvironmentDetectorTest
25+
*/
26+
final class EnvironmentDetector
27+
{
28+
private readonly string $environment;
29+
30+
/**
31+
* @param non-empty-string|null $environment The environment to use, or null to
32+
* fall back to the `ENVIRONMENT` constant.
33+
*/
34+
public function __construct(?string $environment = null)
35+
{
36+
if ($environment !== null && trim($environment) === '') {
37+
throw new InvalidArgumentException('Environment cannot be an empty string.');
38+
}
39+
40+
$this->environment = $environment !== null ? trim($environment) : ENVIRONMENT;
41+
}
42+
43+
public function get(): string
44+
{
45+
return $this->environment;
46+
}
47+
48+
/**
49+
* Checks if the current environment matches any of the given environments.
50+
*
51+
* @param string ...$environments One or more environment names to check against.
52+
*/
53+
public function is(string ...$environments): bool
54+
{
55+
return in_array($this->environment, $environments, true);
56+
}
57+
58+
public function isProduction(): bool
59+
{
60+
return $this->is('production');
61+
}
62+
63+
public function isDevelopment(): bool
64+
{
65+
return $this->is('development');
66+
}
67+
68+
public function isTesting(): bool
69+
{
70+
return $this->is('testing');
71+
}
72+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter;
15+
16+
use CodeIgniter\Exceptions\InvalidArgumentException;
17+
use CodeIgniter\Test\CIUnitTestCase;
18+
use PHPUnit\Framework\Attributes\DataProvider;
19+
use PHPUnit\Framework\Attributes\Group;
20+
21+
/**
22+
* @internal
23+
*/
24+
#[Group('Others')]
25+
final class EnvironmentDetectorTest extends CIUnitTestCase
26+
{
27+
public function testDefaultsToEnvironmentConstant(): void
28+
{
29+
$detector = new EnvironmentDetector();
30+
31+
$this->assertSame(ENVIRONMENT, $detector->get());
32+
}
33+
34+
public function testExplicitEnvironmentOverridesConstant(): void
35+
{
36+
$detector = new EnvironmentDetector('production');
37+
38+
$this->assertSame('production', $detector->get());
39+
}
40+
41+
public function testTrimsSurroundingWhitespace(): void
42+
{
43+
$detector = new EnvironmentDetector(" production\n");
44+
45+
$this->assertSame('production', $detector->get());
46+
}
47+
48+
public function testRejectsEmptyString(): void
49+
{
50+
$this->expectException(InvalidArgumentException::class);
51+
$this->expectExceptionMessage('Environment cannot be an empty string.');
52+
53+
new EnvironmentDetector(''); // @phpstan-ignore argument.type
54+
}
55+
56+
public function testRejectsWhitespaceOnlyString(): void
57+
{
58+
$this->expectException(InvalidArgumentException::class);
59+
$this->expectExceptionMessage('Environment cannot be an empty string.');
60+
61+
new EnvironmentDetector(" \t\n");
62+
}
63+
64+
public function testIsMatchesSingleEnvironment(): void
65+
{
66+
$detector = new EnvironmentDetector('staging');
67+
68+
$this->assertTrue($detector->is('staging'));
69+
$this->assertFalse($detector->is('production'));
70+
}
71+
72+
public function testIsMatchesAnyOfSeveralEnvironments(): void
73+
{
74+
$detector = new EnvironmentDetector('staging');
75+
76+
$this->assertTrue($detector->is('production', 'staging', 'development'));
77+
$this->assertFalse($detector->is('production', 'development', 'testing'));
78+
}
79+
80+
public function testIsReturnsFalseWhenNoEnvironmentsGiven(): void
81+
{
82+
$detector = new EnvironmentDetector('production');
83+
84+
$this->assertFalse($detector->is());
85+
}
86+
87+
public function testIsIsCaseSensitive(): void
88+
{
89+
$detector = new EnvironmentDetector('production');
90+
91+
$this->assertFalse($detector->is('Production'));
92+
$this->assertFalse($detector->is('PRODUCTION'));
93+
}
94+
95+
/**
96+
* @param non-empty-string $environment
97+
*/
98+
#[DataProvider('provideBuiltInEnvironmentHelpers')]
99+
public function testBuiltInEnvironmentHelpers(string $environment, bool $isProduction, bool $isDevelopment, bool $isTesting): void
100+
{
101+
$detector = new EnvironmentDetector($environment);
102+
103+
$this->assertSame($isProduction, $detector->isProduction());
104+
$this->assertSame($isDevelopment, $detector->isDevelopment());
105+
$this->assertSame($isTesting, $detector->isTesting());
106+
}
107+
108+
/**
109+
* @return iterable<string, array{string, bool, bool, bool}>
110+
*/
111+
public static function provideBuiltInEnvironmentHelpers(): iterable
112+
{
113+
yield 'production' => ['production', true, false, false];
114+
115+
yield 'development' => ['development', false, true, false];
116+
117+
yield 'testing' => ['testing', false, false, true];
118+
119+
yield 'custom' => ['staging', false, false, false];
120+
}
121+
122+
public function testResolvesAsSharedService(): void
123+
{
124+
$first = service('environmentdetector');
125+
$second = service('environmentdetector');
126+
127+
$this->assertInstanceOf(EnvironmentDetector::class, $first);
128+
$this->assertSame($first, $second);
129+
}
130+
}

0 commit comments

Comments
 (0)