Skip to content

Commit 082a198

Browse files
committed
Adding Domain::withPublicSuffix method
- Improve Domain usage - Simplify public suffix detection - Reduce strtolower usage
1 parent 500e06b commit 082a198

File tree

7 files changed

+171
-46
lines changed

7 files changed

+171
-46
lines changed

data/pdp-PSL_FULL_5a3cc7f81795bb2e48e848af42d287b4.cache

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/Converter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ private function addRule(array $list, array $rule_parts): array
104104
// "The domain and all rules must be canonicalized in the normal way
105105
// for hostnames - lower-case, Punycode (RFC 3492)."
106106

107-
$part = strtolower($this->idnToAscii($part));
107+
$part = $this->idnToAscii($part);
108108
$isDomain = true;
109109
if (0 === strpos($part, '!')) {
110110
$part = substr($part, 1);

src/Domain.php

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ public function isPrivate(): bool
309309
/**
310310
* {@inheritdoc}
311311
*/
312-
public function toAscii()
312+
public function toAscii(): self
313313
{
314314
static $pattern = '/[^\x20-\x7f]/';
315315
if (null === $this->domain || !preg_match($pattern, $this->domain)) {
@@ -329,7 +329,7 @@ public function toAscii()
329329
/**
330330
* {@inheritdoc}
331331
*/
332-
public function toUnicode()
332+
public function toUnicode(): self
333333
{
334334
if (null === $this->domain || false === strpos($this->domain, 'xn--')) {
335335
return $this;
@@ -344,4 +344,44 @@ public function toUnicode()
344344

345345
return $clone;
346346
}
347+
348+
public function withPublicSuffix(PublicSuffix $publicSuffix): self
349+
{
350+
if ($this->publicSuffix == $publicSuffix) {
351+
return $this;
352+
}
353+
354+
if (null === $publicSuffix->getContent()) {
355+
$clone = clone $this;
356+
$clone->publicSuffix = $publicSuffix;
357+
$clone->registrableDomain = $clone->setRegistrableDomain();
358+
$clone->subDomain = $clone->setSubDomain();
359+
360+
return $clone;
361+
}
362+
363+
static $pattern = '/[^\x20-\x7f]/';
364+
if (preg_match($pattern, $this->domain)) {
365+
$publicSuffix = $publicSuffix->toUnicode();
366+
}
367+
368+
if (null === $this->domain || false === strpos($this->domain, '.') || $this->domain === $publicSuffix->getContent()) {
369+
throw new Exception(sprintf('The domain `%s` can not contain a public suffix', $this->domain));
370+
}
371+
372+
if ($publicSuffix->getContent() !== substr($this->domain, - strlen($publicSuffix->getContent()))) {
373+
throw new Exception(sprintf(
374+
'the public suffix `%s` can not be assign to the domain name `%s`',
375+
$publicSuffix->getContent(),
376+
$this->domain
377+
));
378+
}
379+
380+
$clone = clone $this;
381+
$clone->publicSuffix = $publicSuffix;
382+
$clone->registrableDomain = $clone->setRegistrableDomain();
383+
$clone->subDomain = $clone->setSubDomain();
384+
385+
return $clone;
386+
}
347387
}

src/IDNAConverterTrait.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private function idnToAscii(string $domain): string
7373
{
7474
static $pattern = '/[^\x20-\x7f]/';
7575
if (!preg_match($pattern, $domain)) {
76-
return $domain;
76+
return strtolower($domain);
7777
}
7878

7979
$output = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46, $arr);
@@ -145,6 +145,7 @@ private function setDomain(string $domain = null): array
145145
^(?:(?&reg_name)\.){0,126}(?&reg_name)\.?$/ix';
146146
if (preg_match($domain_name, $formatted_domain)) {
147147
$domain = strtolower($formatted_domain);
148+
148149
return [$domain, array_reverse(explode('.', $domain))];
149150
}
150151

@@ -161,10 +162,11 @@ private function setDomain(string $domain = null): array
161162
}
162163

163164
//if a domain name contains UTF-8 chars it must be convertible using IDNA UTS46
164-
idn_to_ascii($formatted_domain, 0, INTL_IDNA_VARIANT_UTS46, $arr);
165+
$ascii_domain = idn_to_ascii($formatted_domain, 0, INTL_IDNA_VARIANT_UTS46, $arr);
165166
if (0 === $arr['errors']) {
166-
$domain = strtolower($formatted_domain);
167-
return [$domain, array_reverse(explode('.', $domain))];
167+
$idn_domain = $this->idnToUnicode($ascii_domain);
168+
169+
return [$idn_domain, array_reverse(explode('.', $idn_domain))];
168170
}
169171

170172
throw new Exception(sprintf('The domain `%s` is invalid : %s', $domain, self::getIdnErrors($arr['errors'])));

src/Rules.php

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,17 @@ public function getPublicSuffix(string $domain = null, string $section = self::A
105105
throw new Exception(sprintf('The domain `%s` can not contain a public suffix', $domain));
106106
}
107107

108-
return $this->findPublicSuffix($domainObj, $section);
108+
$publicSuffix = $this->findPublicSuffix($domainObj, $section);
109+
if (null === $publicSuffix->getContent()) {
110+
$publicSuffix = new PublicSuffix($domainObj->getLabel(0));
111+
}
112+
113+
static $pattern = '/[^\x20-\x7f]/';
114+
if (preg_match($pattern, $domainObj->getContent())) {
115+
return $publicSuffix->toUnicode();
116+
}
117+
118+
return $publicSuffix;
109119
}
110120

111121
/**
@@ -125,7 +135,12 @@ public function resolve(string $domain = null, string $section = self::ALL_DOMAI
125135
return $domain;
126136
}
127137

128-
return new Domain($domain->getContent(), $this->findPublicSuffix($domain, $section));
138+
$publicSuffix = $this->findPublicSuffix($domain, $section);
139+
if (null === $publicSuffix->getContent()) {
140+
$publicSuffix = new PublicSuffix($domain->getLabel(0));
141+
}
142+
143+
return $domain->withPublicSuffix($publicSuffix);
129144
} catch (Exception $e) {
130145
return new Domain();
131146
}
@@ -178,19 +193,19 @@ private function findPublicSuffix(DomainInterface $domain, string $section): Pub
178193
$asciiDomain = $domain->toAscii();
179194
$icann = $this->findPublicSuffixFromSection($asciiDomain, self::ICANN_DOMAINS);
180195
if (self::ICANN_DOMAINS === $section) {
181-
return $this->normalizePublicSuffix($icann, $domain);
196+
return $icann;
182197
}
183198

184199
$private = $this->findPublicSuffixFromSection($asciiDomain, self::PRIVATE_DOMAINS);
185200
if (count($private) > count($icann)) {
186-
return $this->normalizePublicSuffix($private, $domain);
201+
return $private;
187202
}
188203

189204
if (self::ALL_DOMAINS === $section) {
190-
return $this->normalizePublicSuffix($icann, $domain);
205+
return $icann;
191206
}
192207

193-
return $this->normalizePublicSuffix(new PublicSuffix(), $domain);
208+
return new PublicSuffix();
194209
}
195210

196211
/**
@@ -232,25 +247,4 @@ private function findPublicSuffixFromSection(DomainInterface $domain, string $se
232247

233248
return new PublicSuffix(implode('.', array_reverse($matches)), $section);
234249
}
235-
236-
/**
237-
* Normalize the found Public Suffix against its domain name.
238-
*
239-
* @param PublicSuffix $publicSuffix
240-
* @param DomainInterface $domain
241-
*
242-
* @return PublicSuffix
243-
*/
244-
private function normalizePublicSuffix(PublicSuffix $publicSuffix, DomainInterface $domain): PublicSuffix
245-
{
246-
if (null === $publicSuffix->getContent()) {
247-
$publicSuffix = new PublicSuffix($domain->getLabel(0));
248-
}
249-
250-
if (false === strpos($domain->getContent() ?? '', 'xn--')) {
251-
return $publicSuffix->toUnicode();
252-
}
253-
254-
return $publicSuffix;
255-
}
256250
}

tests/DomainTest.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Pdp\Domain;
88
use Pdp\Exception;
99
use Pdp\PublicSuffix;
10+
use Pdp\Rules;
1011
use PHPUnit\Framework\TestCase;
1112

1213
/**
@@ -330,4 +331,75 @@ public function toAsciiProvider()
330331
],
331332
];
332333
}
334+
335+
/**
336+
* @param Domain $domain
337+
* @param PublicSuffix $publicSuffix
338+
* @param string|null $expected
339+
*
340+
* @covers ::withPublicSuffix
341+
* @dataProvider withPublicSuffixWorksProvider
342+
*/
343+
public function testWithPublicSuffixWorks(Domain $domain, PublicSuffix $publicSuffix, $expected)
344+
{
345+
$this->assertSame($expected, $domain->withPublicSuffix($publicSuffix)->getPublicSuffix());
346+
}
347+
348+
public function withPublicSuffixWorksProvider()
349+
{
350+
$publicSuffix = new PublicSuffix('ac.be', Rules::ICANN_DOMAINS);
351+
$domain = new Domain('ulb.ac.be', $publicSuffix);
352+
353+
return [
354+
'null public suffix' => [
355+
'domain' => $domain,
356+
'public suffix' => new PublicSuffix(),
357+
'expected' => null,
358+
],
359+
'same public suffix' => [
360+
'domain' => $domain,
361+
'public suffix' => $publicSuffix,
362+
'expected' => 'ac.be',
363+
],
364+
'update public suffix' => [
365+
'domain' => $domain,
366+
'public suffix' => new PublicSuffix('be', Rules::ICANN_DOMAINS),
367+
'expected' => 'be',
368+
],
369+
];
370+
}
371+
372+
/**
373+
* @param Domain $domain
374+
* @param PublicSuffix $publicSuffix
375+
*
376+
* @covers ::withPublicSuffix
377+
* @dataProvider withPublicSuffixFailsProvider
378+
*/
379+
public function testWithPublicSuffixFails(Domain $domain, PublicSuffix $publicSuffix)
380+
{
381+
$this->expectException(Exception::class);
382+
$domain->withPublicSuffix($publicSuffix);
383+
}
384+
385+
public function withPublicSuffixFailsProvider()
386+
{
387+
$publicSuffix = new PublicSuffix('ac.be', Rules::ICANN_DOMAINS);
388+
$domain = new Domain('ulb.ac.be', $publicSuffix);
389+
390+
return [
391+
'public suffix mismatch' => [
392+
'domain' => $domain,
393+
'public suffix' => new PublicSuffix('ac.fr'),
394+
],
395+
'domain name can not contains public suffix' => [
396+
'domain' => new Domain('localhost'),
397+
'public suffix' => $publicSuffix,
398+
],
399+
'domain name is equal to public suffix' => [
400+
'domain' => new Domain('ac.be'),
401+
'public suffix' => $publicSuffix,
402+
],
403+
];
404+
}
333405
}

0 commit comments

Comments
 (0)