8000 [RFC] Add RFC 3986 and WHATWG compliant URL parsing support by kocsismate · Pull Request #14461 · php/php-src · GitHub
[go: up one dir, main page]

Skip to content

[RFC] Add RFC 3986 and WHATWG compliant URL parsing support #14461

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
196957b
Create separate lexbor extension
kocsismate May 11, 2025
0334e22
Add RFC 3986 and WHATWG compliant URL parsing support
kocsismate Jun 3, 2024
a09a457
Serialization
kocsismate Oct 22, 2024
3abdb0a
Improve error handling
kocsismate Oct 26, 2024
1445e55
Lot of fixes and added support for equalsTo()
kocsismate Nov 11, 2024
e4de160
Add normalization support
kocsismate Nov 13, 2024
77184ce
SOAP test fixes
kocsismate Nov 13, 2024
3b0449d
Fix some memory leaks
kocsismate Nov 13, 2024
5f90823
Some cleanups
kocsismate Nov 18, 2024
b7011b8
Changes based on discussion
kocsismate Nov 30, 2024
f20842a
Removal of Uri\Uri
kocsismate Dec 30, 2024
c88e92b
A lot of fixes and API changes
kocsismate Jan 6, 2025
c743ead
Updates
kocsismate Feb 5, 2025
19b7180
Add new tests, path fixes
kocsismate Feb 9, 2025
fb0c929
Add more tests for verifying the behavior of withers
kocsismate Feb 15, 2025
cf7ca4e
Fix code review comments
kocsismate Feb 19, 2025
9de1271
A few fixes and improvements after feedback
kocsismate Apr 14, 2025
db3b79d
Test fixes
kocsismate Apr 14, 2025
feefccf
Remove WHATWG non-raw getters
kocsismate Apr 18, 2025
190bbfd
Rename WHATWG getters again
kocsismate Apr 26, 2025
d1df694
Add UriComparisonMode
kocsismate Apr 28, 2025
0d1fc02
Expose $softErrors for Uri\WhatWg\Url::resolve()
kocsismate Apr 30, 2025
df1d0b9
Add SensitiveParameter support
kocsismate May 3, 2025
e8261f7
Proper build support
kocsismate May 19, 2025
7530dd9
Review fixes and serialization update according to the RFC
kocsismate May 20, 2025
b75cc5b
Some extension fixes
kocsismate May 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Review fixes and serialization update according to the RFC
  • Loading branch information
kocsismate committed May 25, 2025
commit 7530dd91b44e1568a48287d86e2b5a3575f62c12
3 changes: 3 additions & 0 deletions build/gen_stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -3054,6 +3054,9 @@ class PropertyInfo extends VariableLike
private const PHP_85_KNOWN = [
"self" => "ZEND_STR_SELF",
"parent" => "ZEND_STR_PARENT",
"userinfo" => "ZEND_STR_USERINFO",
"username" => "ZEND_STR_USERNAME",
"password" => "ZEND_STR_PASSWORD",
];

/**
Expand Down
6 changes: 1 addition & 5 deletions ext/standard/url.c
Original file line number Diff line number Diff line change
Expand Up @@ -936,9 +936,5 @@ PHP_FUNCTION(get_headers)

PHP_MINIT_FUNCTION(url)
{
if (uri_handler_register(&parse_url_uri_handler) == FAILURE) {
return FAILURE;
}

return SUCCESS;
return uri_handler_register(&parse_url_uri_handler);
}
2 changes: 2 additions & 0 deletions ext/uri/CREDITS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
uri
Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Nicolas Grekas
10 changes: 4 additions & 6 deletions ext/uri/php_lexbor.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ const uri_handler_t lexbor_uri_handler = {
if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value) > 0) { \
lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \
} else if (Z_TYPE_P(value) == IS_LONG && Z_LVAL_P(value) != 0) { \
ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \
lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \
ZVAL_STR(value, zend_long_to_str(Z_LVAL_P(value))); \
lexbor_str_init_append(&str, lexbor_parser->mraw, (const lxb_char_t *) Z_STRVAL_P(value), Z_STRLEN_P(value)); \
} else { \
lexbor_str_init(&str, lexbor_parser->mraw, 0); \
} \
Expand All @@ -73,7 +73,7 @@ const uri_handler_t lexbor_uri_handler = {
case URI_COMPONENT_READ_NORMALIZED_UNICODE: /* Intentional fallthrough */ \
case URI_COMPONENT_READ_NORMALIZED_ASCII: { \
ZVAL_STRINGL(retval, (const char *) start, len); \
break; \
break; \
} \
EMPTY_SWITCH_DEFAULT_CASE() \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given the EMPTY_SWITCH_DEFAULT_CASE() and the fact that all of the cases fall through, what is the point of even having a switch?

} \
Expand Down Expand Up @@ -415,9 +415,7 @@ static zend_result lexbor_read_path(const uri_internal_t *internal_uri, uri_comp
{
lxb_url_t *lexbor_uri = (lxb_url_t *) internal_uri->uri;

if (lexbor_uri->path.opaque) {
LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->path.str.data, lexbor_uri->path.str.length, read_mode, retval);
} else if (lexbor_uri->path.str.length) {
if (lexbor_uri->path.str.length) {
LEXBOR_READ_ASCII_URI_COMPONENT(lexbor_uri->path.str.data, lexbor_uri->path.str.length, read_mode, retval);
} else {
ZVAL_EMPTY_STRING(retval);
Expand Down
111 changes: 57 additions & 54 deletions ext/uri/php_uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ PHPAPI void php_uri_instantiate_uri(
void *base_url = NULL;
if (base_url_object != NULL) {
uri_internal_t *internal_base_url = uri_internal_from_obj(base_url_object);
URI_CHECK_INITIALIZATION(internal_base_url);
URI_ASSERT_INITIALIZATION(internal_base_url);
base_url = internal_base_url->uri;
}

Expand Down Expand Up @@ -522,7 +522,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, getPort)

PHP_METHOD(Uri_Rfc3986_Uri, withPort)
{
URI_WITHER_LONG(ZSTR_KNOWN(ZEND_STR_PORT));
URI_WITHER_LONG_OR_NULL(ZSTR_KNOWN(ZEND_STR_PORT));
}

PHP_METHOD(Uri_Rfc3986_Uri, getPath)
Expand Down Expand Up @@ -574,10 +574,10 @@ static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, z
{
zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *this_internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(this_internal_uri);
URI_ASSERT_INITIALIZATION(this_internal_uri);

uri_internal_t *that_internal_uri = uri_internal_from_obj(that_object);
URI_CHECK_INITIALIZATION(that_internal_uri);
URI_ASSERT_INITIALIZATION(that_internal_uri);

if (this_object->ce != that_object->ce &&
!instanceof_function(this_object->ce, that_object->ce) &&
Expand All @@ -595,15 +595,16 @@ static void uri_equals(INTERNAL_FUNCTION_PARAMETERS, zend_object *that_object, z
}

zend_string *this_str = this_internal_uri->handler->uri_to_string(
this_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment);
this_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment);
if (this_str == NULL) {
zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name));
RETURN_THROWS();
}

zend_string *that_str = that_internal_uri->handler->uri_to_string(
that_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment);
that_internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, exclude_fragment);
if (that_str == NULL) {
zend_string_release(this_str);
zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(that_object->ce->name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this_str be released here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch! Yes, it should definitely be released! (this code path is not actually be triggerable by the current implementations -- but custom URIs may trigger this exception)

RETURN_THROWS();
}
Expand Down Expand Up @@ -634,7 +635,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, toRawString)

zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(internal_uri);
URI_ASSERT_INITIALIZATION(internal_uri);

zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false);
if (uri_str == NULL) {
Expand All @@ -651,7 +652,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, toString)

zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(internal_uri);
URI_ASSERT_INITIALIZATION(internal_uri);

zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_NORMALIZED_ASCII, false);
if (uri_str == NULL) {
Expand All @@ -672,7 +673,7 @@ PHP_METHOD(Uri_Rfc3986_Uri, resolve)

zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(internal_uri);
URI_ASSERT_INITIALIZATION(internal_uri);

php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal_uri->handler, uri_str, this_object, true, false, NULL);
}
Expand All @@ -683,71 +684,63 @@ PHP_METHOD(Uri_Rfc3986_Uri, __serialize)

zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(internal_uri);

HashTable *result = zend_array_dup(this_object->handlers->get_properties(this_object));
if (zend_hash_str_find_ind(result, URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1) != NULL) {
zend_hash_destroy(result);
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: $__uri property is not allowed", ZSTR_VAL(this_object->ce->name));
RETURN_THROWS();
}
URI_ASSERT_INITIALIZATION(internal_uri);

/* Serialize state: "uri" key in the first array */
zend_string *uri_str = internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false);
if (uri_str == NULL) {
zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to string", ZSTR_VAL(this_object->ce->name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you not need to zend_hash_destroy(result); here like done above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think I have to, but I'll also have to rewrite the serialization implementation as designed in the RFC.

RETURN_THROWS();
}
zval tmp;
ZVAL_STR(&tmp, uri_str);

zval uri_zv;
ZVAL_STR(&uri_zv, uri_str);
zend_hash_str_add_new(result, URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1, &uri_zv);

ZVAL_ARR(return_value, result);
}

static void uri_restore_custom_properties(zend_object *object, uri_internal_t *internal_uri, HashTable *ht)
{
zend_string *prop_name;
zval *prop_val;
array_init(return_value);

ZEND_HASH_FOREACH_STR_KEY_VAL(ht, prop_name, prop_val) {
if (!prop_name || Z_TYPE_P(prop_val) == IS_REFERENCE ||
zend_string_equals_literal(prop_name, URI_SERIALIZED_PROPERTY_NAME)
) {
continue;
}
zval arr;
array_init(&arr);
zend_hash_str_add_new(Z_ARRVAL(arr), URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1, &tmp);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr);

zend_update_property_ex(object->ce, object, prop_name, prop_val);
if (UNEXPECTED(EG(exception) != NULL)) {
break;
}
} ZEND_HASH_FOREACH_END();
/* Serialize regular properties: second array */
ZVAL_ARR(&arr, this_object->handlers->get_properties(this_object));
Z_TRY_ADDREF(arr);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &arr);
}

static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_name)
{
HashTable *properties;
HashTable *data;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY_HT(properties)
Z_PARAM_ARRAY_HT(data)
ZEND_PARSE_PARAMETERS_END();

zend_object *object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(object);

zval *uri_zv = zend_hash_str_find_ind(properties, URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1);
if (uri_zv == NULL) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: missing \"__uri\" field", ZSTR_VAL(object->ce->name));
/* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
if (zend_hash_num_elements(data) != 2) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name));
RETURN_THROWS();
}

/* Unserialize state: "uri" key in the first array */
zval *arr = zend_hash_index_find(data, 0);
if (arr == NULL || Z_TYPE_P(arr) != IS_ARRAY) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name));
RETURN_THROWS();
}
if (Z_TYPE_P(uri_zv) != IS_STRING) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object: \"__uri\" field is not a string", ZSTR_VAL(object->ce->name));

zval *uri_zv = zend_hash_str_find_ind(Z_ARRVAL_P(arr), URI_SERIALIZED_PROPERTY_NAME, sizeof(URI_SERIALIZED_PROPERTY_NAME) - 1);
if (uri_zv == NULL || Z_TYPE_P(uri_zv) != IS_STRING) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name));
RETURN_THROWS();
}

zval errors;
ZVAL_UNDEF(&errors);

uri_internal_t *internal_uri = uri_internal_from_obj(object);
internal_uri->handler = uri_handler_by_name(handler_name, strlen(handler_name));
if (internal_uri->uri != NULL) {
internal_uri->handler->free_uri(internal_uri->uri);
Expand All @@ -756,12 +749,22 @@ static void uri_unserialize(INTERNAL_FUNCTION_PARAMETERS, const char *handler_na
if (internal_uri->uri == NULL) {
throw_invalid_uri_exception(internal_uri->handler, &errors);
zval_ptr_dtor(&errors);
zval_ptr_dtor(uri_zv);
RETURN_THROWS();
}
zval_ptr_dtor(&errors);

uri_restore_custom_properties(object, internal_uri, properties);
/* Unserialize regular properties: second array */
arr = zend_hash_index_find(data, 1);
if (arr == NULL || Z_TYPE_P(arr) != IS_ARRAY) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name));
RETURN_THROWS();
}

object_properties_load(object, Z_ARRVAL_P(arr));
if (EG(exception)) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(object->ce->name));
RETURN_THROWS();
}
}

PHP_METHOD(Uri_Rfc3986_Uri, __unserialize)
Expand Down Expand Up @@ -822,7 +825,7 @@ PHP_METHOD(Uri_WhatWg_Url, withHost)
ZVAL_STR_COPY(&zv, value);
}

URI_WITHER_COMMON(ZSTR_KNOWN(ZEND_STR_HOST), &zv, return_value) \
URI_WITHER_COMMON(ZSTR_KNOWN(ZEND_STR_HOST), &zv, return_value);
}

PHP_METHOD(Uri_WhatWg_Url, equals)
Expand All @@ -845,7 +848,7 @@ PHP_METHOD(Uri_WhatWg_Url, toUnicodeString)

zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(internal_uri);
URI_ASSERT_INITIALIZATION(internal_uri);

RETURN_STR(internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_UNICODE, false));
}
Expand All @@ -856,7 +859,7 @@ PHP_METHOD(Uri_WhatWg_Url, toAsciiString)

zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(internal_uri);
URI_ASSERT_INITIALIZATION(internal_uri);

RETURN_STR(internal_uri->handler->uri_to_string(internal_uri->uri, URI_RECOMPOSITION_RAW_ASCII, false));
}
Expand All @@ -874,7 +877,7 @@ PHP_METHOD(Uri_WhatWg_Url, resolve)

zend_object *this_object = Z_OBJ_P(ZEND_THIS);
uri_internal_t *internal_uri = uri_internal_from_obj(this_object);
URI_CHECK_INITIALIZATION(internal_uri);
URI_ASSERT_INITIALIZATION(internal_uri);

php_uri_instantiate_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal_uri->handler, uri_str, this_object, true, false, errors);
}
Expand Down Expand Up @@ -920,7 +923,7 @@ static zend_object *uri_clone_obj_handler(zend_object *object)
ZEND_ASSERT(new_object != NULL);
uri_object_t *new_uri_object = uri_object_from_obj(new_object);

URI_CHECK_INITIALIZATION(internal_uri);
URI_ASSERT_INITIALIZATION(internal_uri);

new_uri_object->internal.handler = internal_uri->handler;

Expand Down
12 changes: 6 additions & 6 deletions ext/uri/php_uri_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static inline uri_internal_t *uri_internal_from_obj(const zend_object *object) {

#define URI_PARSER_RFC3986 "Uri\\Rfc3986\\Uri"
#define URI_PARSER_WHATWG "Uri\\WhatWg\\Url"
#define URI_SERIALIZED_PROPERTY_NAME "__uri"
#define URI_SERIALIZED_PROPERTY_NAME "uri"

typedef zend_result (*uri_read_t)(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval);

Expand All @@ -103,14 +103,14 @@ zend_result uri_handler_register(const uri_handler_t *uri_handler);
uri_property_handler_t *uri_property_handler_from_internal_uri(const uri_internal_t *internal_uri, zend_string *name);
void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors);

#define URI_CHECK_INITIALIZATION(internal_uri) do { \
#define URI_ASSERT_INITIALIZATION(internal_uri) do { \
ZEND_ASSERT(internal_uri != NULL && internal_uri->uri != NULL); \
} while (0)

#define URI_GETTER(property_name, component_read_mode) do { \
ZEND_PARSE_PARAMETERS_NONE(); \
uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); \
URI_CHECK_INITIALIZATION(internal_uri); \
URI_ASSERT_INITIALIZATION(internal_uri); \
const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); \
ZEND_ASSERT(property_handler != NULL); \
if (UNEXPECTED(property_handler->read_func(internal_uri, component_read_mode, return_value) == FAILURE)) { \
Expand All @@ -121,7 +121,7 @@ void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors)

#define URI_WITHER_COMMON(property_name, property_zv, return_value) \
uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); \
URI_CHECK_INITIALIZATION(internal_uri); \
URI_ASSERT_INITIALIZATION(internal_uri); \
const uri_property_handler_t *property_handler = uri_property_handler_from_internal_uri(internal_uri, property_name); \
ZEND_ASSERT(property_handler != NULL); \
zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); \
Expand All @@ -131,7 +131,7 @@ void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors)
RETURN_THROWS(); \
} \
uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); \
URI_CHECK_INITIALIZATION(new_internal_uri); /* TODO fix memory leak of new_object */ \
URI_ASSERT_INITIALIZATION(new_internal_uri); /* TODO fix memory leak of new_object */ \
if (property_handler->write_func == NULL) { \
zend_readonly_property_modification_error_ex(ZSTR_VAL(Z_OBJ_P(ZEND_THIS)->ce->name), ZSTR_VAL(property_name)); \
zend_object_release(new_object); \
Expand Down Expand Up @@ -175,7 +175,7 @@ void throw_invalid_uri_exception(const uri_handler_t *uri_handler, zval *errors)
URI_WITHER_COMMON(property_name, &zv, return_value) \
} while (0)

#define URI_WITHER_LONG(property_name) do { \
#define URI_WITHER_LONG_OR_NULL(property_name) do { \
zend_long value; \
bool value_is_null; \
ZEND_PARSE_PARAMETERS_START(1, 1) \
Expand Down
Loading
0