Ankündigung

Einklappen
Keine Ankündigung bisher.

[Erledigt] call_user_func_array und array_multisort Referenzproblem

Einklappen

Neue Werbung 2019

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

  • [Erledigt] call_user_func_array und array_multisort Referenzproblem

    Sers

    Ich bastel grad eine Sortierfunktion für Zeilen aus einer CSV-Textdatei.

    Nun liegen mir die Zeilen als Array folgendermaßen vor:

    Code:
    Array
    (
        [0] => Array
            (
                [id] => 3
                [author] => test
                [message] => test text 3
                [date] => 
            )
    
        [1] => Array
            (
                [id] => 4
                [author] => test
                [message] => test text 4
                [date] => 
            )
    
    )
    Diese möchte ich nun nach beliebigen Kriterien sortieren können wie man es aus einer SQL-Datenbank gewohnt ist. Die Kriterien liegen mir folgendermaßen als Array vor:

    Code:
    Array
    (
        [0] => Array
            (
                [field] => id
                [direction] => desc
            )
    
    )
    Das zweite Array kann theoretisch eine beliebige Anzahl an field/direction Paaren enthalten, daher habe ich mich für array_multisort zum sortieren entschieden.

    Der Aufruf erfolgt ja laut Handbuch folgendermaßen:

    PHP-Code:
    array_multisort($bandSORT_DESC$auflageSORT_ASC$data); 
    Da ich die Anzahl der Parameter nicht kenne rufe ich array_multisort mit call_user_func_array auf.

    Folgendermaßen wird das Array welches als Parameter übergeben wird aufgebaut:

    PHP-Code:
            if (!is_null($order)) {
                
    $orderColumns = array();
                
    $multiSortArguments = array();
                
                
    //$order enthält das oben gepostete Array mit den field/direction Paaren
                //$seletedRows enthält das erste Array mit den Zeilen die zu sortieren sind
                
    foreach ($order AS $o) {

                    
    //hier werden die für array_multisort benötigten Daten in Spaltenform zusammengebaut
                    
    foreach ($selectedRows AS $r) {
                        
    $orderColums[$o['field']][] = $r[$o['field']];
                    }
                    
                    
    //und an das Array welches call_user_func_array als Parameter übergeben werden drangehängt
                    
    $multiSortArguments[] = &$orderColums[$o['field']];
                    
                    
    //hier wird die Sortierichtung an das Parameterarray angehängt damit array_multisort immer ein Array+Sortierkriterium Päärchen bekommt
                    
    if ($o['direction'] == 'asc') {
                        
    $multiSortArguments[] = \SORT_ASC;
                    }
                    if (
    $o['direction'] == 'desc') {
                        
    $multiSortArguments[] = \SORT_DESC;
                    }
                    
                } 
                
                
    //zuletzt wird das Originalarray angehängt um die Beziehungen $orderColums über den Schlüssel aufrecht zu erhalten
                
    $multiSortArguments[] = &$selectedRows;

                
    call_user_func_array('array_multisort'$multiSortArguments);
            } 
    $multiSortArguments hat beim Aufruf von call_user_func_array folgenden Inhalt:

    Code:
    array(3) {
      [0]=>
      &array(2) {
        [0]=>
        string(1) "3"
        [1]=>
        string(1) "4"
      }
      [1]=>
      int(3)
      [2]=>
      &array(2) {
        [0]=>
        array(4) {
          ["id"]=>
          string(1) "3"
          ["author"]=>
          string(4) "test"
          ["message"]=>
          string(11) "test text 3"
          ["date"]=>
          NULL
        }
        [1]=>
        array(4) {
          ["id"]=>
          string(1) "4"
          ["author"]=>
          string(4) "test"
          ["message"]=>
          string(11) "test text 4"
          ["date"]=>
          NULL
        }
      }
    }
    Nun wird eine Warning ausgegeben das array_multisort den zweiten Parameter (in diesem Beispiel also das SORT_DESC) als Referenz erwartet und schlägt somit fehl. Ich kann aber keine Referenz auf eine Konstante übergeben.

    Eine mögliche Lösung sieht folgendermaßen aus:

    PHP-Code:
                    if ($o['direction'] == 'asc') {
                        ${
    $o['field'].'_dir'} = \SORT_ASC;
                        
    $multiSortArguments[] = &${$o['field'].'_dir'};
                    }
                    if (
    $o['direction'] == 'desc') {
                        ${
    $o['field'].'_dir'} = \SORT_DESC;
                        
    $multiSortArguments[] = &${$o['field'].'_dir'};
                    } 
    Ich muss quasi für jedes Feld eine Variable anlegen und diese als Referenz in das Array eintragen welches an array_multisort als Funktionsparameter übergeben wird. Ein Array kann ich für die Zwischenspeicherung der Sortierrichtungen nicht nutzen da dieses auch nur wieder skalare Werte enthalten würde die ich nicht als Referenz übergeben kann.

    Mit den variablen Variablen geht es aber ich finde diese Lösung unschön.

    Jemand eine Idee wie es anders geht oder woran es liegen kann das array_multisort SORT_ASC/DESC bei einem Aufruf via call_user_func_array nur als Referenz annimmt und nicht wie beim normalen Aufruf als Wert der Konstanten?
    "Alles im Universum funktioniert, wenn du nur weißt wie du es anwenden musst".


  • #2
    Zitat von Dark Guardian Beitrag anzeigen
    Nun wird eine Warning ausgegeben das array_multisort den zweiten Parameter (in diesem Beispiel also das SORT_DESC) als Referenz erwartet und schlägt somit fehl.
    Bitte die genaue Meldung zitieren.

    array_multisort erwartet laut Beschreibung die Arrays als Referenzen, die Sort-Flags aber nicht.

    Kommentar


    • #3
      Zitat von ChrisB Beitrag anzeigen
      Bitte die genaue Meldung zitieren.

      array_multisort erwartet laut Beschreibung die Arrays als Referenzen, die Sort-Flags aber nicht.
      Genau das hat mich ja so kalt erwischt...

      Die genaue Meldung:

      Warning: Parameter 2 to array_multisort() expected to be a reference, value given in C:\xampp\htdocs\quiz_guestbook\classes\db\CSVSourc e.php on line 125
      Line 125 ist der Aufruf von call_user_func_array.

      Parameter 2 ist dem var_dump() von $multiSortArguments nach eben das Sort-Flag.
      "Alles im Universum funktioniert, wenn du nur weißt wie du es anwenden musst".

      Kommentar


      • #4
        Seltsames Verhalten. Aber variable Variablen brauchst du für einen Workaround nun wirklich nicht. Ich würde es so versuchen:

        PHP-Code:
        $ASC SORT_ASC;
        $DESC SORT_DESC;
        // und in deiner Verzweigung:
        multiSortArguments[] =& $ASC;
        // bzw:
        multiSortArguments[] =& $DESC
        @fschmengler - @fschmengler - @schmengler
        PHP Blog - Magento Entwicklung - CSS Ribbon Generator

        Kommentar


        • #5
          Zitat von fab Beitrag anzeigen
          Seltsames Verhalten. Aber variable Variablen brauchst du für einen Workaround nun wirklich nicht. Ich würde es so versuchen:
          Stimmt. Sollte funktionieren. So spar ich mir die variablen Variablen aber warum sich die beiden Funktionen in Kombination so verhalten tät mich dennoch interessieren.
          "Alles im Universum funktioniert, wenn du nur weißt wie du es anwenden musst".

          Kommentar


          • #6
            Auch wenn ich im Zeitalter der Datenbanken das Vorhaben etwas sinnfrei finde, fand ich die Aufgabe reizvoll. Hab mal was zusammengehackt, vielleicht kannst DU da was rausziehen:
            PHP-Code:
            <?php


            class CsvSet 
              
            {
              public function 
            __construct ($aSet $aFields $sSeparator)
                {
                
            $this->_sDump implode ($sSeparator $aSet);

                foreach (
            $aFields as $iKey => $sName)
                  {
                  
            $this->$sName $aSet[$iKey];
                  }
                }



              public function 
            __toString ()
                {
                return (
            $this->_sDump);
                }
              }


            class 
            CsvSetUserSort
              
            {
              public function 
            __construct ($aFields)
                {
                
            $this->aOrderSettings $aFields;
                }



              public function 
            perform (CsvSet $oFirst CsvSet $oSecond)
                {
                if ((string) 
            $oFirst === (string) $oSecond) return (0);


                
            $aOrderFlags = array ('asc' => -'desc' => 1);

                foreach (
            $this->aOrderSettings as $sName => $sOrder)
                  {
                  if (
            $oFirst->$sName !== $oSecond->$sName
                    {
                    
            $sOrder strtolower ($sOrder);

                    return (
            $oFirst->$sName $oSecond->$sName 
                              
            $aOrderFlags[$sOrder
                              : 
            $aOrderFlags[$sOrder] * -1
                           
            );
                    }
                  }
                  
                return (
            0);
                }
              }




            $sSeparator '|';


            $rHandle fopen('test.csv' 'r');

            $aFields = array ('id' 'name' 'content');

            while (
            false !== $aSet fgetcsv ($rHandle 1000$sSeparator)) 
              {
              
            $aSets[] = new CsvSet ($aSet $aFields $sSeparator);
              }

            fclose ($rHandle);


            $test = new CsvSetUserSort (array ('id' => 'desc' 'content' => 'desc'));

            var_dump ($aSets);

            usort ($aSets , array ($test 'perform'));

            var_dump ($aSets);
            Der Vergleich über den implode-String ist nicht zwingend nötig, könnte aber sicher aus Performancegründen für lange Listen sinnvoll sein. Abweichungen zwischen Feldnamen-Zuweisungen zu bestehdnen Daten und zwischen Feldnamen in der Sortierabweisung werden nicht abgefangen. Lediglich case-insensitivität für ASC/DESC habe ich mal eingebaut. Ist nur ein Funktionsbeispiel, wie gesagt.

            Funktionalität ist jetzt nicht groß getestet, vor allem nicht für Multibyte-Strings.

            [edit]
            Schöner wäre natürlich so ein Layout:

            PHP-Code:
            $aData = new CsvDatabase ($aFields);

            while (
            false !== $aSet fgetcsv ($rHandle 1000$sSeparator)) 
              {
              
            $aData->addSet ($aSet);
              }

            $aData->sortBy (array ('id' => 'desc' 'content' => 'desc'));
            var_dump ($aData); 
            --

            „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


            • #7
              Auch wenn ich im Zeitalter der Datenbanken das Vorhaben etwas sinnfrei finde, fand ich die Aufgabe reizvoll.
              Naja, prinzipiell finde ich das Sortieren von Datensätzen aus Textdateien nun nicht unbedingt sinnfrei. Abhängig vom Anwendungsfall würde ichs agen. In meinem Fall wäre es auch nicht unbedingt nötig, aber ich finde solche Dinge halt interessant.

              Dein Ansatz hat jedenfalls was. Ich hab derzeit eine Klasse CSVSource welche das auslesen und sortieren in einer Methode durchführt.

              Ich hab irgendwie ein Talent dafür All-In-One Methoden und Klassen zu kreieren. Wenn ich Implementierungen von anderen sehe habe ich zumindest meistens das Gefühl meine Klassen/Methoden machen zuviel.

              Naja, ich werds ja sehen wenn ichs gegen Ende August abgebe...

              Den Beitrag markier ich als Erledigt. Das Verhalten lässt sich scheinbar nciht so hone weiteres erklären. Google findet dazu zwar auch ein paar Dinge, diese beziehen sich aber auf die Referenzen der Arrays und nicht der Sort-Flags.
              "Alles im Universum funktioniert, wenn du nur weißt wie du es anwenden musst".

              Kommentar


              • #8
                Aha! Ich hatte vorhin noch etwas weitergebastelt. Ich kanns ja hier noch posten. Alles wie gehabt - ist nicht kommentiert und lässt wesentliche Fehlerkontrollen vermissen:
                PHP-Code:
                <?php


                class CsvSet 
                  
                {
                  public function 
                __construct ($aSet $aFields $sSeparator)
                    {
                    
                $this->_sDump implode ($sSeparator $aSet);

                    foreach (
                $aFields as $iKey => $sName)
                      {
                      
                $this->aFields[$sName] = $aSet[$iKey];
                      }
                    }



                  public function 
                __toString ()
                    {
                    return (
                $this->_sDump);
                    }




                  public function 
                getFields ($sName null)
                    {
                    if (
                null === $sName)
                      {
                      return (
                $this->aFields);
                      }

                    if (isset (
                $this->aFields[$sName]))
                      {
                      return (
                $this->aFields[$sName]);
                      }

                    return (
                null);
                    }
                  }



                class 
                CsvSetUserSort
                  
                {
                  public function 
                __construct ($aFields)
                    {
                    
                $this->aOrderSettings $aFields;
                    }



                  public function 
                perform (CsvSet $oFirst CsvSet $oSecond)
                    {
                    if ((string) 
                $oFirst === (string) $oSecond) return (0);


                    
                $aOrderFlags = array ('asc' => -'desc' => 1);

                    foreach (
                $this->aOrderSettings as $sName => $sOrder)
                      {
                      
                $sValueFirst  $oFirst->getFields ($sName);
                      
                $sValueSecond $oSecond->getFields ($sName);

                      if (
                $sValueFirst !== $sValueSecond
                        {
                        
                $sOrder strtolower ($sOrder);

                        return (
                $sValueFirst $sValueSecond 
                                  
                $aOrderFlags[$sOrder
                                  : 
                $aOrderFlags[$sOrder] * -1
                               
                );
                        }
                      }
                      
                    return (
                0);
                    }
                  }




                class 
                CsvDatabase
                  
                {
                  public function 
                __construct ($aFields $sSeparator)
                    {
                    
                $this->aFields    $aFields;
                    
                $this->sSeparator $sSeparator;
                    }



                  public function 
                addSet (array $aSet)
                    {
                    
                $this->aSets[] = new CsvSet ($aSet $this->aFields $this->sSeparator);
                    }


                  public function 
                getHeaders ()
                    {
                    return (
                $this->aFields);
                    }

                  public function 
                getSets ()
                    {
                    return (
                $this->aSets);
                    }


                  public function 
                sortBy ($aFields)
                    {
                    
                usort ($this->aSets , array (new CsvSetUserSort ($aFields) , 'perform'));

                    return (
                $this->aSets);
                    }
                  }



                class 
                CsvTableView
                  
                {
                  public function 
                __construct (CsvDatabase $aData $aSorting = array ())
                    {
                    
                $this->aData    $aData;
                    
                $this->aSorting $aSorting;
                    }


                  public function 
                __toString ()
                    {
                    
                $aIcons = array ('asc' => 'asc' 'desc' => 'desc');
                    
                    
                $sContent '<table border="1"><tr>';
                    foreach (
                $this->aData->getHeaders () as $sField)
                      {
                      
                $sContent .= '<th>' $sField;
                      if (isset (
                $this->aSorting[$sField]))
                        {
                        
                $sContent .= ' [' . (array_search ($sField array_keys ($this->aSorting)));
                        
                $sContent .= ':' $aIcons[strtolower ($this->aSorting[$sField])] . ']';
                        }
                      
                $sContent .= '</th>';
                      }

                    
                $sContent .= '</tr>';
                    
                    foreach (
                $this->aData->getSets () as $aSet)
                      {
                      
                $sContent .= '<tr><td>' .
                                   
                implode ('</td><td>' $aSet->getFields ()) .
                                   
                '</td></tr>';
                      }

                    
                $sContent .= '</table>';

                    return (
                $sContent);
                    }
                  }



                $sSeparator '|';
                $aFields    = array ('id' 'name' 'content');

                $aData = new CsvDatabase ($aFields $sSeparator);


                $rHandle fopen('test.csv' 'r');


                while (
                false !== $aSet fgetcsv ($rHandle 1000$sSeparator)) 
                  {
                  
                $aData->addSet ($aSet);
                  }

                fclose ($rHandle);


                echo new 
                CsvTableView ($aData);

                $aSorting = array ('id' => 'desc' 'content' => 'desc');
                $aData->sortBy ($aSorting);

                echo new 
                CsvTableView ($aData $aSorting);

                $aSorting = array ('name' => 'asc' 'id' => 'asc' 'content' => 'desc');
                $aData->sortBy ($aSorting);

                echo new 
                CsvTableView ($aData $aSorting);
                --

                „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

                Lädt...
                X