ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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 $qparts = explode('&', $query);
169 foreach ($qparts as $q) {
170 $this->checkCorrectFormatOrThrow(self::QUERY, $q);
171 }
172 return $query;
173 }
174
178 protected function digestFragment(?string $fragment = null): ?string
179 {
180 if ($fragment === null) {
181 return null;
182 }
183 return $this->checkCorrectFormatOrThrow(self::FRAGMENT, $fragment);
184 }
185
186
191 protected function checkCorrectFormatOrThrow(string $regexp, string $string): string
192 {
193 if (preg_match($regexp, $string) === 1) {
194 return $string;
195 }
196 throw new \InvalidArgumentException('ill-formated component "' . $string . '" expected "' . $regexp . '"');
197 }
198
199 public function getSchema(): string
200 {
201 return $this->schema;
202 }
203
207 public function withSchema(string $schema): URI
208 {
209 $shema = $this->digestSchema($schema);
210 $other = clone $this;
211 $other->schema = $schema;
212 return $other;
213 }
214
215 public function getAuthority(): string
216 {
217 $port = $this->getPort();
218 if ($port === null) {
219 return $this->getHost();
220 }
221 return $this->getHost() . ':' . $port;
222 }
223
227 public function withAuthority(string $authority): URI
228 {
229 $parts = explode(':', $authority);
230 if (count($parts) > 2) {
231 throw new \InvalidArgumentException('ill-formated component ' . $authority);
232 }
233 $host = $this->digestHost($parts[0]);
234 $port = null;
235 if (array_key_exists(1, $parts)) {
236 $port = (int) $this->checkCorrectFormatOrThrow(self::PORT, (string) $parts[1]);
237 }
238 $other = clone $this;
239 $other->host = $host;
240 $other->port = $port;
241 return $other;
242 }
243
244 public function getPort(): ?int
245 {
246 return $this->port;
247 }
248
252 public function withPort(?int $port = null): URI
253 {
254 $port = $this->digestPort($port);
255 $other = clone $this;
256 $other->port = $port;
257 return $other;
258 }
259
260 public function getHost(): string
261 {
262 return $this->host;
263 }
264
268 public function withHost(string $host): URI
269 {
270 $host = $this->digestHost($host);
271 $other = clone $this;
272 $other->host = $host;
273 return $other;
274 }
275
276 public function getPath(): ?string
277 {
278 return $this->path;
279 }
280
284 public function withPath(?string $path = null): URI
285 {
286 $path = $this->digestPath($path);
287 $other = clone $this;
288 $other->path = $path;
289 return $other;
290 }
291
292 public function getQuery(): ?string
293 {
294 return $this->query;
295 }
296
300 public function withQuery(?string $query = null): URI
301 {
302 $query = $this->digestQuery($query);
303 $other = clone $this;
304 $other->query = $query;
305 return $other;
306 }
307
308 public function getFragment(): ?string
309 {
310 return $this->fragment;
311 }
312
316 public function withFragment(?string $fragment = null): URI
317 {
318 $fragment = $this->digestFragment($fragment);
319 $other = clone $this;
320 $other->fragment = $fragment;
321 return $other;
322 }
323
328 public function getBaseURI(): string
329 {
330 $path = $this->getPath();
331 if ($path === null) {
332 return $this->getSchema() . '://' . $this->getAuthority();
333 }
334 return $this->getSchema() . '://' . $this->getAuthority() . '/' . $path;
335 }
336
337 public function __toString(): string
338 {
339 $uri = $this->getBaseURI();
340 $query = $this->getQuery();
341 if ($query) {
342 $uri .= '?' . $query;
343 }
344 $fragment = $this->getFragment();
345 if ($fragment) {
346 $uri .= '#' . $fragment;
347 }
348 return $uri;
349 }
350
354 public function getParameters(): array
355 {
356 $params = [];
357 $query = $this->getQuery();
358 if (!is_null($query)) {
359 parse_str($query, $params);
360 }
361 return $params;
362 }
363
368 public function getParameter(string $param)
369 {
370 $params = $this->getParameters();
371
372 return $params[$param] ?? null;
373 }
374
378 public function withParameters(array $parameters): URI
379 {
380 return $this->withQuery(
381 http_build_query($parameters)
382 );
383 }
384
388 public function withParameter(string $key, $value): URI
389 {
390 $params = $this->getParameters();
391 $params[$key] = $value;
392 return $this->withParameters($params);
393 }
394}
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
digestPath(?string $path=null)
Check path formating.
Definition: URI.php:148
withParameters(array $parameters)
Get URI with modified parameters.
Definition: URI.php:378
withFragment(?string $fragment=null)
Get URI with modified fragment.
Definition: URI.php:316
__construct(string $uri_string)
Definition: URI.php:111
getParameter(string $param)
Get the value of the given parameter (or null)
Definition: URI.php:368
const IPV6_COLONS_BETWEEN
Definition: URI.php:74
const HOST_IPV6
Definition: URI.php:97
int $port
Definition: URI.php:106
const PORT
Definition: URI.php:99
digestPort(?int $port=null)
Check port formating.
Definition: URI.php:140
const BASEURI_PCHAR
Definition: URI.php:65
__toString()
Definition: URI.php:337
withPath(?string $path=null)
Get URI with modified path.
Definition: URI.php:284
withSchema(string $schema)
Get URI with modified schema.
Definition: URI.php:207
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
digestHost(string $host)
Check host formating.
Definition: URI.php:132
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:191
digestFragment(?string $fragment=null)
Check fragment formating.
Definition: URI.php:178
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:388
getFragment()
Definition: URI.php:308
const HOST_IPV6_LONG
Definition: URI.php:79
const HOST
Definition: URI.php:98
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
digestQuery(?string $query=null)
Check query formating.
Definition: URI.php:163
const HOST_IPV6_SHORT
Definition: URI.php:81
getBaseURI()
Get a well-formed URI consisting only of schema, authority and port.
Definition: URI.php:328
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:354
const SCHEMA
Definition: URI.php:67
withPort(?int $port=null)
Get URI with modified port.
Definition: URI.php:252
withHost(string $host)
Get URI with modified host.
Definition: URI.php:268
const DIGIT
Definition: URI.php:42
withQuery(?string $query=null)
Get URI with modified query.
Definition: URI.php:300
withAuthority(string $authority)
Get URI with modified authority.
Definition: URI.php:227
const PCTENCODED
Definition: URI.php:45
getAuthority()
Definition: URI.php:215
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:46