ILIAS  trunk Revision v5.2.0beta1-34132-g2d4d73d4a0
URI.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 namespace ILIAS\Data;
6 
18 class URI
19 {
20  private const PATH_DELIM = '/';
21 
25  private const ALPHA = '[A-Za-z]';
26  private const DIGIT = '[0-9]';
27  private const ALPHA_DIGIT = '[A-Za-z0-9]';
28  private const HEXDIG = '[0-9A-Fa-f]';
29  private const PCTENCODED = '%' . self::HEXDIG . self::HEXDIG;
33  private const PIMP = '[\\+\\-\\.]';
34 
39  private const SUBDELIMS = '[\\$,;=!&\'\\(\\)\\*\\+]';
43  private const BASEURI_SUBDELIMS = '[\\$,;&\'\\*]';
44 
45  private const UNRESERVED = self::ALPHA_DIGIT . '|[\\-\\._~]';
46  private const UNRESERVED_NO_DOT = self::ALPHA_DIGIT . '|[\\-_~]';
47 
48  private const PCHAR = self::UNRESERVED . '|' . self::SUBDELIMS . '|' . self::PCTENCODED . '|:|@';
49  private const BASEURI_PCHAR = self::UNRESERVED . '|' . self::BASEURI_SUBDELIMS . '|' . self::PCTENCODED . '|:|@';
50 
51  private const SCHEMA = '#^' . self::ALPHA . '(' . self::ALPHA_DIGIT . '|' . self::PIMP . ')*$#';
52  private const DOMAIN_LABEL = self::ALPHA_DIGIT . '((' . self::UNRESERVED_NO_DOT . '|' . self::PCTENCODED . '|' . self::BASEURI_SUBDELIMS . ')*' . self::ALPHA_DIGIT . ')*';
53  private const HOST_REG_NAME = '^' . self::DOMAIN_LABEL . '(\\.' . self::DOMAIN_LABEL . ')*$';
54  private const HOST_IPV4_EMBEDDED = '(' . self::DIGIT . '{1,3})(\\.' . self::DIGIT . '{1,3}){3}';
55  private const HOST_IPV4 = '^' . self::HOST_IPV4_EMBEDDED . '$';
56 
57  private const IPV6_COUNT_COLONS = '(' . self::HEXDIG . '{1,4}:)';
58  private const IPV6_COLONS_BETWEEN = '(' . self::HEXDIG . '{0,4}:' . self::HEXDIG . '{0,4})';
59  private const IPV6_LEFT_SHORT = '(' . self::HEXDIG . '{1,4}|' . self::HEXDIG . '{1,4}(:' . self::HEXDIG . '{1,4})*)?'; // Left of :: are separated with one ":".
60 
61  // As specified in RFC4291 Section 2.2 there are three different text forms of an IPv6 address.
62  // HOST_IPV6_LONG corresponds to the form defined in RFC4291 Section 2.2.1.
63  private const HOST_IPV6_LONG = '^\\[' . self::IPV6_COUNT_COLONS . '{7}' . self::HEXDIG . '{1,4}\\]$';
64  // HOST_IPV6_SHORT corresponds to the form defined in RFC4291 Section 2.2.2.
65  private const HOST_IPV6_SHORT = '^\\[(?=' . self::IPV6_COLONS_BETWEEN . '{2,7}\\]$)' . // Ensure whole string contains between 2-7 colons.
66  self::IPV6_LEFT_SHORT . '::' .
67  // Right of "::" are separated with one ":".
68  '(' . self::HEXDIG . '{1,4}|(' . self::HEXDIG . '{1,4}:)*' . self::HEXDIG . '{1,4})?\\]$';
69 
70  // HOST_IPV6_EMBEDDED_LONG corresponds to the form defined in RFC4291 Section 2.2.3 but not in a compressed form.
71  private const HOST_IPV6_EMBEDDED_IPV4_LONG = '^\\[' . self::IPV6_COUNT_COLONS . '{5}' . self::HEXDIG . '{1,4}:' . self::HOST_IPV4_EMBEDDED . '\\]$';
72  // HOST_IPV6_EMBEDDED_SHORT corresponds to the form defined in RFC4291 Section 2.2.3 in a compressed form.
73  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.
74  self::IPV6_LEFT_SHORT . '::' .
75  // Right of "::" are separated with one ":".
76  // 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.
77  '(' . self::HEXDIG . '{1,4}|(' . self::HEXDIG . '{1,4}:)*' . self::HEXDIG . '{0,4})?' . self::HOST_IPV4_EMBEDDED . '\\]$';
78 
79  private const HOST_IPV6_EMBEDDED_IPV4 = self::HOST_IPV6_EMBEDDED_IPV4_SHORT . '|' . self::HOST_IPV6_EMBEDDED_IPV4_LONG;
80 
81  private const HOST_IPV6 = self::HOST_IPV6_LONG . '|' . self::HOST_IPV6_SHORT . '|' . self::HOST_IPV6_EMBEDDED_IPV4;
82  private const HOST = '#' . self::HOST_IPV4 . '|' . self::HOST_REG_NAME . '|' . self::HOST_IPV6 . '#';
83  private const PORT = '#^' . self::DIGIT . '+$#';
84  private const PATH = '#^(?!//)(?!:)(' . self::PCHAR . '|' . self::PATH_DELIM . ')+$#';
85  private const QUERY = '#^(' . self::PCHAR . '|' . self::PATH_DELIM . '|\\?)+$#';
86  private const FRAGMENT = '#^(' . self::PCHAR . '|' . self::PATH_DELIM . '|\\?|\\#)+$#';
87 
88  protected string $schema;
89  protected string $host;
90  protected ?int $port;
91  protected ?string $path;
92  protected ?string $query;
93  protected ?string $fragment;
94 
95  public function __construct(string $uri_string)
96  {
97  $this->schema = $this->digestSchema(parse_url($uri_string, PHP_URL_SCHEME));
98  $this->host = $this->digestHost(parse_url($uri_string, PHP_URL_HOST));
99  $this->port = $this->digestPort(parse_url($uri_string, PHP_URL_PORT));
100  $this->path = $this->digestPath(parse_url($uri_string, PHP_URL_PATH));
101  $this->query = $this->digestQuery(parse_url($uri_string, PHP_URL_QUERY));
102  $this->fragment = $this->digestFragment(parse_url($uri_string, PHP_URL_FRAGMENT));
103  }
104 
108  protected function digestSchema(string $schema): string
109  {
110  return $this->checkCorrectFormatOrThrow(self::SCHEMA, $schema);
111  }
112 
116  protected function digestHost(string $host): string
117  {
118  return $this->checkCorrectFormatOrThrow(self::HOST, $host);
119  }
120 
124  protected function digestPort(int $port = null): ?int
125  {
126  return $port ?? null;
127  }
128 
132  protected function digestPath(string $path = null): ?string
133  {
134  if ($path === null) {
135  return null;
136  }
137  $path = trim($this->checkCorrectFormatOrThrow(self::PATH, $path), self::PATH_DELIM);
138  if ($path === '') {
139  $path = null;
140  }
141  return $path;
142  }
143 
147  protected function digestQuery(string $query = null): ?string
148  {
149  if ($query === null) {
150  return null;
151  }
152  return $this->checkCorrectFormatOrThrow(self::QUERY, $query);
153  }
154 
158  protected function digestFragment(string $fragment = null): ?string
159  {
160  if ($fragment === null) {
161  return null;
162  }
163  return $this->checkCorrectFormatOrThrow(self::FRAGMENT, $fragment);
164  }
165 
166 
171  protected function checkCorrectFormatOrThrow(string $regexp, string $string): string
172  {
173  if (preg_match($regexp, $string) === 1) {
174  return $string;
175  }
176  throw new \InvalidArgumentException('ill-formated component "' . $string . '" expected "' . $regexp . '"');
177  }
178 
179  public function getSchema(): string
180  {
181  return $this->schema;
182  }
183 
187  public function withSchema(string $schema): URI
188  {
189  $shema = $this->digestSchema($schema);
190  $other = clone $this;
191  $other->schema = $schema;
192  return $other;
193  }
194 
195  public function getAuthority(): string
196  {
197  $port = $this->getPort();
198  if ($port === null) {
199  return $this->getHost();
200  }
201  return $this->getHost() . ':' . $port;
202  }
203 
207  public function withAuthority(string $authority): URI
208  {
209  $parts = explode(':', $authority);
210  if (count($parts) > 2) {
211  throw new \InvalidArgumentException('ill-formated component ' . $authority);
212  }
213  $host = $this->digestHost($parts[0]);
214  $port = null;
215  if (array_key_exists(1, $parts)) {
216  $port = (int) $this->checkCorrectFormatOrThrow(self::PORT, (string) $parts[1]);
217  }
218  $other = clone $this;
219  $other->host = $host;
220  $other->port = $port;
221  return $other;
222  }
223 
224  public function getPort(): ?int
225  {
226  return $this->port;
227  }
228 
232  public function withPort(int $port = null): URI
233  {
234  $port = $this->digestPort($port);
235  $other = clone $this;
236  $other->port = $port;
237  return $other;
238  }
239 
240  public function getHost(): string
241  {
242  return $this->host;
243  }
244 
248  public function withHost(string $host): URI
249  {
250  $host = $this->digestHost($host);
251  $other = clone $this;
252  $other->host = $host;
253  return $other;
254  }
255 
256  public function getPath(): ?string
257  {
258  return $this->path;
259  }
260 
264  public function withPath(string $path = null): URI
265  {
266  $path = $this->digestPath($path);
267  $other = clone $this;
268  $other->path = $path;
269  return $other;
270  }
271 
272  public function getQuery(): ?string
273  {
274  return $this->query;
275  }
276 
280  public function withQuery(string $query = null): URI
281  {
282  $query = $this->digestQuery($query);
283  $other = clone $this;
284  $other->query = $query;
285  return $other;
286  }
287 
288  public function getFragment(): ?string
289  {
290  return $this->fragment;
291  }
292 
296  public function withFragment(string $fragment = null): URI
297  {
298  $fragment = $this->digestFragment($fragment);
299  $other = clone $this;
300  $other->fragment = $fragment;
301  return $other;
302  }
303 
308  public function getBaseURI(): string
309  {
310  $path = $this->getPath();
311  if ($path === null) {
312  return $this->getSchema() . '://' . $this->getAuthority();
313  }
314  return $this->getSchema() . '://' . $this->getAuthority() . '/' . $path;
315  }
316 
317  public function __toString(): string
318  {
319  $uri = $this->getBaseURI();
320  $query = $this->getQuery();
321  if ($query) {
322  $uri .= '?' . $query;
323  }
324  $fragment = $this->getFragment();
325  if ($fragment) {
326  $uri .= '#' . $fragment;
327  }
328  return $uri;
329  }
330 
334  public function getParameters(): array
335  {
336  $params = [];
337  $query = $this->getQuery();
338  if (!is_null($query)) {
339  parse_str($query, $params);
340  }
341  return $params;
342  }
343 
348  public function getParameter(string $param)
349  {
350  $params = $this->getParameters();
351 
352  return $params[$param] ?? null;
353  }
354 
358  public function withParameters(array $parameters): URI
359  {
360  return $this->withQuery(
361  http_build_query($parameters)
362  );
363  }
364 
368  public function withParameter(string $key, $value): URI
369  {
370  $params = $this->getParameters();
371  $params[$key] = $value;
372  return $this->withParameters($params);
373  }
374 }
const UNRESERVED
Definition: URI.php:45
const HOST
Definition: URI.php:82
const BASEURI_PCHAR
Definition: URI.php:49
const IPV6_LEFT_SHORT
Definition: URI.php:59
const UNRESERVED_NO_DOT
Definition: URI.php:46
withFragment(string $fragment=null)
Get URI with modified fragment.
Definition: URI.php:296
withHost(string $host)
Get URI with modified host.
Definition: URI.php:248
const PIMP
point|minus|plus to be used in schema.
Definition: URI.php:33
string $path
Definition: URI.php:91
const ALPHA
Relevant character-groups as defined in RFC 3986 Appendix 1.
Definition: URI.php:25
withParameters(array $parameters)
Get URI with modified parameters.
Definition: URI.php:358
const HOST_IPV6_SHORT
Definition: URI.php:65
withPath(string $path=null)
Get URI with modified path.
Definition: URI.php:264
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:62
const HOST_IPV6_LONG
Definition: URI.php:63
__toString()
Definition: URI.php:317
const HOST_IPV4_EMBEDDED
Definition: URI.php:54
const ALPHA_DIGIT
Definition: URI.php:27
const HOST_REG_NAME
Definition: URI.php:53
withQuery(string $query=null)
Get URI with modified query.
Definition: URI.php:280
const IPV6_COUNT_COLONS
Definition: URI.php:57
digestQuery(string $query=null)
Check query formating.
Definition: URI.php:147
const HOST_IPV6_EMBEDDED_IPV4_LONG
Definition: URI.php:71
const HEXDIG
Definition: URI.php:28
const PCHAR
Definition: URI.php:48
withSchema(string $schema)
Get URI with modified schema.
Definition: URI.php:187
const DOMAIN_LABEL
Definition: URI.php:52
const HOST_IPV6
Definition: URI.php:81
const PCTENCODED
Definition: URI.php:29
const SUBDELIMS
valid subdelims according to RFC 3986 Appendix 1: "!" "$" "&" "&#39;" "(" ")" "*" "+" "...
Definition: URI.php:39
string $fragment
Definition: URI.php:93
const QUERY
Definition: URI.php:85
const HOST_IPV6_EMBEDDED_IPV4_SHORT
Definition: URI.php:73
const FRAGMENT
Definition: URI.php:86
const HOST_IPV4
Definition: URI.php:55
getParameters()
Get all parameters as associative array.
Definition: URI.php:334
getParameter(string $param)
Get the value of the given parameter (or null)
Definition: URI.php:348
digestHost(string $host)
Check host formating.
Definition: URI.php:116
__construct(string $uri_string)
Definition: URI.php:95
const IPV6_COLONS_BETWEEN
Definition: URI.php:58
$param
Definition: xapitoken.php:46
const DIGIT
Definition: URI.php:26
string $key
Consumer key/client ID value.
Definition: System.php:193
The scope of this class is split ilias-conform URI&#39;s into components.
Definition: URI.php:18
const HOST_IPV6_EMBEDDED_IPV4
Definition: URI.php:79
getBaseURI()
Get a well-formed URI consisting only of schema, authority and port.
Definition: URI.php:308
checkCorrectFormatOrThrow(string $regexp, string $string)
Check wether a string fits a regexp.
Definition: URI.php:171
if(strtoupper($DIC->http() ->request() ->getMethod()) !=="GET") $params
Definition: ltiregstart.php:34
string $schema
Definition: URI.php:88
const PATH
Definition: URI.php:84
digestPath(string $path=null)
Check path formating.
Definition: URI.php:132
getFragment()
Definition: URI.php:288
digestPort(int $port=null)
Check port formating.
Definition: URI.php:124
const PORT
Definition: URI.php:83
digestSchema(string $schema)
Check schema formating.
Definition: URI.php:108
withPort(int $port=null)
Get URI with modified port.
Definition: URI.php:232
getAuthority()
Definition: URI.php:195
string $host
Definition: URI.php:89
digestFragment(string $fragment=null)
Check fragment formating.
Definition: URI.php:158
const SCHEMA
Definition: URI.php:51
const BASEURI_SUBDELIMS
subdelims without jsf**k characters +!() and =
Definition: URI.php:43
withAuthority(string $authority)
Get URI with modified authority.
Definition: URI.php:207
withParameter(string $key, $value)
Get URI with modified parameters.
Definition: URI.php:368
string $query
Definition: URI.php:92
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const PATH_DELIM
Definition: URI.php:20
int $port
Definition: URI.php:90