Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use PHP Domain Parser to validate domains #1524

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"require-dev": {
"egulias/email-validator": "^4.0",
"giggsey/libphonenumber-for-php-lite": "^8.13",
"jeremykendall/php-domain-parser": "^6.0",
"malukenho/docheader": "^1.0",
"mikey179/vfsstream": "^1.6",
"nette/php-generator": "^4.1",
Expand All @@ -48,6 +49,7 @@
"ext-fileinfo": "File Information",
"ext-mbstring": "Multibyte String Functions",
"egulias/email-validator": "Improves the Email rule if available",
"jeremykendall/php-domain-parser": "Allows validating domains",
"giggsey/libphonenumber-for-php-lite": "Enables the phone rule if available",
"sokil/php-isocodes": "Enable rules that validate ISO codes",
"sokil/php-isocodes-db-only": "Enable rules that validate ISO codes"
Expand Down
81 changes: 22 additions & 59 deletions library/Rules/Domain.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,51 @@

namespace Respect\Validation\Rules;

use Attribute;
use Pdp\Domain as PdpDomain;
use Respect\Validation\Exceptions\MissingComposerDependencyException;
use Respect\Validation\Message\Template;
use Respect\Validation\Result;
use Respect\Validation\Rule;
use Respect\Validation\Rules\Core\Standard;
use Throwable;

use function array_pop;
use function class_exists;
use function count;
use function explode;
use function mb_substr_count;
use function is_string;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
#[Template(
'{{name}} must be a valid domain',
'{{name}} must not be a valid domain',
)]
final class Domain extends Standard
{
private readonly Rule $genericRule;

private readonly Rule $tldRule;

private readonly Rule $partsRule;

public function __construct(bool $tldCheck = true)
{
$this->genericRule = $this->createGenericRule();
$this->tldRule = $this->createTldRule($tldCheck);
$this->partsRule = $this->createPartsRule();
public function __construct(
private readonly bool $requireTld = true,

Check failure on line 30 in library/Rules/Domain.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Property Respect\Validation\Rules\Domain::$requireTld is never read, only written.
) {
if (!class_exists(PdpDomain::class)) {
throw new MissingComposerDependencyException(
'Domain rule requires PHP Domain Parser',
'jeremykendall/php-domain-parser',
);
}
}

public function evaluate(mixed $input): Result
{
$genericResult = $this->genericRule->evaluate($input);
if (!$genericResult->isValid) {
if (!is_string($input)) {
return Result::failed($input, $this);
}

$parts = explode('.', (string) $input);
if (count($parts) >= 2) {
$childResult = $this->tldRule->evaluate(array_pop($parts));
if (!$childResult->isValid) {
try {
$domain = \Pdp\Domain::fromIDNA2008($input);
if (count($domain->labels()) === 1) {
return Result::failed($input, $this);
}
}

return new Result($this->partsRule->evaluate($parts)->isValid, $input, $this);
}

private function createGenericRule(): Circuit
{
return new Circuit(
new StringType(),
new NoWhitespace(),
new Contains('.'),
new Length(new GreaterThanOrEqual(3))
);
}

private function createTldRule(bool $realTldCheck): Rule
{
if ($realTldCheck) {
return new Tld();
$domain->label(0);
} catch (Throwable) {
return Result::failed($input, $this);
}

return new Circuit(new Not(new StartsWith('-')), new Length(new GreaterThanOrEqual(2)));
}

private function createPartsRule(): Rule
{
return new Each(
new Circuit(
new Alnum('-'),
new Not(new StartsWith('-')),
new AnyOf(
new Not(new Contains('--')),
new Callback(static function ($str) {
return mb_substr_count($str, '--') == 1;
})
),
new Not(new EndsWith('-'))
)
);
return Result::passed($input, $this);
}
}
2 changes: 1 addition & 1 deletion tests/unit/Rules/DomainTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public static function providerForDomainWithRealTopLevelDomain(): array
['mail.xn--bcher-kva.ch'],
['example-hyphen.com'],
['example--valid.com'],
['xn--bcher--kva.ch'],
['std--a.com'],
['r--w.com'],
];
Expand All @@ -79,7 +80,6 @@ public static function providerForInvalidDomains(): array
['2222222domain.local'],
['-example-invalid.com'],
['example.invalid.-com'],
['xn--bcher--kva.ch'],
['example.invalid-.com'],
['1.2.3.256'],
['1.2.3.4'],
Expand Down
Loading