1: | <?php
|
2: | namespace Geekwright\RegDom;
|
3: |
|
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: |
|
15: | class RegisteredDomain
|
16: | {
|
17: | protected $tree;
|
18: | protected $psl;
|
19: |
|
20: | |
21: | |
22: | |
23: | |
24: |
|
25: | public function __construct(PublicSuffixList $psl = null)
|
26: | {
|
27: | if (null === $psl) {
|
28: | $psl = new PublicSuffixList();
|
29: | }
|
30: | $this->psl = $psl;
|
31: | }
|
32: |
|
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | |
40: |
|
41: | protected function normalizeHost($url)
|
42: | {
|
43: | $host = (false!==strpos($url, '/')) ? parse_url($url, PHP_URL_HOST) : $url;
|
44: | $parts = explode('.', $host);
|
45: | $utf8Host = '';
|
46: | foreach ($parts as $part) {
|
47: | $utf8Host = $utf8Host . (($utf8Host === '') ? '' : '.') . $this->convertPunycode($part);
|
48: | }
|
49: |
|
50: | return mb_strtolower($utf8Host);
|
51: | }
|
52: |
|
53: | |
54: | |
55: | |
56: | |
57: | |
58: | |
59: |
|
60: | protected function convertPunycode($part)
|
61: | {
|
62: | if (strpos($part, 'xn--')===0) {
|
63: | if (function_exists('idn_to_utf8')) {
|
64: | if (defined('INTL_IDNA_VARIANT_UTS46')) {
|
65: | return idn_to_utf8($part, 0, INTL_IDNA_VARIANT_UTS46);
|
66: | }
|
67: | return idn_to_utf8($part);
|
68: | }
|
69: | return $this->decodePunycode($part);
|
70: | }
|
71: | return $part;
|
72: | }
|
73: |
|
74: | |
75: | |
76: | |
77: | |
78: | |
79: | |
80: | |
81: |
|
82: | protected function decodePunycode($encoded)
|
83: | {
|
84: | $prefix = 'xn--';
|
85: | $safe_char = 0xFFFC;
|
86: | $base = 36;
|
87: | $tmin = 1;
|
88: | $tmax = 26;
|
89: | $skew = 38;
|
90: | $damp = 700;
|
91: |
|
92: | if (strpos($encoded, $prefix) !== 0 || strlen(trim(str_replace($prefix, '', $encoded))) == 0) {
|
93: | return $encoded;
|
94: | }
|
95: |
|
96: | $is_first = true;
|
97: | $bias = 72;
|
98: | $idx = 0;
|
99: | $char = 0x80;
|
100: | $decoded = array();
|
101: | $output = '';
|
102: |
|
103: | $delim_pos = strrpos($encoded, '-');
|
104: | if ($delim_pos > strlen($prefix)) {
|
105: | for ($k = strlen($prefix); $k < $delim_pos; ++$k) {
|
106: | $decoded[] = ord($encoded[$k]);
|
107: | }
|
108: | }
|
109: | $deco_len = count($decoded);
|
110: | $enco_len = strlen($encoded);
|
111: |
|
112: | for ($enco_idx = $delim_pos ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) {
|
113: | for ($old_idx = $idx, $w = 1, $k = $base; 1; $k += $base) {
|
114: | $cp = ord($encoded[$enco_idx++]);
|
115: | $digit = ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $base));
|
116: | $idx += $digit * $w;
|
117: | $t = ($k <= $bias) ? $tmin : (($k >= $bias + $tmax) ? $tmax : ($k - $bias));
|
118: | if ($digit < $t) {
|
119: | break;
|
120: | }
|
121: | $w = (int)($w * ($base - $t));
|
122: | }
|
123: | $delta = $idx - $old_idx;
|
124: | $delta = (int) ($is_first ? ($delta / $damp) : ($delta / 2));
|
125: | $delta += (int) ($delta / ($deco_len + 1));
|
126: | for ($k = 0; $delta > (($base - $tmin) * $tmax) / 2; $k += $base) {
|
127: | $delta = (int) ($delta / ($base - $tmin));
|
128: | }
|
129: | $bias = (int) ($k + ($base - $tmin + 1) * $delta / ($delta + $skew));
|
130: | $is_first = false;
|
131: | $char += (int)($idx / ($deco_len + 1));
|
132: | $idx %= ($deco_len + 1);
|
133: | if ($deco_len > 0) {
|
134: | for ($i = $deco_len; $i > $idx; $i--) {
|
135: | $decoded[$i] = $decoded[($i - 1)];
|
136: | }
|
137: | }
|
138: | $decoded[$idx++] = $char;
|
139: | }
|
140: |
|
141: | foreach ($decoded as $k => $v) {
|
142: | if ($v < 128) {
|
143: | $output .= chr($v);
|
144: | }
|
145: | elseif ($v < (1 << 11)) {
|
146: | $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63));
|
147: | }
|
148: | elseif ($v < (1 << 16)) {
|
149: | $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
|
150: | }
|
151: | elseif ($v < (1 << 21)) {
|
152: | $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63));
|
153: | }
|
154: | else {
|
155: | $output .= $safe_char;
|
156: | }
|
157: | }
|
158: | return $output;
|
159: | }
|
160: |
|
161: | |
162: | |
163: | |
164: | |
165: | |
166: | |
167: |
|
168: | public function getRegisteredDomain($host)
|
169: | {
|
170: | $this->tree = $this->psl->getTree();
|
171: |
|
172: | $signingDomain = $this->normalizeHost($host);
|
173: | $signingDomainParts = explode('.', $signingDomain);
|
174: |
|
175: | $result = $this->findRegisteredDomain($signingDomainParts, $this->tree);
|
176: |
|
177: | if (empty($result)) {
|
178: |
|
179: | return null;
|
180: | }
|
181: |
|
182: |
|
183: | if (!strpos($result, '.')) {
|
184: | $cnt = count($signingDomainParts);
|
185: | if ($cnt == 1 || $signingDomainParts[$cnt-2] == '') {
|
186: | return null;
|
187: | }
|
188: | return $signingDomainParts[$cnt-2] . '.' . $signingDomainParts[$cnt-1];
|
189: | }
|
190: | return $result;
|
191: | }
|
192: |
|
193: | |
194: | |
195: | |
196: | |
197: | |
198: | |
199: | |
200: |
|
201: | protected function findRegisteredDomain($remainingSigningDomainParts, &$treeNode)
|
202: | {
|
203: | $sub = array_pop($remainingSigningDomainParts);
|
204: |
|
205: | $result = null;
|
206: | if (isset($treeNode['!'])) {
|
207: | return '';
|
208: | } elseif (is_array($treeNode) && array_key_exists($sub, $treeNode)) {
|
209: | $result = $this->findRegisteredDomain($remainingSigningDomainParts, $treeNode[$sub]);
|
210: | } elseif (is_array($treeNode) && array_key_exists('*', $treeNode)) {
|
211: | $result = $this->findRegisteredDomain($remainingSigningDomainParts, $treeNode['*']);
|
212: | } else {
|
213: | return $sub;
|
214: | }
|
215: |
|
216: | if ($result === '') {
|
217: | return $sub;
|
218: | } elseif (strlen($result)>0) {
|
219: | return $result . '.' . $sub;
|
220: | }
|
221: | return null;
|
222: | }
|
223: | }
|
224: | |