ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
URI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
21namespace ILIAS\Data;
22
34class URI
35{
36 private const PATH_DELIM = '/';
37
41 private const ALPHA = '[A-Za-z]';
42 private const DIGIT = '[0-9]';
43 private const ALPHA_DIGIT = '[A-Za-z0-9]';
44 private const HEXDIG = '[0-9A-Fa-f]';
45 private const PCTENCODED = '%' . self::HEXDIG . self::HEXDIG;
49 private const PIMP = '[\\+\\-\\.]';
50
55 private const SUBDELIMS = '[\\$,;=!&\'\\(\\)\\*\\+]';
59 private const BASEURI_SUBDELIMS = '[\\$,;&\'\\*]';
60
61 private const UNRESERVED = self::ALPHA_DIGIT . '|[\\-\\._~]';
62 private const UNRESERVED_NO_DOT = self::ALPHA_DIGIT . '|[\\-_~]';
63
64 private const PCHAR = self::UNRESERVED . '|' . self::SUBDELIMS . '|' . self::PCTENCODED;
65
66 private const SCHEMA = '#^' . self::ALPHA . '(' . self::ALPHA_DIGIT . '|' . self::PIMP . ')*$#';
67 private const DOMAIN_LABEL = self::ALPHA_DIGIT . '((' . self::UNRESERVED_NO_DOT . '|' . self::PCTENCODED . '|' . self::BASEURI_SUBDELIMS . ')*' . self::ALPHA_DIGIT . ')*';
68 private const HOST_REG_NAME = '^' . self::DOMAIN_LABEL . '(\\.' . self::DOMAIN_LABEL . ')*$';
69 private const HOST_IPV4_EMBEDDED = '(' . self::DIGIT . '{1,3})(\\.' . self::DIGIT . '{1,3}){3}';
70 private const HOST_IPV4 = '^' . self::HOST_IPV4_EMBEDDED . '$';
71
72 private const IPV6_COUNT_COLONS = '(' . self::HEXDIG . '{1,4}:)';
73 private const IPV6_COLONS_BETWEEN = '(' . self::HEXDIG . '{0,4}:' . self::HEXDIG . '{0,4})';
74 private const IPV6_LEFT_SHORT = '(' . self::HEXDIG . '{1,4}|' . self::HEXDIG . '{1,4}(:' . self::HEXDIG . '{1,4})*)?'; // Left of :: are separated with one ":".
75
76 // As specified in RFC4291 Section 2.2 there are three different text forms of an IPv6 address.
77 // HOST_IPV6_LONG corresponds to the form defined in RFC4291 Section 2.2.1.
78 private const HOST_IPV6_LONG = '^\\[' . self::IPV6_COUNT_COLONS . '{7}' . self::HEXDIG . '{1,4}\\]$';
79 // HOST_IPV6_SHORT corresponds to the form defined in RFC4291 Section 2.2.2.
80 private const HOST_IPV6_SHORT = '^\\[(?=' . self::IPV6_COLONS_BETWEEN . '{2,7}\\]$)' . // Ensure whole string contains between 2-7 colons.
81 self::IPV6_LEFT_SHORT . '::' .
82 // Right of "::" are separated with one ":".
83 '(' . self::HEXDIG . '{1,4}|(' . self::HEXDIG . '{1,4}:)*' . self::HEXDIG . '{1,4})?\\]$';
84
85 // HOST_IPV6_EMBEDDED_LONG corresponds to the form defined in RFC4291 Section 2.2.3 but not in a compressed form.
86 private const HOST_IPV6_EMBEDDED_IPV4_LONG = '^\\[' . self::IPV6_COUNT_COLONS . '{5}' . self::HEXDIG . '{1,4}:' . self::HOST_IPV4_EMBEDDED . '\\]$';
87 // HOST_IPV6_EMBEDDED_SHORT corresponds to the form defined in RFC4291 Section 2.2.3 in a compressed form.
88 private const HOST_IPV6_EMBEDDED_IPV4_SHORT = '^\\[(?=' . self::IPV6_COLONS_BETWEEN . '{2,6}' . self::HOST_IPV4_EMBEDDED . '\\]$)' . // Ensure whole string contains between 2-6 colons.
89 self::IPV6_LEFT_SHORT . '::' .
90 // Right of "::" are separated with one ":".
91 // The hexdig pair before the ipv4 is 0-4 to accommodate for both ::1.2.3.4 and ::2:1.2.3.4 colon usages.
92 '(' . self::HEXDIG . '{1,4}|(' . self::HEXDIG . '{1,4}:)*' . self::HEXDIG . '{0,4})?' . self::HOST_IPV4_EMBEDDED . '\\]$';
93
94 private const HOST_IPV6_EMBEDDED_IPV4 = self::HOST_IPV6_EMBEDDED_IPV4_SHORT . '|' . self::HOST_IPV6_EMBEDDED_IPV4_LONG;
95
96 private const HOST_IPV6 = self::HOST_IPV6_LONG . '|' . self::HOST_IPV6_SHORT . '|' . self::HOST_IPV6_EMBEDDED_IPV4;
97 private const HOST = '#' . self::HOST_IPV4 . '|' . self::HOST_REG_NAME . '|' . self::HOST_IPV6 . '#';
98 private const PORT = '#^' . self::DIGIT . '+$#';
99 private const PATH = '#^(?!//)(?!:)(' . self::PCHAR . '|' . self::PATH_DELIM . ')+$#';
100 private const QUERY = '#^(' . self::PCHAR . '|' . self::PATH_DELIM . '|\\?)+$#';
101 private const FRAGMENT = '#^(' . self::PCHAR . '|' . self::PATH_DELIM . '|\\?|\\#)+$#';
102
103 protected string $schema;
104 protected string $host;
105 protected ?int $port;
106 protected ?string $path;
107 protected ?string $query;
108 protected ?string $fragment;
109
110 public function __construct(string $uri_string)
111 {
112 $this->schema = $this->digestSchema(parse_url($uri_string, PHP_URL_SCHEME));
113 $this->host = $this->digestHost(parse_url($uri_string, PHP_URL_HOST));
114 $this->port = $this->digestPort(parse_url($uri_string, PHP_URL_PORT));
115 $this->path = $this->digestPath(parse_url($uri_string, PHP_URL_PATH));
116 $this->query = $this->digestQuery(parse_url($uri_string, PHP_URL_QUERY));
117 $this->fragment = $this->digestFragment(parse_url($uri_string, PHP_URL_FRAGMENT));
118 }
119
123 protected function digestSchema(string $schema): string
124 {
125 return $this->checkCorrectFormatOrThrow(self::SCHEMA, $schema);
126 }
127
131 protected function digestHost(string $host): string
132 {
133 return $this->checkCorrectFormatOrThrow(self::HOST, $host);
134 }
135
139 protected function digestPort(?int $port = null): ?int
140 {
141 return $port ?? null;
142 }
143
147 protected function digestPath(?string $path = null): ?string
148 {
149 if ($path === null) {
150 return null;
151 }
152 $path = trim($this->checkCorrectFormatOrThrow(self::PATH, $path), self::PATH_DELIM);
153 if ($path === '') {
154 $path = null;
155 }
156 return $path;
157 }
158
162 protected function digestQuery(?string $query = null): ?string
163 {
164 if ($query === null) {
165 return null;
166 }
167 $qparts = explode('&', $query);
168 foreach ($qparts as $q) {
169 $this->checkCorrectFormatOrThrow(self::QUERY, $q);
170 }
171 return $query;
172 }
173
177 protected function digestFragment(?string $fragment = null): ?string
178 {
179 if ($fragment === null) {
180 return null;
181 }
182 return $this->checkCorrectFormatOrThrow(self::FRAGMENT, $fragment);
183 }
184
185
190 protected function checkCorrectFormatOrThrow(string $regexp, string $string): string
191 {
192 if (preg_match($regexp, $string) === 1) {
193 return $string;
194 }
195 throw new \InvalidArgumentException('ill-formated component "' . $string . '" expected "' . $regexp . '"');
196 }
197
198 public function getSchema(): string
199 {
200 return $this->schema;
201 }
202
206 public function withSchema(string $schema): URI
207 {
208 $shema = $this->digestSchema($schema);
209 $other = clone $this;
210 $other->schema = $schema;
211 return $other;
212 }
213
214 public function getAuthority(): string
215 {
216 $port = $this->getPort();
217 if ($port === null) {
218 return $this->getHost();
219 }
220 return $this->getHost() . ':' . $port;
221 }
222
226 public function withAuthority(string $authority): URI
227 {
228 $parts = explode(':', $authority);
229 if (count($parts) > 2) {
230 throw new \InvalidArgumentException('ill-formated component ' . $authority);
231 }
232 $host = $this->digestHost($parts[0]);
233 $port = null;
234 if (array_key_exists(1, $parts)) {
235 $port = (int) $this->checkCorrectFormatOrThrow(self::PORT, (string) $parts[1]);
236 }
237 $other = clone $this;
238 $other->host = $host;
239 $other->port = $port;
240 return $other;
241 }
242
243 public function getPort(): ?int
244 {
245 return $this->port;
246 }
247
251 public function withPort(?int $port = null): URI
252 {
253 $port = $this->digestPort($port);
254 $other = clone $this;
255 $other->port = $port;
256 return $other;
257 }
258
259 public function getHost(): string
260 {
261 return $this->host;
262 }
263
267 public function withHost(string $host): URI
268 {
269 $host = $this->digestHost($host);
270 $other = clone $this;
271 $other->host = $host;
272 return $other;
273 }
274
275 public function getPath(): ?string
276 {
277 return $this->path;
278 }
279
283 public function withPath(?string $path = null): URI
284 {
285 $path = $this->digestPath($path);
286 $other = clone $this;
287 $other->path = $path;
288 return $other;
289 }
290
291 public function getQuery(): ?string
292 {
293 return $this->query;
294 }
295
299 public function withQuery(?string $query = null): URI
300 {
301 $query = $this->digestQuery($query);
302 $other = clone $this;
303 $other->query = $query;
304 return $other;
305 }
306
307 public function getFragment(): ?string
308 {
309 return $this->fragment;
310 }
311
315 public function withFragment(?string $fragment = null): URI
316 {
317 $fragment = $this->digestFragment($fragment);
318 $other = clone $this;
319 $other->fragment = $fragment;
320 return $other;
321 }
322
327 public function getBaseURI(): string
328 {
329 $path = $this->getPath();
330 if ($path === null) {
331 return $this->getSchema() . '://' . $this->getAuthority();
332 }
333 return $this->getSchema() . '://' . $this->getAuthority() . '/' . $path;
334 }
335
336 public function __toString(): string
337 {
338 $uri = $this->getBaseURI();
339 $query = $this->getQuery();
340 if ($query) {
341 $uri .= '?' . $query;
342 }
343 $fragment = $this->getFragment();
344 if ($fragment) {
345 $uri .= '#' . $fragment;
346 }
347 return $uri;
348 }
349
353 public function getParameters(): array
354 {
355 $params = [];
356 $query = $this->getQuery();
357 if (!is_null($query)) {
358 parse_str($query, $params);
359 }
360 return $params;
361 }
362
367 public function getParameter(string $param)
368 {
369 $params = $this->getParameters();
370
371 return $params[$param] ?? null;
372 }
373
377 public function withParameters(array $parameters): URI
378 {
379 return $this->withQuery(
380 http_build_query($parameters)
381 );
382 }
383
387 public function withParameter(string $key, $value): URI
388 {
389 $params = $this->getParameters();
390 $params[$key] = $value;
391 return $this->withParameters($params);
392 }
393}
The scope of this class is split ilias-conform URI's into components.
Definition: URI.php:35
string $schema
Definition: URI.php:103
string $query
Definition: URI.php:107
digestPath(?string $path=null)
Check path formating.
Definition: URI.php:147
withParameters(array $parameters)
Get URI with modified parameters.
Definition: URI.php:377
withFragment(?string $fragment=null)
Get URI with modified fragment.
Definition: URI.php:315
__construct(string $uri_string)
Definition: URI.php:110
getParameter(string $param)
Get the value of the given parameter (or null)
Definition: URI.php:367
const IPV6_COLONS_BETWEEN
Definition: URI.php:73
const HOST_IPV6
Definition: URI.php:96
int $port
Definition: URI.php:105
const PORT
Definition: URI.php:98
digestPort(?int $port=null)
Check port formating.
Definition: URI.php:139
__toString()
Definition: URI.php:336
withPath(?string $path=null)
Get URI with modified path.
Definition: URI.php:283
withSchema(string $schema)
Get URI with modified schema.
Definition: URI.php:206
const HOST_IPV4_EMBEDDED
Definition: URI.php:69
const ALPHA_DIGIT
Definition: URI.php:43
const HOST_IPV6_EMBEDDED_IPV4
Definition: URI.php:94
const PATH
Definition: URI.php:99
const PATH_DELIM
Definition: URI.php:36
const HOST_IPV6_EMBEDDED_IPV4_LONG
Definition: URI.php:86
const BASEURI_SUBDELIMS
subdelims without jsf**k characters +!() and =
Definition: URI.php:59
const IPV6_COUNT_COLONS
Definition: URI.php:72
const HOST_REG_NAME
Definition: URI.php:68
string $path
Definition: URI.php:106
digestHost(string $host)
Check host formating.
Definition: URI.php:131
const FRAGMENT
Definition: URI.php:101
const DOMAIN_LABEL
Definition: URI.php:67
digestSchema(string $schema)
Check schema formating.
Definition: URI.php:123
const ALPHA
Relevant character-groups as defined in RFC 3986 Appendix 1.
Definition: URI.php:41
const PCHAR
Definition: URI.php:64
checkCorrectFormatOrThrow(string $regexp, string $string)
Check wether a string fits a regexp.
Definition: URI.php:190
digestFragment(?string $fragment=null)
Check fragment formating.
Definition: URI.php:177
const PIMP
point|minus|plus to be used in schema.
Definition: URI.php:49
string $host
Definition: URI.php:104
withParameter(string $key, $value)
Get URI with modified parameters.
Definition: URI.php:387
getFragment()
Definition: URI.php:307
const HOST_IPV6_LONG
Definition: URI.php:78
const HOST
Definition: URI.php:97
const IPV6_LEFT_SHORT
Definition: URI.php:74
const HOST_IPV6_EMBEDDED_IPV4_SHORT
Definition: URI.php:88
const HOST_IPV4
Definition: URI.php:70
digestQuery(?string $query=null)
Check query formating.
Definition: URI.php:162
const HOST_IPV6_SHORT
Definition: URI.php:80
getBaseURI()
Get a well-formed URI consisting only of schema, authority and port.
Definition: URI.php:327
const UNRESERVED
Definition: URI.php:61
const HEXDIG
Definition: URI.php:44
const UNRESERVED_NO_DOT
Definition: URI.php:62
string $fragment
Definition: URI.php:108
const QUERY
Definition: URI.php:100
getParameters()
Get all parameters as associative array.
Definition: URI.php:353
const SCHEMA
Definition: URI.php:66
withPort(?int $port=null)
Get URI with modified port.
Definition: URI.php:251
withHost(string $host)
Get URI with modified host.
Definition: URI.php:267
const DIGIT
Definition: URI.php:42
withQuery(?string $query=null)
Get URI with modified query.
Definition: URI.php:299
withAuthority(string $authority)
Get URI with modified authority.
Definition: URI.php:226
const PCTENCODED
Definition: URI.php:45
getAuthority()
Definition: URI.php:214
const SUBDELIMS
valid subdelims according to RFC 3986 Appendix 1: "!" "$" "&" "'" "(" ")" "*" "+" ",...
Definition: URI.php:55
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:31
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
$q
Definition: shib_logout.php:23
$param
Definition: xapitoken.php:44