20
20
use Pdp \Exception \CouldNotResolveSubDomain ;
21
21
use Pdp \Exception \InvalidLabel ;
22
22
use Pdp \Exception \InvalidLabelKey ;
23
+ use Throwable ;
23
24
use TypeError ;
24
25
use function array_count_values ;
25
26
use function array_keys ;
@@ -82,32 +83,59 @@ final class Domain implements DomainInterface, JsonSerializable
82
83
* @var string|null
83
84
*/
84
85
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 ;
86
101
/**
87
102
* {@inheritdoc}
88
103
*/
89
104
public static function __set_state (array $ properties ): self
90
105
{
91
- return new self ($ properties ['domain ' ], $ properties ['publicSuffix ' ]);
106
+ return new self (
107
+ $ properties ['domain ' ],
108
+ $ properties ['publicSuffix ' ],
109
+ $ properties ['asciiIDNAOption ' ],
110
+ $ properties ['unicodeIDNAOption ' ]
111
+ );
92
112
}
93
-
113
+
94
114
/**
95
115
* New instance.
96
- *
97
116
* @param null|mixed $domain
98
117
* @param null|PublicSuffix $publicSuffix
99
- */
100
- public function __construct ($ domain = null , PublicSuffix $ publicSuffix = null )
101
- {
102
- $ this ->labels = $ this ->setLabels ($ domain );
118
+ * @param int $asciiIDNAOption
119
+ * @param int $unicodeIDNAOption
120
+ */
121
+ public function __construct (
122
+ $ domain = null ,
123
+ PublicSuffix $ publicSuffix = null ,
124
+ int $ asciiIDNAOption = IDNA_DEFAULT ,
125
+ int $ unicodeIDNAOption = IDNA_DEFAULT
126
+ ) {
127
+ $ this ->setIDNAOptions ($ asciiIDNAOption , $ unicodeIDNAOption );
128
+ $ this ->labels = $ this ->setLabels ($ domain , $ asciiIDNAOption , $ unicodeIDNAOption );
103
129
if ([] !== $ this ->labels ) {
104
130
$ this ->domain = implode ('. ' , array_reverse ($ this ->labels ));
105
131
}
106
- $ this ->publicSuffix = $ this ->setPublicSuffix ($ publicSuffix ?? new PublicSuffix ());
132
+ $ this ->publicSuffix = $ this ->setPublicSuffix (
133
+ $ publicSuffix ?? new PublicSuffix (null , '' , $ asciiIDNAOption , $ unicodeIDNAOption )
134
+ );
107
135
$ this ->registrableDomain = $ this ->setRegistrableDomain ();
108
136
$ this ->subDomain = $ this ->setSubDomain ();
109
137
}
110
-
138
+
111
139
/**
112
140
* Sets the public suffix domain part.
113
141
*
@@ -224,7 +252,7 @@ public function jsonSerialize()
224
252
*/
225
253
public function __debugInfo ()
226
254
{
227
- return [
255
+ return [
228
256
'domain ' => $ this ->domain ,
229
257
'registrableDomain ' => $ this ->registrableDomain ,
230
258
'subDomain ' => $ this ->subDomain ,
@@ -346,7 +374,7 @@ public function getPublicSuffix()
346
374
*/
347
375
public function isResolvable (): bool
348
376
{
349
- return 1 < count ($ this ->labels ) && '. ' !== substr ($ this ->domain , -1 , 1 );
377
+ return 1 < count ($ this ->labels ?? [] ) && '. ' !== substr ($ this ->domain , -1 , 1 );
350
378
}
351
379
352
380
/**
@@ -388,12 +416,12 @@ public function toAscii()
388
416
return $ this ;
389
417
}
390
418
391
- $ domain = $ this ->idnToAscii ($ this ->domain );
419
+ $ domain = $ this ->idnToAscii ($ this ->domain , $ this -> asciiIDNAOption );
392
420
if ($ domain === $ this ->domain ) {
393
421
return $ this ;
394
422
}
395
423
396
- return new self ($ domain , $ this ->publicSuffix );
424
+ return new self ($ domain , $ this ->publicSuffix , ... $ this -> getIDNAOptions () );
397
425
}
398
426
399
427
/**
@@ -405,7 +433,11 @@ public function toUnicode()
405
433
return $ this ;
406
434
}
407
435
408
- return new self ($ this ->idnToUnicode ($ this ->domain ), $ this ->publicSuffix );
436
+ return new self (
437
+ $ this ->idnToUnicode ($ this ->domain , $ this ->unicodeIDNAOption ),
438
+ $ this ->publicSuffix ,
439
+ ...$ this ->getIDNAOptions ()
440
+ );
409
441
}
410
442
411
443
/**
@@ -426,15 +458,15 @@ public function toUnicode()
426
458
public function resolve ($ publicSuffix ): self
427
459
{
428
460
if (!$ publicSuffix instanceof PublicSuffix) {
429
- $ publicSuffix = new PublicSuffix ($ publicSuffix );
461
+ $ publicSuffix = new PublicSuffix ($ publicSuffix, '' , ... $ this -> getIDNAOptions () );
430
462
}
431
463
432
464
$ publicSuffix = $ this ->normalize ($ publicSuffix );
433
465
if ($ this ->publicSuffix == $ publicSuffix ) {
434
466
return $ this ;
435
467
}
436
468
437
- return new self ($ this ->domain , $ publicSuffix );
469
+ return new self ($ this ->domain , $ publicSuffix, ... $ this -> getIDNAOptions () );
438
470
}
439
471
440
472
/**
@@ -453,7 +485,7 @@ public function resolve($publicSuffix): self
453
485
public function withPublicSuffix ($ publicSuffix ): self
454
486
{
455
487
if (!$ publicSuffix instanceof PublicSuffix) {
456
- $ publicSuffix = new PublicSuffix ($ publicSuffix );
488
+ $ publicSuffix = new PublicSuffix ($ publicSuffix, '' , ... $ this -> getIDNAOptions () );
457
489
}
458
490
459
491
$ publicSuffix = $ this ->normalize ($ publicSuffix );
@@ -463,10 +495,10 @@ public function withPublicSuffix($publicSuffix): self
463
495
464
496
$ domain = implode ('. ' , array_reverse (array_slice ($ this ->labels , count ($ this ->publicSuffix ))));
465
497
if (null === $ publicSuffix ->getContent ()) {
466
- return new self ($ domain );
498
+ return new self ($ domain, null , ... $ this -> getIDNAOptions () );
467
499
}
468
500
469
- return new self ($ domain .'. ' .$ publicSuffix ->getContent (), $ publicSuffix );
501
+ return new self ($ domain .'. ' .$ publicSuffix ->getContent (), $ publicSuffix, ... $ this -> getIDNAOptions () );
470
502
}
471
503
472
504
@@ -494,10 +526,14 @@ public function withSubDomain($subDomain): self
494
526
}
495
527
496
528
if (null === $ subDomain ) {
497
- return new self ($ this ->registrableDomain , $ this ->publicSuffix );
529
+ return new self ($ this ->registrableDomain , $ this ->publicSuffix , ... $ this -> getIDNAOptions () );
498
530
}
499
531
500
- return new self ($ subDomain .'. ' .$ this ->registrableDomain , $ this ->publicSuffix );
532
+ return new self (
533
+ $ subDomain .'. ' .$ this ->registrableDomain ,
534
+ $ this ->publicSuffix ,
535
+ ...$ this ->getIDNAOptions ()
536
+ );
501
537
}
502
538
503
539
/**
@@ -529,10 +565,10 @@ private function normalizeContent($domain)
529
565
}
530
566
531
567
if (preg_match (self ::REGEXP_IDN_PATTERN , $ this ->domain )) {
532
- return $ this ->idnToUnicode ($ domain );
568
+ return $ this ->idnToUnicode ($ domain, $ this -> unicodeIDNAOption );
533
569
}
534
570
535
- return $ this ->idnToAscii ($ domain );
571
+ return $ this ->idnToAscii ($ domain, $ this -> asciiIDNAOption );
536
572
}
537
573
538
574
/**
@@ -605,10 +641,14 @@ public function withLabel(int $key, $label): self
605
641
ksort ($ labels );
606
642
607
643
if (null !== $ this ->publicSuffix ->getLabel ($ key )) {
608
- return new self (implode ('. ' , array_reverse ($ labels )));
644
+ return new self (implode ('. ' , array_reverse ($ labels )), null , ... $ this -> getIDNAOptions () );
609
645
}
610
646
611
- return new self (implode ('. ' , array_reverse ($ labels )), $ this ->publicSuffix );
647
+ return new self (
648
+ implode ('. ' , array_reverse ($ labels )),
649
+ $ this ->publicSuffix ,
650
+ ...$ this ->getIDNAOptions ()
651
+ );
612
652
}
613
653
614
654
/**
@@ -651,15 +691,63 @@ public function withoutLabel(int $key, int ...$keys): self
651
691
}
652
692
653
693
if ([] === $ labels ) {
654
- return new self ();
694
+ return new self (null , null , ... $ this -> getIDNAOptions () );
655
695
}
656
696
657
697
$ domain = implode ('. ' , array_reverse ($ labels ));
658
698
$ psContent = $ this ->publicSuffix ->getContent ();
659
699
if (null === $ psContent || '. ' .$ psContent !== substr ($ domain , - strlen ($ psContent ) - 1 )) {
660
- return new self ($ domain );
700
+ return new self ($ domain, null , ... $ this -> getIDNAOptions () );
661
701
}
662
702
663
- return new self ($ domain , $ this ->publicSuffix );
703
+ return new self ($ domain , $ this ->publicSuffix , ...$ this ->getIDNAOptions ());
704
+ }
705
+
706
+ /**
707
+ * @return array
708
+ */
709
+ public function getIDNAOptions (): array
710
+ {
711
+ return [$ this ->asciiIDNAOption , $ this ->unicodeIDNAOption ];
712
+ }
713
+
714
+ public function getAsciiIDNAOption (): int
715
+ {
716
+ return $ this ->asciiIDNAOption ;
717
+ }
718
+
719
+ public function getUnicodeIDNAOption (): int
720
+ {
721
+ return $ this ->unicodeIDNAOption ;
722
+ }
723
+ /**
724
+ * Set IDNA_* options for functions idn_to_ascii, idn_to_utf.
725
+ * @see https://www.php.net/manual/en/intl.constants.php
726
+ * @param int $forAscii
727
+ * @param int $forUnicode
728
+ * @return $this
729
+ */
730
+ public function setIDNAOptions (int $ forAscii , int $ forUnicode )
731
+ {
732
+ $ this ->asciiIDNAOption = $ forAscii ;
733
+ $ this ->unicodeIDNAOption = $ forUnicode ;
734
+ return $ this ;
735
+ }
736
+
737
+ /**
738
+ * return true if domain contains deviation characters.
739
+ * @see http://unicode.org/reports/tr46/#Transition_Considerations
740
+ * @return bool
741
+ **/
742
+ public function isTransitionalDifferent (): bool
743
+ {
744
+ if ($ this ->isTransitionalDifferent === null ) {
745
+ try {
746
+ $ this ->idnToAscii ($ this ->getContent (), $ this ->asciiIDNAOption );
747
+ } catch (Throwable $ e ) {
748
+ $ this ->isTransitionalDifferent = false ;
749
+ }
750
+ }
751
+ return $ this ->isTransitionalDifferent ;
664
752
}
665
753
}
0 commit comments