Coverage Report

Created: 2026-04-01 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/uri/uri_parser_whatwg.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | https://www.php.net/license/3_01.txt                                 |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Authors: Máté Kocsis <kocsismate@php.net>                            |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#include "php.h"
18
#include "uri_parser_whatwg.h"
19
#include "php_uri_common.h"
20
#include "Zend/zend_enum.h"
21
#include "Zend/zend_smart_str.h"
22
#include "Zend/zend_exceptions.h"
23
#ifdef HAVE_ARPA_INET_H
24
#include <arpa/inet.h>
25
#endif
26
27
ZEND_TLS lexbor_mraw_t lexbor_mraw = {0};
28
ZEND_TLS lxb_url_parser_t lexbor_parser = {0};
29
ZEND_TLS lxb_unicode_idna_t lexbor_idna = {0};
30
31
static const size_t lexbor_mraw_byte_size = 8192;
32
33
static zend_always_inline void zval_string_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str)
34
0
{
35
0
  if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) {
36
0
    lexbor_str->data = (lxb_char_t *) Z_STRVAL_P(value);
37
0
    lexbor_str->length = Z_STRLEN_P(value);
38
0
  } else {
39
0
    ZEND_ASSERT(Z_ISNULL_P(value) || (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) == 0));
40
0
    lexbor_str->data = (lxb_char_t *) "";
41
0
    lexbor_str->length = 0;
42
0
  }
43
0
}
44
45
static zend_always_inline void zval_long_or_null_to_lexbor_str(zval *value, lexbor_str_t *lexbor_str)
46
0
{
47
0
  if (Z_TYPE_P(value) == IS_LONG) {
48
0
    ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value)));
49
0
    lexbor_str_init_append(lexbor_str, lexbor_parser.mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value));
50
0
    zval_ptr_dtor_str(value);
51
0
  } else {
52
0
    ZEND_ASSERT(Z_ISNULL_P(value));
53
0
    lexbor_str->data = (lxb_char_t *) "";
54
0
    lexbor_str->length = 0;
55
0
  }
56
0
}
57
58
/**
59
 * Creates a Uri\WhatWg\UrlValidationError class by mapping error codes listed in
60
 * https://url.spec.whatwg.org/#writing to a Uri\WhatWg\UrlValidationErrorType enum.
61
 * The result is passed by reference to the errors parameter.
62
 */
63
static const char *fill_errors(zval *errors)
64
0
{
65
0
  size_t log_len;
66
0
  if (lexbor_parser.log == NULL || (log_len = lexbor_plog_length(lexbor_parser.log)) == 0) {
67
0
    ZVAL_EMPTY_ARRAY(errors);
68
0
    return NULL;
69
0
  }
70
71
0
  array_init_size(errors, log_len);
72
0
  const char *result = NULL;
73
74
0
  lexbor_plog_entry_t *lxb_error;
75
0
  while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) {
76
0
    zval error;
77
0
    object_init_ex(&error, php_uri_ce_whatwg_url_validation_error);
78
0
    zend_update_property_string(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data);
79
80
0
    const char *error_str;
81
0
    zval failure;
82
0
    switch (lxb_error->id) {
83
0
      case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII:
84
0
        error_str = "DomainToAscii";
85
0
        ZVAL_TRUE(&failure);
86
0
        break;
87
0
      case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE:
88
0
        error_str = "DomainToUnicode";
89
0
        ZVAL_FALSE(&failure);
90
0
        break;
91
0
      case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT:
92
0
        error_str = "DomainInvalidCodePoint";
93
0
        ZVAL_TRUE(&failure);
94
0
        break;
95
0
      case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT:
96
0
        error_str = "HostInvalidCodePoint";
97
0
        ZVAL_TRUE(&failure);
98
0
        break;
99
0
      case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART:
100
0
        error_str = "Ipv4EmptyPart";
101
0
        ZVAL_FALSE(&failure);
102
0
        break;
103
0
      case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS:
104
0
        error_str = "Ipv4TooManyParts";
105
0
        ZVAL_TRUE(&failure);
106
0
        break;
107
0
      case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART:
108
0
        error_str = "Ipv4NonNumericPart";
109
0
        ZVAL_TRUE(&failure);
110
0
        break;
111
0
      case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART:
112
0
        error_str = "Ipv4NonDecimalPart";
113
0
        ZVAL_FALSE(&failure);
114
0
        break;
115
0
      case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART:
116
0
        error_str = "Ipv4OutOfRangePart";
117
0
        ZVAL_TRUE(&failure);
118
0
        break;
119
0
      case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED:
120
0
        error_str = "Ipv6Unclosed";
121
0
        ZVAL_TRUE(&failure);
122
0
        break;
123
0
      case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION:
124
0
        error_str = "Ipv6InvalidCompression";
125
0
        ZVAL_TRUE(&failure);
126
0
        break;
127
0
      case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES:
128
0
        error_str = "Ipv6TooManyPieces";
129
0
        ZVAL_TRUE(&failure);
130
0
        break;
131
0
      case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION:
132
0
        error_str = "Ipv6MultipleCompression";
133
0
        ZVAL_TRUE(&failure);
134
0
        break;
135
0
      case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT:
136
0
        error_str = "Ipv6InvalidCodePoint";
137
0
        ZVAL_TRUE(&failure);
138
0
        break;
139
0
      case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES:
140
0
        error_str = "Ipv6TooFewPieces";
141
0
        ZVAL_TRUE(&failure);
142
0
        break;
143
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES:
144
0
        error_str = "Ipv4InIpv6TooManyPieces";
145
0
        ZVAL_TRUE(&failure);
146
0
        break;
147
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT:
148
0
        error_str = "Ipv4InIpv6InvalidCodePoint";
149
0
        ZVAL_TRUE(&failure);
150
0
        break;
151
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART:
152
0
        error_str = "Ipv4InIpv6OutOfRangePart";
153
0
        ZVAL_TRUE(&failure);
154
0
        break;
155
0
      case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS:
156
0
        error_str = "Ipv4InIpv6TooFewParts";
157
0
        ZVAL_TRUE(&failure);
158
0
        break;
159
0
      case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT:
160
0
        error_str = "InvalidUrlUnit";
161
0
        ZVAL_FALSE(&failure);
162
0
        break;
163
0
      case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS:
164
0
        error_str = "SpecialSchemeMissingFollowingSolidus";
165
0
        ZVAL_FALSE(&failure);
166
0
        break;
167
0
      case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL:
168
0
        error_str = "MissingSchemeNonRelativeUrl";
169
0
        ZVAL_TRUE(&failure);
170
0
        break;
171
0
      case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS:
172
0
        error_str = "InvalidReverseSoldius";
173
0
        ZVAL_FALSE(&failure);
174
0
        break;
175
0
      case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS:
176
0
        error_str = "InvalidCredentials";
177
0
        ZVAL_FALSE(&failure);
178
0
        break;
179
0
      case LXB_URL_ERROR_TYPE_HOST_MISSING:
180
0
        error_str = "HostMissing";
181
0
        ZVAL_TRUE(&failure);
182
0
        break;
183
0
      case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE:
184
0
        error_str = "PortOutOfRange";
185
0
        ZVAL_TRUE(&failure);
186
0
        break;
187
0
      case LXB_URL_ERROR_TYPE_PORT_INVALID:
188
0
        error_str = "PortInvalid";
189
0
        ZVAL_TRUE(&failure);
190
0
        break;
191
0
      case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER:
192
0
        error_str = "FileInvalidWindowsDriveLetter";
193
0
        ZVAL_FALSE(&failure);
194
0
        break;
195
0
      case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST:
196
0
        error_str = "FileInvalidWindowsDriveLetterHost";
197
0
        ZVAL_FALSE(&failure);
198
0
        break;
199
0
      EMPTY_SWITCH_DEFAULT_CASE()
200
0
    }
201
202
0
    zval error_type;
203
0
    ZVAL_OBJ(&error_type, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_validation_error_type, error_str));
204
0
    zend_update_property_ex(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type);
205
206
0
    zend_update_property(php_uri_ce_whatwg_url_validation_error, Z_OBJ(error), ZEND_STRL("failure"), &failure);
207
208
0
    if (Z_TYPE(failure) == IS_TRUE) {
209
0
      result = error_str;
210
0
    }
211
212
0
    add_next_index_zval(errors, &error);
213
0
  }
214
215
0
  return result;
216
0
}
217
218
static void throw_invalid_url_exception_during_write(zval *errors, const char *component)
219
0
{
220
0
  zval err;
221
0
  const char *reason = fill_errors(&err);
222
0
  zend_object *exception = zend_throw_exception_ex(
223
0
    php_uri_ce_whatwg_invalid_url_exception,
224
0
    0,
225
0
    "The specified %s is malformed%s%s%s",
226
0
    component,
227
0
    reason ? " (" : "",
228
0
    reason ? reason : "",
229
0
    reason ? ")" : ""
230
0
  );
231
0
  zend_update_property(exception->ce, exception, ZEND_STRL("errors"), &err);
232
0
  if (errors) {
233
0
    zval_ptr_dtor(errors);
234
0
    ZVAL_COPY_VALUE(errors, &err);
235
0
  } else {
236
0
    zval_ptr_dtor(&err);
237
0
  }
238
0
}
239
240
static lxb_status_t serialize_to_smart_str_callback(const lxb_char_t *data, size_t length, void *ctx)
241
0
{
242
0
  smart_str *uri_str = ctx;
243
244
0
  if (data != NULL && length > 0) {
245
0
    smart_str_appendl(uri_str, (const char *) data, length);
246
0
  }
247
248
0
  return LXB_STATUS_OK;
249
0
}
250
251
static zend_result php_uri_parser_whatwg_scheme_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
252
0
{
253
0
  const lxb_url_t *lexbor_uri = uri;
254
255
0
  ZEND_ASSERT(lexbor_uri->scheme.type != LXB_URL_SCHEMEL_TYPE__UNDEF);
256
257
0
  ZVAL_STRINGL(retval, (const char *) lexbor_uri->scheme.name.data, lexbor_uri->scheme.name.length);
258
259
0
  return SUCCESS;
260
0
}
261
262
static zend_result php_uri_parser_whatwg_scheme_write(void *uri, zval *value, zval *errors)
263
0
{
264
0
  lxb_url_t *lexbor_uri = uri;
265
0
  lexbor_str_t str = {0};
266
267
0
  zval_string_or_null_to_lexbor_str(value, &str);
268
269
0
  if (lxb_url_api_protocol_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
270
0
    throw_invalid_url_exception_during_write(errors, "scheme");
271
272
0
    return FAILURE;
273
0
  }
274
275
0
  return SUCCESS;
276
0
}
277
278
/* 4.2. URL miscellaneous: A URL includes credentials if its username or password is not the empty string. */
279
static bool includes_credentials(const lxb_url_t *lexbor_uri)
280
0
{
281
0
  return lexbor_uri->username.length > 0 || lexbor_uri->password.length > 0;
282
0
}
283
284
static zend_result php_uri_parser_whatwg_username_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
285
0
{
286
0
  const lxb_url_t *lexbor_uri = uri;
287
288
0
  if (includes_credentials(lexbor_uri)) {
289
0
    ZVAL_STRINGL_FAST(retval, (const char *) lexbor_uri->username.data, lexbor_uri->username.length);
290
0
  } else {
291
0
    ZVAL_NULL(retval);
292
0
  }
293
294
0
  return SUCCESS;
295
0
}
296
297
static zend_result php_uri_parser_whatwg_username_write(void *uri, zval *value, zval *errors)
298
0
{
299
0
  lxb_url_t *lexbor_uri = uri;
300
0
  lexbor_str_t str = {0};
301
302
0
  zval_string_or_null_to_lexbor_str(value, &str);
303
304
0
  if (lxb_url_api_username_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) {
305
0
    throw_invalid_url_exception_during_write(errors, "username");
306
307
0
    return FAILURE;
308
0
  }
309
310
0
  return SUCCESS;
311
0
}
312
313
static zend_result php_uri_parser_whatwg_password_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
314
0
{
315
0
  const lxb_url_t *lexbor_uri = uri;
316
317
0
  if (includes_credentials(lexbor_uri)) {
318
0
    ZVAL_STRINGL_FAST(retval, (const char *) lexbor_uri->password.data, lexbor_uri->password.length);
319
0
  } else {
320
0
    ZVAL_NULL(retval);
321
0
  }
322
323
0
  return SUCCESS;
324
0
}
325
326
static zend_result php_uri_parser_whatwg_password_write(void *uri, zval *value, zval *errors)
327
0
{
328
0
  lxb_url_t *lexbor_uri = uri;
329
0
  lexbor_str_t str = {0};
330
331
0
  zval_string_or_null_to_lexbor_str(value, &str);
332
333
0
  if (lxb_url_api_password_set(lexbor_uri, str.data, str.length) != LXB_STATUS_OK) {
334
0
    throw_invalid_url_exception_during_write(errors, "password");
335
336
0
    return FAILURE;
337
0
  }
338
339
0
  return SUCCESS;
340
0
}
341
342
static zend_result php_uri_parser_whatwg_host_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
343
0
{
344
0
  const lxb_url_t *lexbor_uri = uri;
345
346
0
  if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV4) {
347
0
    smart_str host_str = {0};
348
349
0
    lxb_url_serialize_host_ipv4(lexbor_uri->host.u.ipv4, serialize_to_smart_str_callback, &host_str);
350
351
0
    ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
352
0
  } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_IPV6) {
353
0
    smart_str host_str = {0};
354
355
0
    smart_str_appendc(&host_str, '[');
356
0
    lxb_url_serialize_host_ipv6(lexbor_uri->host.u.ipv6, serialize_to_smart_str_callback, &host_str);
357
0
    smart_str_appendc(&host_str, ']');
358
359
0
    ZVAL_NEW_STR(retval, smart_str_extract(&host_str));
360
0
  } else if (lexbor_uri->host.type == LXB_URL_HOST_TYPE_EMPTY) {
361
0
    ZVAL_EMPTY_STRING(retval);
362
0
  } else if (lexbor_uri->host.type != LXB_URL_HOST_TYPE__UNDEF) {
363
0
    switch (read_mode) {
364
0
      case PHP_URI_COMPONENT_READ_MODE_NORMALIZED_UNICODE: {
365
0
        smart_str host_str = {0};
366
0
        lxb_url_serialize_host_unicode(&lexbor_idna, &lexbor_uri->host, serialize_to_smart_str_callback, &host_str);
367
0
        lxb_unicode_idna_clean(&lexbor_idna);
368
369
0
        ZVAL_STR(retval, smart_str_extract(&host_str));
370
0
        break;
371
0
      }
372
0
      case PHP_URI_COMPONENT_READ_MODE_NORMALIZED_ASCII:
373
0
        ZEND_FALLTHROUGH;
374
0
      case PHP_URI_COMPONENT_READ_MODE_RAW:
375
0
        ZVAL_STRINGL(retval, (const char *) lexbor_uri->host.u.domain.data, lexbor_uri->host.u.domain.length);
376
0
        break;
377
0
      EMPTY_SWITCH_DEFAULT_CASE()
378
0
    }
379
0
  } else {
380
0
    ZVAL_NULL(retval);
381
0
  }
382
383
0
  return SUCCESS;
384
0
}
385
386
static zend_result php_uri_parser_whatwg_host_write(void *uri, zval *value, zval *errors)
387
0
{
388
0
  lxb_url_t *lexbor_uri = uri;
389
0
  lexbor_str_t str = {0};
390
391
0
  zval_string_or_null_to_lexbor_str(value, &str);
392
393
0
  if (lxb_url_api_hostname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
394
0
    throw_invalid_url_exception_during_write(errors, "host");
395
396
0
    return FAILURE;
397
0
  }
398
399
0
  return SUCCESS;
400
0
}
401
402
static zend_result php_uri_parser_whatwg_port_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
403
0
{
404
0
  const lxb_url_t *lexbor_uri = uri;
405
406
0
  if (lexbor_uri->has_port) {
407
0
    ZVAL_LONG(retval, lexbor_uri->port);
408
0
  } else {
409
0
    ZVAL_NULL(retval);
410
0
  }
411
412
0
  return SUCCESS;
413
0
}
414
415
static zend_result php_uri_parser_whatwg_port_write(void *uri, zval *value, zval *errors)
416
0
{
417
0
  lxb_url_t *lexbor_uri = uri;
418
0
  lexbor_str_t str = {0};
419
420
0
  zval_long_or_null_to_lexbor_str(value, &str);
421
422
0
  if (lxb_url_api_port_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
423
0
    throw_invalid_url_exception_during_write(errors, "port");
424
425
0
    return FAILURE;
426
0
  }
427
428
0
  return SUCCESS;
429
0
}
430
431
static zend_result php_uri_parser_whatwg_path_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
432
0
{
433
0
  const lxb_url_t *lexbor_uri = uri;
434
435
0
  if (lexbor_uri->path.str.length > 0) {
436
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->path.str.data, lexbor_uri->path.str.length);
437
0
  } else {
438
0
    ZVAL_EMPTY_STRING(retval);
439
0
  }
440
441
0
  return SUCCESS;
442
0
}
443
444
static zend_result php_uri_parser_whatwg_path_write(void *uri, zval *value, zval *errors)
445
0
{
446
0
  lxb_url_t *lexbor_uri = uri;
447
0
  lexbor_str_t str = {0};
448
449
0
  zval_string_or_null_to_lexbor_str(value, &str);
450
451
0
  if (lxb_url_api_pathname_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
452
0
    throw_invalid_url_exception_during_write(errors, "path");
453
454
0
    return FAILURE;
455
0
  }
456
457
0
  return SUCCESS;
458
0
}
459
460
static zend_result php_uri_parser_whatwg_query_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
461
0
{
462
0
  const lxb_url_t *lexbor_uri = uri;
463
464
0
  if (lexbor_uri->query.data != NULL) {
465
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->query.data, lexbor_uri->query.length);
466
0
  } else {
467
0
    ZVAL_NULL(retval);
468
0
  }
469
470
0
  return SUCCESS;
471
0
}
472
473
static zend_result php_uri_parser_whatwg_query_write(void *uri, zval *value, zval *errors)
474
0
{
475
0
  lxb_url_t *lexbor_uri = uri;
476
0
  lexbor_str_t str = {0};
477
478
0
  zval_string_or_null_to_lexbor_str(value, &str);
479
480
0
  if (lxb_url_api_search_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
481
0
    throw_invalid_url_exception_during_write(errors, "query string");
482
483
0
    return FAILURE;
484
0
  }
485
486
0
  return SUCCESS;
487
0
}
488
489
static zend_result php_uri_parser_whatwg_fragment_read(void *uri, php_uri_component_read_mode read_mode, zval *retval)
490
0
{
491
0
  const lxb_url_t *lexbor_uri = uri;
492
493
0
  if (lexbor_uri->fragment.data != NULL) {
494
0
    ZVAL_STRINGL(retval, (const char *) lexbor_uri->fragment.data, lexbor_uri->fragment.length);
495
0
  } else {
496
0
    ZVAL_NULL(retval);
497
0
  }
498
499
0
  return SUCCESS;
500
0
}
501
502
static zend_result php_uri_parser_whatwg_fragment_write(void *uri, zval *value, zval *errors)
503
0
{
504
0
  lxb_url_t *lexbor_uri = uri;
505
0
  lexbor_str_t str = {0};
506
507
0
  zval_string_or_null_to_lexbor_str(value, &str);
508
509
0
  if (lxb_url_api_hash_set(lexbor_uri, &lexbor_parser, str.data, str.length) != LXB_STATUS_OK) {
510
0
    throw_invalid_url_exception_during_write(errors, "fragment");
511
512
0
    return FAILURE;
513
0
  }
514
515
0
  return SUCCESS;
516
0
}
517
518
PHP_RINIT_FUNCTION(uri_parser_whatwg)
519
224k
{
520
224k
  lxb_status_t status;
521
  
522
224k
  status = lexbor_mraw_init(&lexbor_mraw, lexbor_mraw_byte_size);
523
224k
  if (status != LXB_STATUS_OK) {
524
0
    goto fail;
525
0
  }
526
527
224k
  status = lxb_url_parser_init(&lexbor_parser, &lexbor_mraw);
528
224k
  if (status != LXB_STATUS_OK) {
529
0
    goto fail;
530
0
  }
531
532
224k
  status = lxb_unicode_idna_init(&lexbor_idna);
533
224k
  if (status != LXB_STATUS_OK) {
534
0
    goto fail;
535
0
  }
536
537
224k
  return SUCCESS;
538
539
0
 fail:
540
541
  /* Unconditionally calling the _destroy() functions is
542
   * safe on a zeroed structure. */
543
0
  lxb_unicode_idna_destroy(&lexbor_idna, false);
544
0
  memset(&lexbor_idna, 0, sizeof(lexbor_idna));
545
0
  lxb_url_parser_destroy(&lexbor_parser, false);
546
0
  memset(&lexbor_parser, 0, sizeof(lexbor_parser));
547
0
  lexbor_mraw_destroy(&lexbor_mraw, false);
548
0
  memset(&lexbor_mraw, 0, sizeof(lexbor_mraw));
549
550
0
  return FAILURE;
551
224k
}
552
553
ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri_parser_whatwg)
554
224k
{
555
224k
  lxb_unicode_idna_destroy(&lexbor_idna, false);
556
224k
  memset(&lexbor_idna, 0, sizeof(lexbor_idna));
557
224k
  lxb_url_parser_destroy(&lexbor_parser, false);
558
224k
  memset(&lexbor_parser, 0, sizeof(lexbor_parser));
559
224k
  lexbor_mraw_destroy(&lexbor_mraw, false);
560
224k
  memset(&lexbor_mraw, 0, sizeof(lexbor_mraw));
561
562
224k
  return SUCCESS;
563
224k
}
564
565
lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, bool silent)
566
0
{
567
0
  lxb_url_parser_clean(&lexbor_parser);
568
569
0
  lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) uri_str, uri_str_len);
570
571
0
  if ((url == NULL && !silent) || errors != NULL) {
572
0
    zval err;
573
0
    const char *reason = fill_errors(&err);
574
0
    if (url == NULL && !silent) {
575
0
      zend_object *exception = zend_throw_exception_ex(php_uri_ce_whatwg_invalid_url_exception, 0, "The specified URI is malformed%s%s%s", reason ? " (" : "", reason ? reason : "", reason ? ")" : "");
576
0
      zend_update_property(exception->ce, exception, ZEND_STRL("errors"), &err);
577
0
    }
578
0
    if (errors != NULL) {
579
0
      zval_ptr_dtor(errors);
580
0
      ZVAL_COPY_VALUE(errors, &err);
581
0
    } else {
582
0
      zval_ptr_dtor(&err);
583
0
    }
584
0
  }
585
586
0
  return url;
587
0
}
588
589
static void *php_uri_parser_whatwg_parse(const char *uri_str, size_t uri_str_len, const void *base_url, zval *errors, bool silent)
590
0
{
591
0
  return php_uri_parser_whatwg_parse_ex(uri_str, uri_str_len, base_url, errors, silent);
592
0
}
593
594
static void *php_uri_parser_whatwg_clone(void *uri)
595
0
{
596
0
  const lxb_url_t *lexbor_uri = uri;
597
598
0
  return lxb_url_clone(lexbor_parser.mraw, lexbor_uri);
599
0
}
600
601
static zend_string *php_uri_parser_whatwg_to_string(void *uri, php_uri_recomposition_mode recomposition_mode, bool exclude_fragment)
602
0
{
603
0
  const lxb_url_t *lexbor_uri = uri;
604
0
  smart_str uri_str = {0};
605
606
0
  switch (recomposition_mode) {
607
0
    case PHP_URI_RECOMPOSITION_MODE_RAW_UNICODE:
608
0
      ZEND_FALLTHROUGH;
609
0
    case PHP_URI_RECOMPOSITION_MODE_NORMALIZED_UNICODE:
610
0
      lxb_url_serialize_idna(&lexbor_idna, lexbor_uri, serialize_to_smart_str_callback, &uri_str, exclude_fragment);
611
0
      lxb_unicode_idna_clean(&lexbor_idna);
612
0
      break;
613
0
    case PHP_URI_RECOMPOSITION_MODE_RAW_ASCII:
614
0
      ZEND_FALLTHROUGH;
615
0
    case PHP_URI_RECOMPOSITION_MODE_NORMALIZED_ASCII:
616
0
      lxb_url_serialize(lexbor_uri, serialize_to_smart_str_callback, &uri_str, exclude_fragment);
617
0
      break;
618
0
    EMPTY_SWITCH_DEFAULT_CASE()
619
0
  }
620
621
0
  return smart_str_extract(&uri_str);
622
0
}
623
624
static void php_uri_parser_whatwg_destroy(void *uri)
625
43
{
626
43
  lxb_url_t *lexbor_uri = uri;
627
628
43
  lxb_url_destroy(lexbor_uri);
629
43
}
630
631
PHPAPI const php_uri_parser php_uri_parser_whatwg = {
632
  .name = PHP_URI_PARSER_WHATWG,
633
  .parse = php_uri_parser_whatwg_parse,
634
  .clone = php_uri_parser_whatwg_clone,
635
  .to_string = php_uri_parser_whatwg_to_string,
636
  .destroy = php_uri_parser_whatwg_destroy,
637
  {
638
    .scheme = {.read = php_uri_parser_whatwg_scheme_read, .write = php_uri_parser_whatwg_scheme_write},
639
    .username = {.read = php_uri_parser_whatwg_username_read, .write = php_uri_parser_whatwg_username_write},
640
    .password = {.read = php_uri_parser_whatwg_password_read, .write = php_uri_parser_whatwg_password_write},
641
    .host = {.read = php_uri_parser_whatwg_host_read, .write = php_uri_parser_whatwg_host_write},
642
    .port = {.read = php_uri_parser_whatwg_port_read, .write = php_uri_parser_whatwg_port_write},
643
    .path = {.read = php_uri_parser_whatwg_path_read, .write = php_uri_parser_whatwg_path_write},
644
    .query = {.read = php_uri_parser_whatwg_query_read, .write = php_uri_parser_whatwg_query_write},
645
    .fragment = {.read = php_uri_parser_whatwg_fragment_read, .write = php_uri_parser_whatwg_fragment_write},
646
  }
647
};