4242#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE )]
4343class Restrict implements RouteAttributeInterface
4444{
45- private const TWO_PART_TLDS = [
46- 'co.uk ' , 'org.uk ' , 'gov.uk ' , 'ac.uk ' , 'sch.uk ' , 'ltd.uk ' , 'plc.uk ' ,
47- 'com.au ' , 'net.au ' , 'org.au ' , 'edu.au ' , 'gov.au ' , 'asn.au ' , 'id.au ' ,
48- 'co.jp ' , 'ac.jp ' , 'go.jp ' , 'or.jp ' , 'ne.jp ' , 'gr.jp ' ,
49- 'co.nz ' , 'org.nz ' , 'govt.nz ' , 'ac.nz ' , 'net.nz ' , 'geek.nz ' , 'maori.nz ' , 'school.nz ' ,
50- 'co.in ' , 'net.in ' , 'org.in ' , 'ind.in ' , 'ac.in ' , 'gov.in ' , 'res.in ' ,
51- 'com.cn ' , 'net.cn ' , 'org.cn ' , 'gov.cn ' , 'edu.cn ' ,
52- 'com.sg ' , 'net.sg ' , 'org.sg ' , 'gov.sg ' , 'edu.sg ' , 'per.sg ' ,
53- 'co.za ' , 'org.za ' , 'gov.za ' , 'ac.za ' , 'net.za ' ,
54- 'co.kr ' , 'or.kr ' , 'go.kr ' , 'ac.kr ' , 'ne.kr ' , 'pe.kr ' ,
55- 'co.th ' , 'or.th ' , 'go.th ' , 'ac.th ' , 'net.th ' , 'in.th ' ,
56- 'com.my ' , 'net.my ' , 'org.my ' , 'edu.my ' , 'gov.my ' , 'mil.my ' , 'name.my ' ,
57- 'com.mx ' , 'org.mx ' , 'net.mx ' , 'edu.mx ' , 'gob.mx ' ,
58- 'com.br ' , 'net.br ' , 'org.br ' , 'gov.br ' , 'edu.br ' , 'art.br ' , 'eng.br ' ,
59- 'co.il ' , 'org.il ' , 'ac.il ' , 'gov.il ' , 'net.il ' , 'muni.il ' ,
60- 'co.id ' , 'or.id ' , 'ac.id ' , 'go.id ' , 'net.id ' , 'web.id ' , 'my.id ' ,
61- 'com.hk ' , 'edu.hk ' , 'gov.hk ' , 'idv.hk ' , 'net.hk ' , 'org.hk ' ,
62- 'com.tw ' , 'net.tw ' , 'org.tw ' , 'edu.tw ' , 'gov.tw ' , 'idv.tw ' ,
63- 'com.sa ' , 'net.sa ' , 'org.sa ' , 'gov.sa ' , 'edu.sa ' , 'sch.sa ' , 'med.sa ' ,
64- 'co.ae ' , 'net.ae ' , 'org.ae ' , 'gov.ae ' , 'ac.ae ' , 'sch.ae ' ,
65- 'com.tr ' , 'net.tr ' , 'org.tr ' , 'gov.tr ' , 'edu.tr ' , 'av.tr ' , 'gen.tr ' ,
66- 'co.ke ' , 'or.ke ' , 'go.ke ' , 'ac.ke ' , 'sc.ke ' , 'me.ke ' , 'mobi.ke ' , 'info.ke ' ,
67- 'com.ng ' , 'org.ng ' , 'gov.ng ' , 'edu.ng ' , 'net.ng ' , 'sch.ng ' , 'name.ng ' ,
68- 'com.pk ' , 'net.pk ' , 'org.pk ' , 'gov.pk ' , 'edu.pk ' , 'fam.pk ' ,
69- 'com.eg ' , 'edu.eg ' , 'gov.eg ' , 'org.eg ' , 'net.eg ' ,
70- 'com.cy ' , 'net.cy ' , 'org.cy ' , 'gov.cy ' , 'ac.cy ' ,
71- 'com.lk ' , 'org.lk ' , 'edu.lk ' , 'gov.lk ' , 'net.lk ' , 'int.lk ' ,
72- 'com.bd ' , 'net.bd ' , 'org.bd ' , 'ac.bd ' , 'gov.bd ' , 'mil.bd ' ,
73- 'com.ar ' , 'net.ar ' , 'org.ar ' , 'gov.ar ' , 'edu.ar ' , 'mil.ar ' ,
74- 'gob.cl ' ,
75- ];
76-
7745 public function __construct (
7846 public array |string |null $ environment = null ,
7947 public array |string |null $ hostname = null ,
@@ -145,7 +113,7 @@ private function checkSubdomain(RequestInterface $request): void
145113 return ;
146114 }
147115
148- $ currentSubdomain = $ this -> getSubdomain ( $ request );
116+ $ currentSubdomain = parse_subdomain ( $ request -> getUri ()-> getHost () );
149117 $ allowedSubdomains = array_map ('strtolower ' , (array ) $ this ->subdomain );
150118
151119 // If no subdomain exists but one is required
@@ -158,40 +126,4 @@ private function checkSubdomain(RequestInterface $request): void
158126 throw new PageNotFoundException ('Access denied: subdomain is blocked. ' );
159127 }
160128 }
161-
162- private function getSubdomain (RequestInterface $ request ): string
163- {
164- $ host = strtolower ($ request ->getUri ()->getHost ());
165-
166- // Handle localhost and IP addresses - they don't have subdomains
167- if ($ host === 'localhost ' || filter_var ($ host , FILTER_VALIDATE_IP )) {
168- return '' ;
169- }
170-
171- $ parts = explode ('. ' , $ host );
172- $ partCount = count ($ parts );
173-
174- // Need at least 3 parts for a subdomain (subdomain.domain.tld)
175- // e.g., api.example.com
176- if ($ partCount < 3 ) {
177- return '' ;
178- }
179- // Check if we have a two-part TLD (e.g., co.uk, com.au)
180- $ lastTwoParts = $ parts [$ partCount - 2 ] . '. ' . $ parts [$ partCount - 1 ];
181- if (in_array ($ lastTwoParts , self ::TWO_PART_TLDS , true )) {
182- // For two-part TLD, need at least 4 parts for subdomain
183- // e.g., api.example.co.uk (4 parts)
184- if ($ partCount < 4 ) {
185- return '' ; // No subdomain, just domain.co.uk
186- }
187-
188- // Remove the two-part TLD and domain name (last 3 parts)
189- // e.g., admin.api.example.co.uk -> admin.api
190- return implode ('. ' , array_slice ($ parts , 0 , $ partCount - 3 ));
191- }
192-
193- // Standard TLD: Remove TLD and domain (last 2 parts)
194- // e.g., admin.api.example.com -> admin.api
195- return implode ('. ' , array_slice ($ parts , 0 , $ partCount - 2 ));
196- }
197129}
0 commit comments