@@ -42,6 +42,11 @@ protected function parseUri(string $uri):false|array {
4242 return $ authorityStyleParts ;
4343 }
4444
45+ $ hostLikeParts = $ this ->parseHostLikeParts ($ uri , $ parts );
46+ if (!is_null ($ hostLikeParts )) {
47+ return $ hostLikeParts ;
48+ }
49+
4550 return $ parts ;
4651 }
4752
@@ -77,6 +82,34 @@ protected function parseAuthorityStyleParts(
7782 return $ authorityParts ;
7883 }
7984
85+ /**
86+ * @param array<string, int|string> $parts
87+ * @return null|array<string, int|string>
88+ */
89+ protected function parseHostLikeParts (
90+ string $ uri ,
91+ array $ parts
92+ ):?array {
93+ if (!$ this ->canBeHostLikeUri ($ uri , $ parts )) {
94+ return null ;
95+ }
96+
97+ $ hostLikeParts = parse_url ("// " . $ uri );
98+ if ($ hostLikeParts === false || !isset ($ hostLikeParts ["host " ])) {
99+ return null ;
100+ }
101+
102+ if (!$ this ->isAuthorityPathValid ($ hostLikeParts )) {
103+ return null ;
104+ }
105+
106+ if (!$ this ->isAuthorityHostLike ($ hostLikeParts )) {
107+ return null ;
108+ }
109+
110+ return $ hostLikeParts ;
111+ }
112+
80113 /** @param array<string, int|string> $parts */
81114 protected function canBeAuthorityStyleUri (string $ uri , array $ parts ):bool {
82115 if (str_contains ($ uri , ":// " )) {
@@ -91,6 +124,29 @@ protected function canBeAuthorityStyleUri(string $uri, array $parts):bool {
91124 return str_contains ($ path , "@ " );
92125 }
93126
127+ /** @param array<string, int|string> $parts */
128+ protected function canBeHostLikeUri (string $ uri , array $ parts ):bool {
129+ if (str_contains ($ uri , ":// " )) {
130+ return false ;
131+ }
132+
133+ if (isset ($ parts ["host " ]) || !isset ($ parts ["path " ])) {
134+ return false ;
135+ }
136+
137+ if (isset ($ parts ["scheme " ])) {
138+ return false ;
139+ }
140+
141+ $ path = (string )$ parts ["path " ];
142+ if ($ path === "" || str_starts_with ($ path , "/ " )) {
143+ return false ;
144+ }
145+
146+ $ firstSegment = explode ("/ " , $ path , 2 )[0 ];
147+ return $ this ->isHostLikeString ($ firstSegment );
148+ }
149+
94150 /** @param array<string, int|string> $authorityParts */
95151 protected function hasRequiredAuthorityParts (array $ authorityParts ):bool {
96152 return isset ($ authorityParts ["user " ], $ authorityParts ["pass " ], $ authorityParts ["host " ]);
@@ -104,7 +160,16 @@ protected function isAuthorityPathValid(array $authorityParts):bool {
104160
105161 /** @param array<string, int|string> $authorityParts */
106162 protected function isAuthorityHostLike (array $ authorityParts ):bool {
107- $ host = (string )$ authorityParts ["host " ];
163+ return $ this ->isHostLikeString ((string )$ authorityParts ["host " ]);
164+ }
165+
166+ protected function isHostLikeString (string $ host ):bool {
167+ if ($ host === ". "
168+ || $ host === ".. "
169+ || str_starts_with ($ host , ". " )) {
170+ return false ;
171+ }
172+
108173 if (filter_var ($ host , FILTER_VALIDATE_IP ) !== false ) {
109174 return true ;
110175 }
0 commit comments