. Fixed bug #62059 (ArrayObject and isset are not friends). (Nikita)
. Fixed bug #71871 (Interfaces allow final and abstract functions). (Nikita)
. Fixed bug #71922 (Crash on assert(new class{})). (Nikita)
+ . Fixed bug #71334 (Cannot access array keys while uksort()). (Nikita)
- Curl:
. Fixed bug #71831 (CURLOPT_NOPROXY applied as long instead of string).
#define Z_SPLARRAY_P(zv) spl_array_from_obj(Z_OBJ_P((zv)))
-static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /* {{{ */
+static inline HashTable **spl_array_get_hash_table_ptr(spl_array_object* intern) { /* {{{ */
//??? TODO: Delay duplication for arrays; only duplicate for write operations
if (intern->ar_flags & SPL_ARRAY_IS_SELF) {
if (!intern->std.properties) {
rebuild_object_properties(&intern->std);
}
- return intern->std.properties;
+ return &intern->std.properties;
} else if (intern->ar_flags & SPL_ARRAY_USE_OTHER) {
spl_array_object *other = Z_SPLARRAY_P(&intern->array);
- return spl_array_get_hash_table(other);
+ return spl_array_get_hash_table_ptr(other);
} else if (Z_TYPE(intern->array) == IS_ARRAY) {
- return Z_ARRVAL(intern->array);
+ return &Z_ARRVAL(intern->array);
} else {
zend_object *obj = Z_OBJ(intern->array);
if (!obj->properties) {
}
obj->properties = zend_array_dup(obj->properties);
}
- return obj->properties;
+ return &obj->properties;
}
-} /* }}} */
+}
+/* }}} */
+
+static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /* {{{ */
+ return *spl_array_get_hash_table_ptr(intern);
+}
+/* }}} */
+
+static inline void spl_array_replace_hash_table(spl_array_object* intern, HashTable *ht) { /* {{{ */
+ HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern);
+ zend_array_destroy(*ht_ptr);
+ *ht_ptr = ht;
+}
+/* }}} */
static inline zend_bool spl_array_is_object(spl_array_object *intern) /* {{{ */
{
spl_array_object *intern = Z_SPLARRAY_P(getThis());
HashTable *aht = spl_array_get_hash_table(intern);
zval function_name, params[2], *arg = NULL;
- uint32_t old_refcount;
ZVAL_STRINGL(&function_name, fname, fname_len);
- /* A tricky way to pass "aht" by reference, reset refcount */
- //??? It may be not safe, if user comparison handler accesses "aht"
- old_refcount = GC_REFCOUNT(aht);
- GC_REFCOUNT(aht) = 1;
ZVAL_NEW_EMPTY_REF(¶ms[0]);
ZVAL_ARR(Z_REFVAL(params[0]), aht);
+ GC_REFCOUNT(aht)++;
if (!use_arg) {
intern->nApplyCount++;
}
exit:
- /* A tricky way to pass "aht" by reference, copy back and cleanup */
- GC_REFCOUNT(aht) = old_refcount;
- efree(Z_REF(params[0]));
- zend_string_free(Z_STR(function_name));
+ {
+ HashTable *new_ht = Z_ARRVAL_P(Z_REFVAL(params[0]));
+ if (aht != new_ht) {
+ spl_array_replace_hash_table(intern, new_ht);
+ } else {
+ GC_REFCOUNT(aht)--;
+ }
+ efree(Z_REF(params[0]));
+ zend_string_free(Z_STR(function_name));
+ }
} /* }}} */
#define SPL_ARRAY_METHOD(cname, fname, use_arg) \
static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, zend_bool renumber) /* {{{ */
{
zval *array;
- zend_refcounted *arr;
+ zend_array *arr;
zend_bool retval;
PHP_ARRAY_CMP_FUNC_VARS;
PHP_ARRAY_CMP_FUNC_BACKUP();
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/f", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
PHP_ARRAY_CMP_FUNC_RESTORE();
return;
}
- /* Increase reference counter, so the attempts to modify the array in user
- * comparison function will create a copy of array and won't affect the
- * original array. The fact of modification is detected by comparing the
- * zend_array pointer. The result of sorting in such case is undefined and
- * the function returns FALSE.
- */
- Z_ADDREF_P(array);
- arr = Z_COUNTED_P(array);
+ arr = Z_ARR_P(array);
+ if (zend_hash_num_elements(arr) == 0) {
+ PHP_ARRAY_CMP_FUNC_RESTORE();
+ RETURN_TRUE;
+ }
- retval = zend_hash_sort(Z_ARRVAL_P(array), compare_func, renumber) != FAILURE;
+ /* Copy array, so the in-place modifications will not be visible to the callback function */
+ arr = zend_array_dup(arr);
- if (arr != Z_COUNTED_P(array)) {
- php_error_docref(NULL, E_WARNING, "Array was modified by the user comparison function");
- if (--GC_REFCOUNT(arr) <= 0) {
- _zval_dtor_func(arr ZEND_FILE_LINE_CC);
- }
- retval = 0;
- } else {
- Z_DELREF_P(array);
- }
+ retval = zend_hash_sort(arr, compare_func, renumber) != FAILURE;
+
+ zval_ptr_dtor(array);
+ ZVAL_ARR(array, arr);
PHP_ARRAY_CMP_FUNC_RESTORE();
RETURN_BOOL(retval);
--- /dev/null
+--TEST--
+Bug #71334: Cannot access array keys while uksort()
+--FILE--
+<?php
+
+class myClass
+{
+ private $a = [
+ 'foo-test' => [1],
+ '-' => [2],
+ 'bar-test' => [3]
+ ];
+
+ private function _mySort($x, $y)
+ {
+ if (!isset($this->a[$x])) {
+ throw new Exception('Missing X: "' . $x . '"');
+ }
+
+ if (!isset($this->a[$y])) {
+ throw new Exception('Missing Y: "' . $y . '"');
+ }
+
+ return $x < $y;
+ }
+
+ public function __construct()
+ {
+ uksort($this->a, [$this, '_mySort']);
+ }
+}
+
+new myClass();
+echo "Done";
+
+?>
+--EXPECT--
+Done
<?php
function usercompare($a,$b) {
unset($GLOBALS['my_var'][2]);
- return 0;
+ return $a <=> $b;
}
$my_var = array(1 => "entry_1",
2 => "entry_2",
4 => "entry_4",
5 => "entry_5");
usort($my_var, "usercompare");
+var_dump($my_var);
-echo "Done.\n";
?>
---EXPECTF--
-
-Warning: usort(): Array was modified by the user comparison function in %s on line %d
-Done.
+--EXPECT--
+array(5) {
+ [0]=>
+ string(7) "entry_1"
+ [1]=>
+ string(7) "entry_2"
+ [2]=>
+ string(7) "entry_3"
+ [3]=>
+ string(7) "entry_4"
+ [4]=>
+ string(7) "entry_5"
+}
--- /dev/null
+--TEST--
+Crash when function parameter modified via reference while keeping orig refcount
+--FILE--
+<?php
+
+$array = array(
+ 1 => "entry_1",
+ 2 => "entry_2",
+ 3 => "entry_3",
+ 4 => "entry_4",
+ 5 => "entry_5"
+);
+usort($array, function($a, $b) use (&$array, &$ref) {
+ unset($array[2]);
+ $ref = $array;
+ return $a <=> $b;
+});
+var_dump($array);
+
+?>
+--EXPECT--
+array(5) {
+ [0]=>
+ string(7) "entry_1"
+ [1]=>
+ string(7) "entry_2"
+ [2]=>
+ string(7) "entry_3"
+ [3]=>
+ string(7) "entry_4"
+ [4]=>
+ string(7) "entry_5"
+}