Bug->Doc #75559 [Opn]: array_unique() with SORT_REGULAR misbehaves with array of objects

From: Date: Thu, 23 Nov 2017 15:13:00 +0000
Subject: Bug->Doc #75559 [Opn]: array_unique() with SORT_REGULAR misbehaves with array of objects
References: 1  Groups: php.doc.bugs 
Request: Send a blank email to doc-bugs+get-15253@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=75559&edit=1

 ID:                 75559
 Updated by:         requinix@php.net
 Reported by:        geompse at gmail dot com
 Summary:            array_unique() with SORT_REGULAR misbehaves with
                     array of objects
 Status:             Open
-Type:               Bug
+Type:               Documentation Problem
 Package:            Arrays related
 Operating System:   Debian
 PHP Version:        7.2.0RC6
 Block user comment: N
 Private report:     N

 New Comment:

Disabling use of the function because it doesn't work the way you expect in one particular case
seems just a bit... I don't know... ridiculous? But it's your codebase so have fun.

If it wasn't already apparent, behind the scenes array_unique() sorts [a copy of] the array.
That's how it's able to find duplicates in less than the O(n^2) time of the obvious but
naive algorithm. Thing is, objects in PHP are not relatively comparable, meaning $a < $b and $a
> $b are both false. This creates inconsistencies when it comes to sorting and the array
basically gets shuffled - just like if you used usort() with a bad comparison function.

Scanning the sorted array for duplicates is a matter of comparing adjacent elements, but since the
$a and $b objects aren't all next to each other it occasionally seems that there are new
"unique" values.

I don't see room for improvement in the function: I don't think we can suddenly make
objects comparable (and even if how would they work?) and sorting is the best general-purpose
solution for finding uniques. So I think we should warn in the docs that using array_uniques() on an
array of objects will only really work well with SORT_STRING/SORT_LOCALE_STRING and after making
sure to implement __toString() in all the relevant classes.

Maybe we add a array_uunique() for a user-defined sorting comparison callback?


Previous Comments:
------------------------------------------------------------------------
[2017-11-23 12:11:04] geompse at gmail dot com

Description:
------------
According to the documentation, array_unique() does the following:
    (string) $elem1 === (string) $elem2
When passed the SORT_REGULAR flag, the comparaison is done without changing types:
    $elem1 === $elem2

In a particuliar case maybe related to the length of the array, the unicity is not done, and
duplicate objects subsists.

In the test script below, you can see that $a !== $b because $a is an instance of class A, and $b is
an instance of class B
When adding multiple times the same object ($a) in an array, the array_unique/SORT_REGULAR does
function properly with only one occurence kept.
When adding a different object ($b) the array_unique/SORT_REGULAR does not function properly,
returning an array containing duplicates.

According to 3v4l.org the issue is reproductible in almost every versions of PHP ranging between
5.2.9 - 5.6.30 and 7.0.0 - 7.2.0rc6:
https://3v4l.org/fYtvR (only versions 5.6 & 7)
https://3v4l.org/sE03m (most versions)

On my side I will deprecate array_unique by adding it to the "disable_functions"
directive, however I would really like to see this issue resolved.
Thank you.

Test script:
---------------
<?php

class A {};
$a = new A();

class B {};
$b = new B();

$array = array();
for($i=0; $i<28; $i++)
    $array[$i] = $a;
$array[0] = $array[26] = $b;

# var_dump($array);
$array = array_unique($array,SORT_REGULAR);
var_dump($array);

Expected result:
----------------
array(5) {
  [0]=>
  object(B)#2 (0) {
  }
  [1]=>
  object(A)#1 (0) {
  }
}

Actual result:
--------------
array(5) {
  [0]=>
  object(B)#2 (0) {
  }
  [1]=>
  object(A)#1 (0) {
  }
  [2]=>
  object(A)#1 (0) {
  }
  [13]=>
  object(A)#1 (0) {
  }
  [26]=>
  object(B)#2 (0) {
  }
}


------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=75559&edit=1


Thread (4 messages)

« previous php.doc.bugs (#15253) next »