Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ BASE_DIR="/var/webroot/project-root"
CACHE_DIR="${BASE_DIR}/cache"
TMP_DIR="${BASE_DIR}/tmp"
```
### URL and Email Variables

You can validate common types like URLs and email addresses:

```php
$dotenv->required('APP_URL')->isUrl();
$dotenv->ifPresent('SUPPORT_EMAIL')->isEmail();


### Immutability and Repository Customization
Expand Down
39 changes: 39 additions & 0 deletions src/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Util\Regex;
use Dotenv\Util\Str;
use InvalidArgumentException;

class Validator
{
Expand Down Expand Up @@ -110,6 +111,44 @@ static function (string $value) {
'is not a boolean'
);
}
/**
* Assert that each specified variable is a valid URL.
*
* @param int $flags Optional FILTER_VALIDATE_URL flags (e.g., FILTER_FLAG_PATH_REQUIRED)
* @throws ValidationException
* @return self
*/
public function isUrl(int $flags = 0): self
{
return $this->assertNullable(
static function (string $value) use ($flags): bool {
if (filter_var($value, FILTER_VALIDATE_URL) === false) {
return false;
}
if ($flags !== 0 && filter_var($value, FILTER_VALIDATE_URL, $flags) === false) {
return false;
}
return true;
},
'is not a valid URL'
);
}

/**
* Assert that each specified variable is a valid email address.
*
* @throws ValidationException
* @return self
*/
public function isEmail(): self
{
return $this->assertNullable(
static function (string $value): bool {
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
},
'is not a valid email'
);
}

/**
* Assert that each variable is amongst the given choices.
Expand Down
72 changes: 72 additions & 0 deletions tests/Dotenv/ValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,78 @@ final class ValidatorTest extends TestCase
* @var string
*/
private static $folder;
private string $dir;

protected function setUp(): void
{
$this->dir = sys_get_temp_dir() . '/phpdotenv-tests-' . uniqid();
mkdir($this->dir);
}

protected function tearDown(): void
{
array_map('unlink', glob($this->dir . '/*') ?: []);
@rmdir($this->dir);
}

private function writeEnv(string $contents): void
{
file_put_contents($this->dir . '/.env', $contents);
}

public function testIsUrlPasses(): void
{
$this->writeEnv("APP_URL=https://example.com/path?x=1\n");
$dotenv = Dotenv::createImmutable($this->dir);
$dotenv->load();

$dotenv->required('APP_URL')->isUrl(); // should not throw

$this->assertTrue(true);
}

public function testIsUrlFails(): void
{
$this->writeEnv("APP_URL=not-a-url\n");
$dotenv = Dotenv::createImmutable($this->dir);
$dotenv->load();

$this->expectException(RuntimeException::class);
$dotenv->required('APP_URL')->isUrl();
}

public function testIsEmailPasses(): void
{
$this->writeEnv("SUPPORT_EMAIL=helpdesk@example.org\n");
$dotenv = Dotenv::createImmutable($this->dir);
$dotenv->load();

$dotenv->required('SUPPORT_EMAIL')->isEmail(); // should not throw

$this->assertTrue(true);
}

public function testIsEmailFails(): void
{
$this->writeEnv("SUPPORT_EMAIL=foo@@bar\n");
$dotenv = Dotenv::createImmutable($this->dir);
$dotenv->load();

$this->expectException(RuntimeException::class);
$dotenv->required('SUPPORT_EMAIL')->isEmail();
}

public function testIfPresentWithIsUrl(): void
{
$this->writeEnv(""); // not set
$dotenv = Dotenv::createImmutable($this->dir);
$dotenv->load();

// Should not throw since var is absent and ifPresent() defers validation
$dotenv->ifPresent('OPTIONAL_WEBHOOK')->isUrl();

$this->assertTrue(true);
}

/**
* @beforeClass
Expand Down