Skip to content

ext/spl: Refactor ArrayObject sort methods #19023

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

Merged
merged 2 commits into from
Jul 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 31 additions & 21 deletions ext/spl/spl_array.c
Original file line number Diff line number Diff line change
Expand Up @@ -1203,43 +1203,54 @@ PHP_METHOD(ArrayObject, count)
RETURN_LONG(spl_array_object_count_elements_helper(intern));
} /* }}} */

static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, size_t fname_len, int use_arg) /* {{{ */
enum spl_array_object_sort_methods {
SPL_NAT_SORT,
SPL_CALLBACK_SORT,
SPL_OPTIONAL_FLAG_SORT
};

static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, const char *fname, size_t fname_len, enum spl_array_object_sort_methods use_arg) /* {{{ */
{
spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern);
HashTable *aht = *ht_ptr;
zval function_name, params[2], *arg = NULL;
zval params[2], *arg = NULL;

ZVAL_STRINGL(&function_name, fname, fname_len);
zend_function *fn = zend_hash_str_find_ptr(EG(function_table), fname, fname_len);
if (UNEXPECTED(fn == NULL)) {
zend_throw_error(NULL, "Cannot call method %s when function %s is disabled", fname, fname);
RETURN_THROWS();
}

ZVAL_NEW_EMPTY_REF(&params[0]);
ZVAL_ARR(Z_REFVAL(params[0]), aht);
GC_ADDREF(aht);

if (!use_arg) {
if (use_arg == SPL_NAT_SORT) {
if (zend_parse_parameters_none() == FAILURE) {
goto exit;
}

intern->nApplyCount++;
call_user_function(EG(function_table), NULL, &function_name, return_value, 1, params);
zend_call_known_function(fn, NULL, NULL, return_value, 1, params, NULL);
intern->nApplyCount--;
} else if (use_arg == SPL_ARRAY_METHOD_SORT_FLAGS_ARG) {
} else if (use_arg == SPL_OPTIONAL_FLAG_SORT) {
zend_long sort_flags = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &sort_flags) == FAILURE) {
goto exit;
}
ZVAL_LONG(&params[1], sort_flags);
intern->nApplyCount++;
call_user_function(EG(function_table), NULL, &function_name, return_value, 2, params);
zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL);
intern->nApplyCount--;
} else {
ZEND_ASSERT(use_arg == SPL_CALLBACK_SORT);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
goto exit;
}
ZVAL_COPY_VALUE(&params[1], arg);
intern->nApplyCount++;
call_user_function(EG(function_table), NULL, &function_name, return_value, 2, params);
zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL);
intern->nApplyCount--;
}

Expand All @@ -1251,7 +1262,6 @@ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, size_t f
*ht_ptr = Z_ARRVAL_P(ht_zv);
ZVAL_NULL(ht_zv);
zval_ptr_dtor(&params[0]);
zend_string_free(Z_STR(function_name));
}
} /* }}} */

Expand All @@ -1261,23 +1271,23 @@ PHP_METHOD(cname, fname) \
spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \
}

/* {{{ Sort the entries by values. */
SPL_ARRAY_METHOD(ArrayObject, asort, SPL_ARRAY_METHOD_SORT_FLAGS_ARG) /* }}} */
/* Sort the entries by values. */
SPL_ARRAY_METHOD(ArrayObject, asort, SPL_OPTIONAL_FLAG_SORT)

/* {{{ Sort the entries by key. */
SPL_ARRAY_METHOD(ArrayObject, ksort, SPL_ARRAY_METHOD_SORT_FLAGS_ARG) /* }}} */
/* Sort the entries by key. */
SPL_ARRAY_METHOD(ArrayObject, ksort, SPL_OPTIONAL_FLAG_SORT)

/* {{{ Sort the entries by values user defined function. */
SPL_ARRAY_METHOD(ArrayObject, uasort, SPL_ARRAY_METHOD_CALLBACK_ARG) /* }}} */
/* Sort the entries by values user defined function. */
SPL_ARRAY_METHOD(ArrayObject, uasort, SPL_CALLBACK_SORT)

/* {{{ Sort the entries by key using user defined function. */
SPL_ARRAY_METHOD(ArrayObject, uksort, SPL_ARRAY_METHOD_CALLBACK_ARG) /* }}} */
/* Sort the entries by key using user defined function. */
SPL_ARRAY_METHOD(ArrayObject, uksort, SPL_CALLBACK_SORT)

/* {{{ Sort the entries by values using "natural order" algorithm. */
SPL_ARRAY_METHOD(ArrayObject, natsort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
/* Sort the entries by values using "natural order" algorithm. */
SPL_ARRAY_METHOD(ArrayObject, natsort, SPL_NAT_SORT)

/* {{{ Sort the entries by key using case insensitive "natural order" algorithm. */
SPL_ARRAY_METHOD(ArrayObject, natcasesort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
/* {{{ Sort the entries by key using case-insensitive "natural order" algorithm. */
SPL_ARRAY_METHOD(ArrayObject, natcasesort, SPL_NAT_SORT)

/* {{{ Serialize the object */
PHP_METHOD(ArrayObject, serialize)
Expand Down
4 changes: 0 additions & 4 deletions ext/spl/spl_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
#define SPL_ARRAY_INT_MASK 0xFFFF0000
#define SPL_ARRAY_CLONE_MASK 0x0100FFFF

#define SPL_ARRAY_METHOD_NO_ARG 0
#define SPL_ARRAY_METHOD_CALLBACK_ARG 1
#define SPL_ARRAY_METHOD_SORT_FLAGS_ARG 2

extern PHPAPI zend_class_entry *spl_ce_ArrayObject;
extern PHPAPI zend_class_entry *spl_ce_ArrayIterator;
extern PHPAPI zend_class_entry *spl_ce_RecursiveArrayIterator;
Expand Down
18 changes: 18 additions & 0 deletions ext/spl/tests/ArrayObject/asort_disabled.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
ArrayObject when function asort() is disabled
--INI--
disable_functions=asort
--FILE--
<?php
$a = ['hello', 'world', 'SPL', 'is', 'fun', '!'];
$ao = new ArrayObject($a);
$ao->asort();
var_dump($ao);

?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot call method asort when function asort is disabled in %s:%d
Stack trace:
#0 %s(%d): ArrayObject->asort()
#1 {main}
thrown in %s on line %d
18 changes: 18 additions & 0 deletions ext/spl/tests/ArrayObject/ksort_disabled.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
ArrayObject when function ksort() is disabled
--INI--
disable_functions=ksort
--FILE--
<?php
$a = ['hello', 'world', 'SPL', 'is', 'fun', '!'];
$ao = new ArrayObject($a);
$ao->ksort();
var_dump($ao);

?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot call method ksort when function ksort is disabled in %s:%d
Stack trace:
#0 %s(%d): ArrayObject->ksort()
#1 {main}
thrown in %s on line %d
18 changes: 18 additions & 0 deletions ext/spl/tests/ArrayObject/natcasesort_disabled.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
ArrayObject when function natcasesort() is disabled
--INI--
disable_functions=natcasesort
--FILE--
<?php
$a = ['hello', 'world', 'SPL', 'is', 'fun', '!'];
$ao = new ArrayObject($a);
$ao->natcasesort();
var_dump($ao);

?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot call method natcasesort when function natcasesort is disabled in %s:%d
Stack trace:
#0 %s(%d): ArrayObject->natcasesort()
#1 {main}
thrown in %s on line %d
18 changes: 18 additions & 0 deletions ext/spl/tests/ArrayObject/natsort_disabled.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
ArrayObject when function natsort() is disabled
--INI--
disable_functions=natsort
--FILE--
<?php
$a = ['hello', 'world', 'SPL', 'is', 'fun', '!'];
$ao = new ArrayObject($a);
$ao->natsort();
var_dump($ao);

?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot call method natsort when function natsort is disabled in %s:%d
Stack trace:
#0 %s(%d): ArrayObject->natsort()
#1 {main}
thrown in %s on line %d
18 changes: 18 additions & 0 deletions ext/spl/tests/ArrayObject/uasort_disabled.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
ArrayObject when function uasort() is disabled
--INI--
disable_functions=uasort
--FILE--
<?php
$a = ['hello', 'world', 'SPL', 'is', 'fun', '!'];
$ao = new ArrayObject($a);
$ao->uasort(fn ($l, $r) => $l <=> $r);
var_dump($ao);

?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot call method uasort when function uasort is disabled in %s:%d
Stack trace:
#0 %s(%d): ArrayObject->uasort(Object(Closure))
#1 {main}
thrown in %s on line %d
18 changes: 18 additions & 0 deletions ext/spl/tests/ArrayObject/uksort_disabled.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
ArrayObject when function uksort() is disabled
--INI--
disable_functions=uksort
--FILE--
<?php
$a = ['hello', 'world', 'SPL', 'is', 'fun', '!'];
$ao = new ArrayObject($a);
$ao->uksort(fn ($l, $r) => $l <=> $r);
var_dump($ao);

?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot call method uksort when function uksort is disabled in %s:%d
Stack trace:
#0 %s(%d): ArrayObject->uksort(Object(Closure))
#1 {main}
thrown in %s on line %d