ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
URI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 namespace ILIAS\Data;
22 
34 class 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 }
digestPath(?string $path=null)
Check path formating.
Definition: URI.php:148
const UNRESERVED
Definition: URI.php:61
const HOST
Definition: URI.php:98
const BASEURI_PCHAR
Definition: URI.php:65
const IPV6_LEFT_SHORT
Definition: URI.php:75
const UNRESERVED_NO_DOT
Definition: URI.php:62
withHost(string $host)
Get URI with modified host.
Definition: URI.php:268
const PIMP
point|minus|plus to be used in schema.
Definition: URI.php:49
string $path
Definition: URI.php:107
const ALPHA
Relevant character-groups as defined in RFC 3986 Appendix 1.
Definition: URI.php:41
withParameters(array $parameters)
Get URI with modified parameters.
Definition: URI.php:378
const HOST_IPV6_SHORT
Definition: URI.php:81
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
const HOST_IPV6_LONG
Definition: URI.php:79
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:31
__toString()
Definition: URI.php:337
const HOST_IPV4_EMBEDDED
Definition: URI.php:70
digestQuery(?string $query=null)
Check query formating.
Definition: URI.php:163
const ALPHA_DIGIT
Definition: URI.php:43
withPath(?string $path=null)
Get URI with modified path.
Definition: URI.php:284
withPort(?int $port=null)
Get URI with modified port.
Definition: URI.php:252
const HOST_REG_NAME
Definition: URI.php:69
const IPV6_COUNT_COLONS
Definition: URI.php:73
withQuery(?string $query=null)
Get URI with modified query.
Definition: URI.php:300
const HOST_IPV6_EMBEDDED_IPV4_LONG
Definition: URI.php:87
const HEXDIG
Definition: URI.php:44
const PCHAR
Definition: URI.php:64
withSchema(string $schema)
Get URI with modified schema.
Definition: URI.php:207
const DOMAIN_LABEL
Definition: URI.php:68
const HOST_IPV6
Definition: URI.php:97
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
const PCTENCODED
Definition: URI.php:45
const SUBDELIMS
valid subdelims according to RFC 3986 Appendix 1: "!" "$" "&" "&#39;" "(" ")" "*" "+" "...
Definition: URI.php:55
string $fragment
Definition: URI.php:109
const QUERY
Definition: URI.php:101
const HOST_IPV6_EMBEDDED_IPV4_SHORT
Definition: URI.php:89
const FRAGMENT
Definition: URI.php:102
const HOST_IPV4
Definition: URI.php:71
getParameters()
Get all parameters as associative array.
Definition: URI.php:354
digestFragment(?string $fragment=null)
Check fragment formating.
Definition: URI.php:178
getParameter(string $param)
Get the value of the given parameter (or null)
Definition: URI.php:368
digestHost(string $host)
Check host formating.
Definition: URI.php:132
__construct(string $uri_string)
Definition: URI.php:111
const IPV6_COLONS_BETWEEN
Definition: URI.php:74
$param
Definition: xapitoken.php:46
const DIGIT
Definition: URI.php:42
digestPort(?int $port=null)
Check port formating.
Definition: URI.php:140
const HOST_IPV6_EMBEDDED_IPV4
Definition: URI.php:95
withFragment(?string $fragment=null)
Get URI with modified fragment.
Definition: URI.php:316
getBaseURI()
Get a well-formed URI consisting only of schema, authority and port.
Definition: URI.php:328
checkCorrectFormatOrThrow(string $regexp, string $string)
Check wether a string fits a regexp.
Definition: URI.php:191
string $schema
Definition: URI.php:104
const PATH
Definition: URI.php:100
getFragment()
Definition: URI.php:308
const PORT
Definition: URI.php:99
digestSchema(string $schema)
Check schema formating.
Definition: URI.php:124
getAuthority()
Definition: URI.php:215
string $host
Definition: URI.php:105
$q
Definition: shib_logout.php:23
const SCHEMA
Definition: URI.php:67
const BASEURI_SUBDELIMS
subdelims without jsf**k characters +!() and =
Definition: URI.php:59
withAuthority(string $authority)
Get URI with modified authority.
Definition: URI.php:227
withParameter(string $key, $value)
Get URI with modified parameters.
Definition: URI.php:388
string $query
Definition: URI.php:108
const PATH_DELIM
Definition: URI.php:36
int $port
Definition: URI.php:106