Skip to content

Commit 9b348d3

Browse files
committed
Merge branch 'custom_idna_options' into idna_options
2 parents eaf6017 + 5dd957f commit 9b348d3

10 files changed

+541
-67
lines changed

src/Domain.php

Lines changed: 131 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Pdp\Exception\CouldNotResolveSubDomain;
2121
use Pdp\Exception\InvalidLabel;
2222
use Pdp\Exception\InvalidLabelKey;
23+
use Throwable;
2324
use TypeError;
2425
use function array_count_values;
2526
use function array_keys;
@@ -82,32 +83,62 @@ final class Domain implements DomainInterface, JsonSerializable
8283
* @var string|null
8384
*/
8485
private $subDomain;
85-
86+
87+
/**
88+
* @var int
89+
*/
90+
private $asciiIDNAOption = IDNA_DEFAULT;
91+
92+
/**
93+
* @var int
94+
*/
95+
private $unicodeIDNAOption = IDNA_DEFAULT;
96+
97+
/**
98+
* @var bool
99+
*/
100+
private $isTransitionalDifferent;
101+
86102
/**
87103
* {@inheritdoc}
88104
*/
89105
public static function __set_state(array $properties): self
90106
{
91-
return new self($properties['domain'], $properties['publicSuffix']);
107+
return new self(
108+
$properties['domain'],
109+
$properties['publicSuffix'],
110+
$properties['asciiIDNAOption'] ?? IDNA_DEFAULT,
111+
$properties['unicodeIDNAOption'] ?? IDNA_DEFAULT
112+
);
92113
}
93-
114+
94115
/**
95116
* New instance.
96-
*
97117
* @param null|mixed $domain
98118
* @param null|PublicSuffix $publicSuffix
99-
*/
100-
public function __construct($domain = null, PublicSuffix $publicSuffix = null)
101-
{
102-
$this->labels = $this->setLabels($domain);
119+
* @param int $asciiIDNAOption
120+
* @param int $unicodeIDNAOption
121+
*/
122+
public function __construct(
123+
$domain = null,
124+
PublicSuffix $publicSuffix = null,
125+
int $asciiIDNAOption = IDNA_DEFAULT,
126+
int $unicodeIDNAOption = IDNA_DEFAULT
127+
) {
128+
$this->asciiIDNAOption = $asciiIDNAOption;
129+
$this->unicodeIDNAOption = $unicodeIDNAOption;
130+
$this->labels = $this->setLabels($domain, $asciiIDNAOption, $unicodeIDNAOption);
131+
$this->isTransitionalDifferent = $this->hasTransitionalDifference($domain);
103132
if ([] !== $this->labels) {
104133
$this->domain = implode('.', array_reverse($this->labels));
105134
}
106-
$this->publicSuffix = $this->setPublicSuffix($publicSuffix ?? new PublicSuffix());
135+
$this->publicSuffix = $this->setPublicSuffix(
136+
$publicSuffix ?? new PublicSuffix(null, '', $asciiIDNAOption, $unicodeIDNAOption)
137+
);
107138
$this->registrableDomain = $this->setRegistrableDomain();
108139
$this->subDomain = $this->setSubDomain();
109140
}
110-
141+
111142
/**
112143
* Sets the public suffix domain part.
113144
*
@@ -224,7 +255,7 @@ public function jsonSerialize()
224255
*/
225256
public function __debugInfo()
226257
{
227-
return [
258+
return [
228259
'domain' => $this->domain,
229260
'registrableDomain' => $this->registrableDomain,
230261
'subDomain' => $this->subDomain,
@@ -388,12 +419,12 @@ public function toAscii()
388419
return $this;
389420
}
390421

391-
$domain = $this->idnToAscii($this->domain);
422+
$domain = $this->idnToAscii($this->domain, $this->asciiIDNAOption);
392423
if ($domain === $this->domain) {
393424
return $this;
394425
}
395426

396-
return new self($domain, $this->publicSuffix);
427+
return new self($domain, $this->publicSuffix, $this->getAsciiIDNAOption(), $this->getUnicodeIDNAOption());
397428
}
398429

399430
/**
@@ -405,7 +436,12 @@ public function toUnicode()
405436
return $this;
406437
}
407438

408-
return new self($this->idnToUnicode($this->domain), $this->publicSuffix);
439+
return new self(
440+
$this->idnToUnicode($this->domain, $this->unicodeIDNAOption),
441+
$this->publicSuffix,
442+
$this->getAsciiIDNAOption(),
443+
$this->getUnicodeIDNAOption()
444+
);
409445
}
410446

411447
/**
@@ -426,15 +462,20 @@ public function toUnicode()
426462
public function resolve($publicSuffix): self
427463
{
428464
if (!$publicSuffix instanceof PublicSuffix) {
429-
$publicSuffix = new PublicSuffix($publicSuffix);
465+
$publicSuffix = new PublicSuffix(
466+
$publicSuffix,
467+
'',
468+
$this->getAsciiIDNAOption(),
469+
$this->getUnicodeIDNAOption()
470+
);
430471
}
431472

432473
$publicSuffix = $this->normalize($publicSuffix);
433474
if ($this->publicSuffix == $publicSuffix) {
434475
return $this;
435476
}
436477

437-
return new self($this->domain, $publicSuffix);
478+
return new self($this->domain, $publicSuffix, $this->getAsciiIDNAOption(), $this->getUnicodeIDNAOption());
438479
}
439480

440481
/**
@@ -453,7 +494,12 @@ public function resolve($publicSuffix): self
453494
public function withPublicSuffix($publicSuffix): self
454495
{
455496
if (!$publicSuffix instanceof PublicSuffix) {
456-
$publicSuffix = new PublicSuffix($publicSuffix);
497+
$publicSuffix = new PublicSuffix(
498+
$publicSuffix,
499+
'',
500+
$this->getAsciiIDNAOption(),
501+
$this->getUnicodeIDNAOption()
502+
);
457503
}
458504

459505
$publicSuffix = $this->normalize($publicSuffix);
@@ -463,10 +509,15 @@ public function withPublicSuffix($publicSuffix): self
463509

464510
$domain = implode('.', array_reverse(array_slice($this->labels, count($this->publicSuffix))));
465511
if (null === $publicSuffix->getContent()) {
466-
return new self($domain);
512+
return new self($domain, null, $this->getAsciiIDNAOption(), $this->getUnicodeIDNAOption());
467513
}
468514

469-
return new self($domain.'.'.$publicSuffix->getContent(), $publicSuffix);
515+
return new self(
516+
$domain.'.'.$publicSuffix->getContent(),
517+
$publicSuffix,
518+
$this->getAsciiIDNAOption(),
519+
$this->getUnicodeIDNAOption()
520+
);
470521
}
471522

472523

@@ -494,10 +545,20 @@ public function withSubDomain($subDomain): self
494545
}
495546

496547
if (null === $subDomain) {
497-
return new self($this->registrableDomain, $this->publicSuffix);
548+
return new self(
549+
$this->registrableDomain,
550+
$this->publicSuffix,
551+
$this->getAsciiIDNAOption(),
552+
$this->getUnicodeIDNAOption()
553+
);
498554
}
499555

500-
return new self($subDomain.'.'.$this->registrableDomain, $this->publicSuffix);
556+
return new self(
557+
$subDomain.'.'.$this->registrableDomain,
558+
$this->publicSuffix,
559+
$this->getAsciiIDNAOption(),
560+
$this->getUnicodeIDNAOption()
561+
);
501562
}
502563

503564
/**
@@ -529,10 +590,10 @@ private function normalizeContent($domain)
529590
}
530591

531592
if (preg_match(self::REGEXP_IDN_PATTERN, $this->domain)) {
532-
return $this->idnToUnicode($domain);
593+
return $this->idnToUnicode($domain, $this->unicodeIDNAOption);
533594
}
534595

535-
return $this->idnToAscii($domain);
596+
return $this->idnToAscii($domain, $this->asciiIDNAOption);
536597
}
537598

538599
/**
@@ -605,10 +666,20 @@ public function withLabel(int $key, $label): self
605666
ksort($labels);
606667

607668
if (null !== $this->publicSuffix->getLabel($key)) {
608-
return new self(implode('.', array_reverse($labels)));
669+
return new self(
670+
implode('.', array_reverse($labels)),
671+
null,
672+
$this->getAsciiIDNAOption(),
673+
$this->getUnicodeIDNAOption()
674+
);
609675
}
610676

611-
return new self(implode('.', array_reverse($labels)), $this->publicSuffix);
677+
return new self(
678+
implode('.', array_reverse($labels)),
679+
$this->publicSuffix,
680+
$this->getAsciiIDNAOption(),
681+
$this->getUnicodeIDNAOption()
682+
);
612683
}
613684

614685
/**
@@ -651,15 +722,47 @@ public function withoutLabel(int $key, int ...$keys): self
651722
}
652723

653724
if ([] === $labels) {
654-
return new self();
725+
return new self(null, null, $this->getAsciiIDNAOption(), $this->getUnicodeIDNAOption());
655726
}
656727

657728
$domain = implode('.', array_reverse($labels));
658729
$psContent = $this->publicSuffix->getContent();
659730
if (null === $psContent || '.'.$psContent !== substr($domain, - strlen($psContent) - 1)) {
660-
return new self($domain);
731+
return new self($domain, null, $this->getAsciiIDNAOption(), $this->getUnicodeIDNAOption());
661732
}
662733

663-
return new self($domain, $this->publicSuffix);
734+
return new self($domain, $this->publicSuffix, $this->getAsciiIDNAOption(), $this->getUnicodeIDNAOption());
735+
}
736+
737+
738+
public function getAsciiIDNAOption(): int
739+
{
740+
return $this->asciiIDNAOption;
741+
}
742+
743+
public function getUnicodeIDNAOption(): int
744+
{
745+
return $this->unicodeIDNAOption;
746+
}
747+
/**
748+
* Set IDNA_* options for functions idn_to_ascii, idn_to_utf.
749+
* @see https://www.php.net/manual/en/intl.constants.php
750+
* @param int $forAscii
751+
* @param int $forUnicode
752+
* @return $this
753+
*/
754+
public function withIDNAOptions(int $forAscii, int $forUnicode)
755+
{
756+
return new self($this->domain, $this->publicSuffix, $forAscii, $forUnicode);
757+
}
758+
759+
/**
760+
* return true if domain contains deviation characters.
761+
* @see http://unicode.org/reports/tr46/#Transition_Considerations
762+
* @return bool
763+
**/
764+
public function isTransitionalDifferent(): bool
765+
{
766+
return $this->isTransitionalDifferent;
664767
}
665768
}

src/DomainInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,11 @@ public function toUnicode();
9999
* from the right-most label to the left-most label.
100100
*/
101101
public function getIterator();
102+
103+
/**
104+
* return true if domain contains deviation characters.
105+
* @see http://unicode.org/reports/tr46/#Transition_Considerations
106+
* @return bool
107+
**/
108+
public function isTransitionalDifferent(): bool;
102109
}

0 commit comments

Comments
 (0)