Skip to content

Commit

Permalink
Support enums in array_unique
Browse files Browse the repository at this point in the history
Fixes GH-9775
Closes GH-11015
  • Loading branch information
iluuu1994 committed Apr 16, 2023
1 parent 84be904 commit e8b8341
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
2 changes: 2 additions & 0 deletions NEWS
Expand Up @@ -27,6 +27,8 @@ PHP NEWS
- Standard:
. Fixed bug GH-10990 (mail() throws TypeError after iterating over
$additional_headers array by reference). (nielsdos)
. Fixed bug GH-9775 (Duplicates returned by array_unique when using enums).
(ilutov)

13 Apr 2023, PHP 8.1.18

Expand Down
56 changes: 56 additions & 0 deletions Zend/tests/gh9775_1.phpt
@@ -0,0 +1,56 @@
--TEST--
GH-9775: Backed enum in array_unique()
--FILE--
<?php

enum Test: string
{
case AUTHENTICATED = 'authenticated';
case COURSES_ADMIN = 'courses.admin';
case BUNDLES_ADMIN = 'bundles.admin';
case COURSES_REPORTING_ACCESS = 'courses-reporting.access';
case B2B_DASHBOARD_ACCESS = 'b2b-dashboard.access';
case INSTRUCTORS_ADMIN = 'instructors.admin';
case USERS_ADMIN = 'users.admin';
case COUPONS_ADMIN = 'coupons.admin';
}

$instructorsAdmin = Test::INSTRUCTORS_ADMIN;

$data = [
Test::COURSES_ADMIN,
Test::COURSES_REPORTING_ACCESS,
Test::BUNDLES_ADMIN,
Test::USERS_ADMIN,
Test::B2B_DASHBOARD_ACCESS,
Test::B2B_DASHBOARD_ACCESS,
Test::INSTRUCTORS_ADMIN,
&$instructorsAdmin,
Test::COUPONS_ADMIN,
Test::AUTHENTICATED,
];

$data = array_unique($data, flags: SORT_REGULAR);

var_dump($data);

?>
--EXPECT--
array(8) {
[0]=>
enum(Test::COURSES_ADMIN)
[1]=>
enum(Test::COURSES_REPORTING_ACCESS)
[2]=>
enum(Test::BUNDLES_ADMIN)
[3]=>
enum(Test::USERS_ADMIN)
[4]=>
enum(Test::B2B_DASHBOARD_ACCESS)
[6]=>
enum(Test::INSTRUCTORS_ADMIN)
[8]=>
enum(Test::COUPONS_ADMIN)
[9]=>
enum(Test::AUTHENTICATED)
}
56 changes: 56 additions & 0 deletions Zend/tests/gh9775_2.phpt
@@ -0,0 +1,56 @@
--TEST--
GH-9775: Pure enum in array_unique()
--FILE--
<?php

enum Test
{
case AUTHENTICATED;
case COURSES_ADMIN;
case BUNDLES_ADMIN;
case COURSES_REPORTING_ACCESS;
case B2B_DASHBOARD_ACCESS;
case INSTRUCTORS_ADMIN;
case USERS_ADMIN;
case COUPONS_ADMIN;
}

$instructorsAdmin = Test::INSTRUCTORS_ADMIN;

$data = [
Test::COURSES_ADMIN,
Test::COURSES_REPORTING_ACCESS,
Test::BUNDLES_ADMIN,
Test::USERS_ADMIN,
Test::B2B_DASHBOARD_ACCESS,
Test::B2B_DASHBOARD_ACCESS,
Test::INSTRUCTORS_ADMIN,
&$instructorsAdmin,
Test::COUPONS_ADMIN,
Test::AUTHENTICATED,
];

$data = array_unique($data, flags: SORT_REGULAR);

var_dump($data);

?>
--EXPECT--
array(8) {
[0]=>
enum(Test::COURSES_ADMIN)
[1]=>
enum(Test::COURSES_REPORTING_ACCESS)
[2]=>
enum(Test::BUNDLES_ADMIN)
[3]=>
enum(Test::USERS_ADMIN)
[4]=>
enum(Test::B2B_DASHBOARD_ACCESS)
[6]=>
enum(Test::INSTRUCTORS_ADMIN)
[8]=>
enum(Test::COUPONS_ADMIN)
[9]=>
enum(Test::AUTHENTICATED)
}
22 changes: 21 additions & 1 deletion ext/standard/array.c
Expand Up @@ -345,7 +345,27 @@ static zend_always_inline int php_array_key_compare_string_locale_unstable_i(Buc

static zend_always_inline int php_array_data_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */
{
return zend_compare(&f->val, &s->val);
int result = zend_compare(&f->val, &s->val);
/* Special enums handling for array_unique. We don't want to add this logic to zend_compare as
* that would be observable via comparison operators. */
zval *rhs = &s->val;
ZVAL_DEREF(rhs);
if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT)
&& result == ZEND_UNCOMPARABLE
&& (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) {
zval *lhs = &f->val;
ZVAL_DEREF(lhs);
if (Z_TYPE_P(lhs) == IS_OBJECT && (Z_OBJCE_P(lhs)->ce_flags & ZEND_ACC_ENUM)) {
// Order doesn't matter, we just need to group the same enum values
uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs);
uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs);
return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1);
} else {
// Shift enums to the end of the array
return -1;
}
}
return result;
}
/* }}} */

Expand Down

0 comments on commit e8b8341

Please sign in to comment.