ILIAS  trunk Revision v11.0_alpha-1731-gff9cd7e2bd3
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
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  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 }
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:264
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:374
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:333
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:280
withPort(?int $port=null)
Get URI with modified port.
Definition: URI.php:248
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:296
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:203
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:350
digestFragment(?string $fragment=null)
Check fragment formating.
Definition: URI.php:174
getParameter(string $param)
Get the value of the given parameter (or null)
Definition: URI.php:364
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:312
getBaseURI()
Get a well-formed URI consisting only of schema, authority and port.
Definition: URI.php:324
checkCorrectFormatOrThrow(string $regexp, string $string)
Check wether a string fits a regexp.
Definition: URI.php:187
string $schema
Definition: URI.php:104
const PATH
Definition: URI.php:100
getFragment()
Definition: URI.php:304
const PORT
Definition: URI.php:99
digestSchema(string $schema)
Check schema formating.
Definition: URI.php:124
getAuthority()
Definition: URI.php:211
string $host
Definition: URI.php:105
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:223
withParameter(string $key, $value)
Get URI with modified parameters.
Definition: URI.php:384
string $query
Definition: URI.php:108
const PATH_DELIM
Definition: URI.php:36
int $port
Definition: URI.php:106