ILIAS  release_8 Revision v8.24
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 private const BASEURI_PCHAR = self::UNRESERVED . '|' . self::BASEURI_SUBDELIMS . '|' . self::PCTENCODED . '|:|@';
66
67 private const SCHEMA = '#^' . self::ALPHA . '(' . self::ALPHA_DIGIT . '|' . self::PIMP . ')*$#';
68 private const DOMAIN_LABEL = self::ALPHA_DIGIT . '((' . self::UNRESERVED_NO_DOT . '|' . self::PCTENCODED . '|' . self::BASEURI_SUBDELIMS . ')*' . self::ALPHA_DIGIT . ')*';
69 private const HOST_REG_NAME = '^' . self::DOMAIN_LABEL . '(\\.' . self::DOMAIN_LABEL . ')*$';
70 private const HOST_IPV4_EMBEDDED = '(' . self::DIGIT . '{1,3})(\\.' . self::DIGIT . '{1,3}){3}';
71 private const HOST_IPV4 = '^' . self::HOST_IPV4_EMBEDDED . '$';
72
73 private const IPV6_COUNT_COLONS = '(' . self::HEXDIG . '{1,4}:)';
74 private const IPV6_COLONS_BETWEEN = '(' . self::HEXDIG . '{0,4}:' . self::HEXDIG . '{0,4})';
75 private const IPV6_LEFT_SHORT = '(' . self::HEXDIG . '{1,4}|' . self::HEXDIG . '{1,4}(:' . self::HEXDIG . '{1,4})*)?'; // Left of :: are separated with one ":".
76
77 // As specified in RFC4291 Section 2.2 there are three different text forms of an IPv6 address.
78 // HOST_IPV6_LONG corresponds to the form defined in RFC4291 Section 2.2.1.
79 private const HOST_IPV6_LONG = '^\\[' . self::IPV6_COUNT_COLONS . '{7}' . self::HEXDIG . '{1,4}\\]$';
80 // HOST_IPV6_SHORT corresponds to the form defined in RFC4291 Section 2.2.2.
81 private const HOST_IPV6_SHORT = '^\\[(?=' . self::IPV6_COLONS_BETWEEN . '{2,7}\\]$)' . // Ensure whole string contains between 2-7 colons.
82 self::IPV6_LEFT_SHORT . '::' .
83 // Right of "::" are separated with one ":".
84 '(' . self::HEXDIG . '{1,4}|(' . self::HEXDIG . '{1,4}:)*' . self::HEXDIG . '{1,4})?\\]$';
85
86 // HOST_IPV6_EMBEDDED_LONG corresponds to the form defined in RFC4291 Section 2.2.3 but not in a compressed form.
87 private const HOST_IPV6_EMBEDDED_IPV4_LONG = '^\\[' . self::IPV6_COUNT_COLONS. '{5}' . self::HEXDIG . '{1,4}:' . self::HOST_IPV4_EMBEDDED . '\\]$';
88 // HOST_IPV6_EMBEDDED_SHORT corresponds to the form defined in RFC4291 Section 2.2.3 in a compressed form.
89 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.
90 self::IPV6_LEFT_SHORT . '::' .
91 // Right of "::" are separated with one ":".
92 // 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.
93 '(' . self::HEXDIG . '{1,4}|(' . self::HEXDIG . '{1,4}:)*' . self::HEXDIG . '{0,4})?' . self::HOST_IPV4_EMBEDDED . '\\]$';
94
95 private const HOST_IPV6_EMBEDDED_IPV4 = self::HOST_IPV6_EMBEDDED_IPV4_SHORT . '|' . self::HOST_IPV6_EMBEDDED_IPV4_LONG;
96
97 private const HOST_IPV6 = self::HOST_IPV6_LONG . '|' . self::HOST_IPV6_SHORT . '|' . self::HOST_IPV6_EMBEDDED_IPV4;
98 private const HOST = '#' . self::HOST_IPV4 . '|' . self::HOST_REG_NAME . '|' . self::HOST_IPV6 . '#';
99 private const PORT = '#^' . self::DIGIT . '+$#';
100 private const PATH = '#^(?!//)(?!:)(' . self::PCHAR . '|' . self::PATH_DELIM . ')+$#';
101 private const QUERY = '#^(' . self::PCHAR . '|' . self::PATH_DELIM . '|\\?)+$#';
102 private const FRAGMENT = '#^(' . self::PCHAR . '|' . self::PATH_DELIM . '|\\?|\\#)+$#';
103
104 protected string $schema;
105 protected string $host;
106 protected ?int $port;
107 protected ?string $path;
108 protected ?string $query;
109 protected ?string $fragment;
110
111 public function __construct(string $uri_string)
112 {
113 $this->schema = $this->digestSchema(parse_url($uri_string, PHP_URL_SCHEME));
114 $this->host = $this->digestHost(parse_url($uri_string, PHP_URL_HOST));
115 $this->port = $this->digestPort(parse_url($uri_string, PHP_URL_PORT));
116 $this->path = $this->digestPath(parse_url($uri_string, PHP_URL_PATH));
117 $this->query = $this->digestQuery(parse_url($uri_string, PHP_URL_QUERY));
118 $this->fragment = $this->digestFragment(parse_url($uri_string, PHP_URL_FRAGMENT));
119 }
120
124 protected function digestSchema(string $schema): string
125 {
126 return $this->checkCorrectFormatOrThrow(self::SCHEMA, $schema);
127 }
128
132 protected function digestHost(string $host): string
133 {
134 return $this->checkCorrectFormatOrThrow(self::HOST, $host);
135 }
136
140 protected function digestPort(int $port = null): ?int
141 {
142 return $port ?? null;
143 }
144
148 protected function digestPath(string $path = null): ?string
149 {
150 if ($path === null) {
151 return null;
152 }
153 $path = trim($this->checkCorrectFormatOrThrow(self::PATH, $path), self::PATH_DELIM);
154 if ($path === '') {
155 $path = null;
156 }
157 return $path;
158 }
159
163 protected function digestQuery(string $query = null): ?string
164 {
165 if ($query === null) {
166 return null;
167 }
168 return $this->checkCorrectFormatOrThrow(self::QUERY, $query);
169 }
170
174 protected function digestFragment(string $fragment = null): ?string
175 {
176 if ($fragment === null) {
177 return null;
178 }
179 return $this->checkCorrectFormatOrThrow(self::FRAGMENT, $fragment);
180 }
181
182
187 protected function checkCorrectFormatOrThrow(string $regexp, string $string): string
188 {
189 if (preg_match($regexp, $string) === 1) {
190 return $string;
191 }
192 throw new \InvalidArgumentException('ill-formated component "' . $string . '" expected "' . $regexp . '"');
193 }
194
195 public function getSchema(): string
196 {
197 return $this->schema;
198 }
199
203 public function withSchema(string $schema): URI
204 {
205 $shema = $this->digestSchema($schema);
206 $other = clone $this;
207 $other->schema = $schema;
208 return $other;
209 }
210
211 public function getAuthority(): string
212 {
213 $port = $this->getPort();
214 if ($port === null) {
215 return $this->getHost();
216 }
217 return $this->getHost() . ':' . $port;
218 }
219
223 public function withAuthority(string $authority): URI
224 {
225 $parts = explode(':', $authority);
226 if (count($parts) > 2) {
227 throw new \InvalidArgumentException('ill-formated component ' . $authority);
228 }
229 $host = $this->digestHost($parts[0]);
230 $port = null;
231 if (array_key_exists(1, $parts)) {
232 $port = (int) $this->checkCorrectFormatOrThrow(self::PORT, (string) $parts[1]);
233 }
234 $other = clone $this;
235 $other->host = $host;
236 $other->port = $port;
237 return $other;
238 }
239
240 public function getPort(): ?int
241 {
242 return $this->port;
243 }
244
248 public function withPort(int $port = null): URI
249 {
250 $port = $this->digestPort($port);
251 $other = clone $this;
252 $other->port = $port;
253 return $other;
254 }
255
256 public function getHost(): string
257 {
258 return $this->host;
259 }
260
264 public function withHost(string $host): URI
265 {
266 $host = $this->digestHost($host);
267 $other = clone $this;
268 $other->host = $host;
269 return $other;
270 }
271
272 public function getPath(): ?string
273 {
274 return $this->path;
275 }
276
280 public function withPath(string $path = null): URI
281 {
282 $path = $this->digestPath($path);
283 $other = clone $this;
284 $other->path = $path;
285 return $other;
286 }
287
288 public function getQuery(): ?string
289 {
290 return $this->query;
291 }
292
296 public function withQuery(string $query = null): URI
297 {
298 $query = $this->digestQuery($query);
299 $other = clone $this;
300 $other->query = $query;
301 return $other;
302 }
303
304 public function getFragment(): ?string
305 {
306 return $this->fragment;
307 }
308
312 public function withFragment(string $fragment = null): URI
313 {
314 $fragment = $this->digestFragment($fragment);
315 $other = clone $this;
316 $other->fragment = $fragment;
317 return $other;
318 }
319
324 public function getBaseURI(): string
325 {
326 $path = $this->getPath();
327 if ($path === null) {
328 return $this->getSchema() . '://' . $this->getAuthority();
329 }
330 return $this->getSchema() . '://' . $this->getAuthority() . '/' . $path;
331 }
332
333 public function __toString(): string
334 {
335 $uri = $this->getBaseURI();
336 $query = $this->getQuery();
337 if ($query) {
338 $uri .= '?' . $query;
339 }
340 $fragment = $this->getFragment();
341 if ($fragment) {
342 $uri .= '#' . $fragment;
343 }
344 return $uri;
345 }
346
350 public function getParameters(): array
351 {
352 $params = [];
353 $query = $this->getQuery();
354 if (!is_null($query)) {
355 parse_str($query, $params);
356 }
357 return $params;
358 }
359
364 public function getParameter(string $param)
365 {
366 $params = $this->getParameters();
367
368 return $params[$param] ?? null;
369 }
370
374 public function withParameters(array $parameters): URI
375 {
376 return $this->withQuery(
377 http_build_query($parameters)
378 );
379 }
380
384 public function withParameter(string $key, $value): URI
385 {
386 $params = $this->getParameters();
387 $params[$key] = $value;
388 return $this->withParameters($params);
389 }
390}
The scope of this class is split ilias-conform URI's into components.
Definition: URI.php:35
string $schema
Definition: URI.php:104
string $query
Definition: URI.php:108
withParameters(array $parameters)
Get URI with modified parameters.
Definition: URI.php:374
__construct(string $uri_string)
Definition: URI.php:111
getParameter(string $param)
Get the value of the given parameter (or null)
Definition: URI.php:364
const IPV6_COLONS_BETWEEN
Definition: URI.php:74
withPath(string $path=null)
Get URI with modified path.
Definition: URI.php:280
withPort(int $port=null)
Get URI with modified port.
Definition: URI.php:248
const HOST_IPV6
Definition: URI.php:97
digestPath(string $path=null)
Check path formating.
Definition: URI.php:148
int $port
Definition: URI.php:106
const PORT
Definition: URI.php:99
const BASEURI_PCHAR
Definition: URI.php:65
__toString()
Definition: URI.php:333
withSchema(string $schema)
Get URI with modified schema.
Definition: URI.php:203
withQuery(string $query=null)
Get URI with modified query.
Definition: URI.php:296
const HOST_IPV4_EMBEDDED
Definition: URI.php:70
const ALPHA_DIGIT
Definition: URI.php:43
const HOST_IPV6_EMBEDDED_IPV4
Definition: URI.php:95
const PATH
Definition: URI.php:100
const PATH_DELIM
Definition: URI.php:36
const HOST_IPV6_EMBEDDED_IPV4_LONG
Definition: URI.php:87
const BASEURI_SUBDELIMS
subdelims without jsf**k characters +!() and =
Definition: URI.php:59
const IPV6_COUNT_COLONS
Definition: URI.php:73
const HOST_REG_NAME
Definition: URI.php:69
string $path
Definition: URI.php:107
digestPort(int $port=null)
Check port formating.
Definition: URI.php:140
digestHost(string $host)
Check host formating.
Definition: URI.php:132
digestFragment(string $fragment=null)
Check fragment formating.
Definition: URI.php:174
const FRAGMENT
Definition: URI.php:102
const DOMAIN_LABEL
Definition: URI.php:68
digestSchema(string $schema)
Check schema formating.
Definition: URI.php:124
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:187
withFragment(string $fragment=null)
Get URI with modified fragment.
Definition: URI.php:312
const PIMP
point|minus|plus to be used in schema.
Definition: URI.php:49
string $host
Definition: URI.php:105
withParameter(string $key, $value)
Get URI with modified parameters.
Definition: URI.php:384
getFragment()
Definition: URI.php:304
const HOST_IPV6_LONG
Definition: URI.php:79
const HOST
Definition: URI.php:98
digestQuery(string $query=null)
Check query formating.
Definition: URI.php:163
const IPV6_LEFT_SHORT
Definition: URI.php:75
const HOST_IPV6_EMBEDDED_IPV4_SHORT
Definition: URI.php:89
const HOST_IPV4
Definition: URI.php:71
const HOST_IPV6_SHORT
Definition: URI.php:81
getBaseURI()
Get a well-formed URI consisting only of schema, authority and port.
Definition: URI.php:324
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:109
const QUERY
Definition: URI.php:101
getParameters()
Get all parameters as associative array.
Definition: URI.php:350
const SCHEMA
Definition: URI.php:67
withHost(string $host)
Get URI with modified host.
Definition: URI.php:264
const DIGIT
Definition: URI.php:42
withAuthority(string $authority)
Get URI with modified authority.
Definition: URI.php:223
const PCTENCODED
Definition: URI.php:45
getAuthority()
Definition: URI.php:211
const SUBDELIMS
valid subdelims according to RFC 3986 Appendix 1: "!" "$" "&" "'" "(" ")" "*" "+" ",...
Definition: URI.php:55
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:33
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:64
string $key
Consumer key/client ID value.
Definition: System.php:193
const PATH
Definition: proxy_ylocal.php:8
$param
Definition: xapitoken.php:46