From: Nikita Popov Date: Mon, 20 Jul 2020 13:15:23 +0000 (+0200) Subject: Fix bug #79868 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6f8d0ba0dd7d9e96ec6b2b75337f701a5ce3d3f0;p=php Fix bug #79868 This simply restores the code from PHP 7.4 which I incorrectly "simplified" in master. --- diff --git a/NEWS b/NEWS index 55bba33d12..f1ba9cfaf7 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,9 @@ PHP NEWS . Fixed bug #79867 (Promoted untyped properties should get null default value). (Nikita) +- Standard: + . Fixed bug #79868 (Sorting with array_unique gives unwanted result). (Nikita) + 09 Jul 2020, PHP 8.0.0alpha2 - FFI: diff --git a/ext/standard/array.c b/ext/standard/array.c index 4d409e8be5..9874c5eaf1 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4462,13 +4462,30 @@ PHP_FUNCTION(array_change_key_case) } /* }}} */ +struct bucketindex { + Bucket b; + unsigned int i; +}; + +static void array_bucketindex_swap(void *p, void *q) +{ + struct bucketindex *f = (struct bucketindex *)p; + struct bucketindex *g = (struct bucketindex *)q; + struct bucketindex t; + t = *f; + *f = *g; + *g = t; +} + /* {{{ Removes duplicate values from array */ PHP_FUNCTION(array_unique) { zval *array; - Bucket *p, *lastkept = NULL; + Bucket *p; zend_long sort_type = PHP_SORT_STRING; bucket_compare_func_t cmp; + struct bucketindex *arTmp, *cmpdata, *lastkept; + uint32_t i, idx; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ARRAY(array) @@ -4520,19 +4537,47 @@ PHP_FUNCTION(array_unique) return; } - cmp = php_get_data_compare_func(sort_type, 0); + cmp = php_get_data_compare_func_unstable(sort_type, 0); RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array))); - zend_hash_sort(Z_ARRVAL_P(return_value), cmp, /* reindex */ 0); + /* create and sort array with pointers to the target_hash buckets */ + arTmp = pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT); + for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++) { + p = Z_ARRVAL_P(array)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (Z_TYPE(p->val) == IS_INDIRECT && Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) continue; + arTmp[i].b = *p; + arTmp[i].i = i; + i++; + } + ZVAL_UNDEF(&arTmp[i].b.val); + zend_sort((void *) arTmp, i, sizeof(struct bucketindex), + (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap); /* go through the sorted array and delete duplicates from the copy */ - ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(return_value), p) { - if (!lastkept || cmp(lastkept, p)) { - lastkept = p; + lastkept = arTmp; + for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) { + if (cmp(&lastkept->b, &cmpdata->b)) { + lastkept = cmpdata; } else { - zend_hash_del_bucket(Z_ARRVAL_P(return_value), p); + if (lastkept->i > cmpdata->i) { + p = &lastkept->b; + lastkept = cmpdata; + } else { + p = &cmpdata->b; + } + if (p->key == NULL) { + zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); + } else { + if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) { + zend_delete_global_variable(p->key); + } else { + zend_hash_del(Z_ARRVAL_P(return_value), p->key); + } + } } - } ZEND_HASH_FOREACH_END(); + } + pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT); } /* }}} */ diff --git a/ext/standard/tests/array/bug79868.phpt b/ext/standard/tests/array/bug79868.phpt new file mode 100644 index 0000000000..5c6267d612 --- /dev/null +++ b/ext/standard/tests/array/bug79868.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #79868: Sorting with array_unique gives unwanted result +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + string(1) "b" + [1]=> + string(1) "a" +}