Ankündigung

Einklappen
Keine Ankündigung bisher.

array_unique, Bug?

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • array_unique, Bug?

    Hallo,
    array_unique() kann erst ab 5.2.9 mit multidimensionalen Arrays umgehen:

    PHP: array_unique - Manual
    Note that keys are preserved. array_unique() sorts the values treated as string
    [..]
    5.2.9 Added the optional sort_flags defaulting to SORT_REGULAR. Prior to 5.2.9, this function used to sort the array with SORT_STRING internally.
    Sprich array(array(1,2), array(1,3), array(1, 2)) liefert mir vor 5.2.9 ein array(array(1,2)) da Arrays als Strings behandelt "Array" geben, somit nur das erste Element behalten wird.


    Nun brauche ich die Funktionalität aber in 5.2.5 (unserer Live-Umgebung). Update ausgeschlossen. Jetzt versuche ich die Funktion nachzubauen:
    PHP-Code:
    <?php
    class AIS_Util_Array
    {
        
    /**
        * @link http://php.net/array_unique
        * @desc 5.2.9: Added the optional sort_flags defaulting to SORT_REGULAR.
        *       Prior to 5.2.9, this function used to sort the array with SORT_STRING internally.
        *
        * @param array $array
        * @return array
        */
        
    public static function unique(array $array)
        {
            if (
    version_compare(PHP_VERSION"5.2.9") >= 0) {
                return 
    array_unique($array);
            }
            
    $isSimpleArray = empty($array) || (== count(
                
    array_filter(
                    
    $array,
                    
    create_function(
                        
    '$e',
                        
    'return is_array($e) || is_object($e);'
                    
    )
                )));
            if (
    $isSimpleArray) {
                
    // no array or object inside
                
    return array_unique($array);
            }
            return 
    self::_unique($array);
        }

        
    /* @desc allow access to own array_unique implementation no matter which php version */
        
    public static function testUnique(array $array)
        {
            return 
    self::_unique($array);
        }

        protected static function 
    _unique(array $array)
        {
            
    $newArray = array();
            foreach (
    $array as $key => $value) {
                
    $newArray[$key] = self::_getHash($value);
            }
            
    $newArray array_unique($newArray);
            foreach (
    $newArray as $key => $value) {
                
    $newArray[$key] = $array[$key];
            }
            return 
    $newArray;
        }

        protected static function 
    _getHash($variable)
        {
            if (
    is_array($variable)) {
                return 
    md5(
                    
    serialize(
                        
    array_combine(
                            
    array_keys($variable),
                            
    array_map(array(__CLASS__"_getHash"), $variable)
                        )
                    )
                );
            } elseif (
    is_object($variable)) {
                return 
    md5(spl_object_hash($variable));
            } else {
                return 
    md5((string)$variable);
            }
        }
    }
    Bei Unit-Tests habe ich jetzt folgendes gefunden, was doch ein Bug ist oder?

    PHP 5.2.9:
    PHP-Code:
    <?php
    $array1 
    = array(array(123), true);
    var_dump(AIS_Util_Array::testUnique($array1));
    echo 
    "<hr />";
    var_dump(AIS_Util_Array::unique($array1));
    echo 
    "<hr />";
    var_dump(array_unique($array1));
    ?>
    Ausgabe:
    Code:
    array(2) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } [1]=>  bool(true) }
    
    array(1) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } }
    
    array(1) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } }
    Das TRUE wird von array_unique() verschluckt, von meiner Implementierung nicht, daher bin ich drübergestolpert. FALSE, NULL, 1, 0 etc werden nicht verschluckt. Bug?

    Könnt ihr das mal mit euren PHP-Versionen testen, oder ist mein Beispiel Schwachsinn (sehe gerade nurnoch Arrays )

    PS: md5((string)$variable) ist ungenau, sprich array_unique(array(null, "")) gibt (meiner Meinung nach unsinnigerweise) array(null) zurück. md5(serialize(array(gettype($variable), (string)$variable))) wäre allerdings nicht mehr identisch mit dem Handling von array_unique().
    "Mein Name ist Lohse, ich kaufe hier ein."


  • #2
    Ich habe PHP Version 5.2.10 und bekomme folgende Ausgabe:
    Code:
    array(2) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } [1]=>  bool(true) }
    array(2) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } [1]=>  bool(true) }
    array(2) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } [1]=>  bool(true) }
    Denke mal in der Version 5.2.9 hat die Funktion noch einen Fehler, sodass es zu dem Verschlucken von TRUE kommt!
    __________________________________________________ __________________________________________________

    Zitat von Chriz Beitrag anzeigen
    PS: md5((string)$variable) ist ungenau, sprich array_unique(array(null, "")) gibt (meiner Meinung nach unsinnigerweise) array(null) zurück.
    Das Problem liegt hier ja in dem Casting von PHP zusammen, dabei werden NULL und auch FALSE nach "" konvertiert.
    PHP-Code:
    var_dump( (string)null );
    echo 
    "<hr />";
    var_dump( (string)"" );
    echo 
    "<hr />";
    var_dump( (string)false ); 
    Dabei bekomme ich dreimal die selbe Ausgabe: string(0) ""
    __________________________________________________ __________________________________________________

    Viel interessanter ist ein noch "Fehler" in deinem Code durch das Bilden des Hashes aus den Keys und Values mit array_keys, weil hierbei nicht sortiert wird, und daher deine Methode bei folgendem Code ...
    PHP-Code:
        $array1 = array(
        array(
    123),
        array(
    321)
    );
    var_dump(AIS_Util_Array::testUnique($array1));
    echo 
    "<hr />";
    var_dump(AIS_Util_Array::unique($array1));
    echo 
    "<hr />";
    var_dump(array_unique($array1)); 
    ... keine gleichen Arrays erkennt oder sollen diese Arrays nicht gleich sein (Definitionssache)?
    Code:
    array(2) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } [1]=>  array(3) { [0]=>  int(3) [1]=>  int(2) [2]=>  int(1) } }
    array(1) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } }
    array(1) { [0]=>  array(3) { [0]=>  int(1) [1]=>  int(2) [2]=>  int(3) } }
    Ich denke hier muss eine Methode zum Normalisieren von Arrays noch verwendet werden, damit es nicht zu diesen Problemen kommt...

    Kommentar


    • #3
      PHP-Code:
      var_dump(phpversion () , array_unique($array1)); 
      Code:
      string(5) "5.2.5"
      array(2) {
        [0]=>
        array(3) {
          [0]=>
          int(1)
          [1]=>
          int(2)
          [2]=>
          int(3)
        }
        [1]=>
        bool(true)
      }
      string(5) "5.3.1"
      array(2) {
        [0]=>
        array(3) {
          [0]=>
          int(1)
          [1]=>
          int(2)
          [2]=>
          int(3)
        }
        [1]=>
        bool(true)
      }
      --

      „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
      Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


      --

      Kommentar


      • #4
        Interessant ist, dass null bei einem serialize komplett verworfen wird.
        PHP-Code:
        $array1 = array(array(123), true false null 0);

        var_dump (unserialize (serialize (array_unique($array1)))); 
        Code:
        array(4) {
          [0]=>
          array(3) {
            [0]=>
            int(1)
            [1]=>
            int(2)
            [2]=>
            int(3)
          }
          [1]=>
          bool(true)
          [2]=>
          bool(false)
          [4]=>
          int(0)
        }
        Ansonsten fände ich das ganz brauchbar, ist dann eben die strict-Variante.


        [edit] Sorry, war Quark.
        --

        „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
        Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


        --

        Kommentar


        • #5
          Hallo,
          danke für eure Antworten und das Testen!

          @Sirke:
          Nein sortieren will ich auf keinen Fall (oder habe ich dich falsch verstanden?), array(1,2) und array(2,1) sollen nicht gleich behandelt werden. Erstens zuviel "Arbeit", zweitens einfach nicht richtig. Die JavaScript Bibliothek YUI hat sich da auch mal was geleistet, in dem sie bei einem neuen Release plötzlich bei YAHOO.lang.JSON.stringify() einfach mal Arrays vorher sortiert hat. Die Reihenfolge von Arrays ist ja fast immer relevant behaupte ich mal.


          Trotzdem macht mir das Problem sorgen, wenn es nicht so wirklich von euch reproduzierbar ist. Ich schau morgen nochmal ins Changelog, vielleicht find ich da ja was dazu.
          "Mein Name ist Lohse, ich kaufe hier ein."

          Kommentar


          • #6
            Jup, du hattest mich richtig verstanden für den Fall array(1,2) und array(2,1), aber das meinte ich mit Definitionssache, was genau "unterschiedliche" Arrays sind!

            ABER ich glaube ich habe dein Problem gefunden UND du musst zum Sortieren zurückkommen:
            PHP-Code:
            $array1 = array(
                array( 
            => 1=> 2=> 3),
                array( 
            => 3=> 2=> 1)
            );

            var_dumparray_keys$array1[0] ) );
            echo 
            "<hr />";
            var_dumparray_keys$array1[1] ) );
            echo 
            "<hr />";

            var_dump(AIS_Util_Array::testUnique($array1));
            echo 
            "<hr />";
            var_dump(AIS_Util_Array::unique($array1));
            echo 
            "<hr />";
            var_dump(array_unique($array1)); 
            Ich gehe mal davon aus, dass hier die beiden Arrays sich nur von der Reihenfolge der Zuweisung nicht aber von den Key-Value-Kombinationen unterscheiden und daher diese Arrays gleich sein sollten. Die Funktion array_keys() liestet aber nach der Zuweisungsreihenfolge, sodass du hier irgendwie normalisieren musst.

            Warum jedoch in meinem Beispiel weiter oben die Arrays array(1,2,3) und array(3,2,1) von array_unique() als gleich angesehen werden, scheint dann an einer unterschiedlichen Definition zu liegen oder einfach an einem Fehler!?!

            Kommentar


            • #7
              array_unique sortiert intern.

              Hier auf dem quellcode von php:

              Code:
              zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), array_data_compare TSRMLS_CC);
              
                  /* go through the sorted array and delete duplicates from the copy */
              I like cooking my family and my pets.
              Use commas. Don't be a psycho.
              Blog - CoverflowJS

              Kommentar

              Lädt...
              X