Ankündigung

Einklappen
Keine Ankündigung bisher.

Encoding in SQL-LIKE-Statement durch PHP

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

  • Encoding in SQL-LIKE-Statement durch PHP

    Einen wunderschönen Abend wünsche ich allen,

    ich habe ein vermutlich recht simples Problem aber ich steh auf dem Schlauch. Eine zweistündige Recherche hat nichts ergeben und ich weiß einfach nicht mehr weiter.

    Folgender Usecase: Der Nutzer gibt einen String in ein Suchfeld ein und je nach bestimmten Parametern wird dann die passende SQL-Abfrage zusammengeschustert. Das funktioniert auch wunderbar, aber als ich "Benutzer" eingegeben habe, natürlich ohne "", ergab sich folgendes Problem:

    Statt einem
    Code:
    LIKE '%Benutzer%'
    (SQL)
    erhielt ich ein
    Code:
     LIKE '¾nutzer%'
    Offensichtlich wurde "%Be" zu "¾" umgewandelt. Es kommt dafür nur PHP in Frage. Ich finde keine Möglichkeit das zu verhindern (bin ich blöd? ).

    Eine der Zeilen um die es geht:
    PHP-Code:
    $where[] = $this->class->getTableName().".".$haystack[0]." LIKE '%".$needle."%'"
    Wobei $this-class->getTableName() logischerweise der Tabellenname und $haystack[0] der Spaltenname ist.

    Ich steh echt auf dem Schlauch, wenn irgendjemand eine Idee hat, wär ich wahnsinnig Dankbar. Wenn ihr noch Informationen benötigt, bin ich da.

    Liebe Grüße,
    Julian


  • #2
    http://krypted.com/utilities/html-encoding-reference/

    Und wir sollen jetzt raten wo die Unicodeumwandlung stattfindet?

    Kommentar


    • #3
      Hallo prostestix,

      das wäre wohl etwas zu viel verlangt ^^
      Meiner Meinung ist die Sache ganz einfach: $needle ist direkt davor "Benutzer".
      PHP-Code:
      $this->class->getTableName().".".$haystack[0]." LIKE '%".$needle."%'"
      Ist eben Fehlerhaft. Also bereits wenn ich das ausgebe. Ich würde, naiv, wie ich bin, behaupten, dass dort die Umwandlung statt findet.
      Oder bin ich da zu naiv?

      So wie ich das sehe, muss PHP ja scheinbar generell Strings entsprechend umwandeln, oder?
      Meine Frage kann ich auch allgemeiner formulieren: Wie kann ich verhindern, dass ein String entsprechend encodet wird?

      Noch dazu hab ich meinen Zeichensatz im Meta-Tag und im PHP header als utf8 definiert. ASCII ist doch auch ein Zeichensatz, oder? Dann sollte der String doch gar nicht mit ASCII sondern mit UTF-8 encodet werden, oder?

      Also falls letzteres zutreffen sollte, wäre das ja ein guter Ansatzpunkt. Entschuldigt manche Fragen, auf dem Gebiet der Kodierung kenne ich mich leider nicht sehr gut aus. Das ist Ausbaufähig.

      Grüße,
      Julian

      Kommentar


      • #4
        UTF-8 ist eine 8-Bit-Kodierung von Unicode, die zu ASCII abwärtskompatibel ist.

        Was passiert mit dem $where[]?
        Du verwendest das wohl in einer URL, dann musst du auch urlencode anwenden.
        Aber leider zeigst du ja nicht den ganzen Code, so dass es mehr Spekulation als Wissen ist.

        Kommentar


        • #5
          Weil der ganze Code auch nur Teil eines größeren Codes ist. Hmm.
          Ich kürze einfach nicht relevante Sachen raus und pack euch den gesamten Code mal rein. Ist besser so.

          PHP-Code:
          public function find($needle$haystacks)
              {
              
          $needle escape($needle);
              
          $where = array();
              
          $sql = array();
              foreach (
          $haystacks as $haystack) {
                  
          $classname $this->classname;
                  
          $class $this->class;
                  
          $haystack explode("."$haystack);
                  if (
          count($haystack) == 1) {
                      
          $where[] = $this->class->getTableName().".".$haystack[0]." LIKE '%".$needle."%'";
                  } else {
                      
          //Tiefer gehende Haystacks
                  
          }
              }
              
          $sql array_values(array_unique($sql));
              
          $sql[] = "SELECT ".$this->class->getTableName().".* FROM ".$this->class->getTableName()." WHERE ".implode(" OR ",$where);
              
          $sql "(".implode(") UNION ("$sql).")";
              
          $this->find $sql//Das SQL wird zwischengespeichert
              
          return $this;
              } 
          Diese Funktion dient dazu aus $nedle und mindestens einem Haystack ein Sucharray zu bauen. Das funktioniert auch, aber eben nicht mit bestimmten Suchbegriffen.
          $this->find wird noch zwei Mal im Iterator aufgerufen:
          PHP-Code:
          public function next()
              {
                  
          $this->loop_count++;
                  if (
          $this->loop_count == $this->maxitems) {
                      if (
          $this->getpage) {
                          
          $this->data = array();
                      } else {
                          
          $this->loop_count 0;
                          
          $this->loop_offset += $this->maxitems;
                          if (
          $this->find) { //Wenn das SQL gesetzt wird, wird $this->data mit unserem Find-SQL statt dem normalen gefüttert.
                              
          $this->data DataBaseController::executeSQL(
                                  
          $this->find." ".$this->getOrderBy()." LIMIT ".$this->loop_offset.", ".$this->maxitems
                              
          );
                          } else {
                              if (
          $this->ismm) {
                                  
          $this->data DataBaseController::executeSQL(
                                      
          "SELECT t2.* FROM ".$this->mmtablename." AS t1 INNER JOIN ".$this->class->getTableName(
                                      ).
          " AS t2 ON t1.".$this->mmconnectorname."id = t2.id ".$this->getWhere(
                                      ).
          " ".$this->getOrderBy()." LIMIT ".$this->loop_offset.", ".$this->maxitems
                                  
          );
                              } else {
                                  
          $this->data DataBaseController::executeSQL(
                                      
          "SELECT * FROM ".$this->class->getTableName()." ".$this->getWhere()." ".$this->getOrderBy(
                                      ).
          " LIMIT ".$this->loop_offset.", ".$this->maxitems
                                  
          );
                              }
                          }
                      }
                  }
              }

          public function 
          rewind()
              {
                  
          $this->loop_count 0;
                  
          $this->loop_offset $this->offset_override $this->offset_override 0;
                  if (
          $this->find) { //Wenn das SQL gesetzt wird, wird $this->data mit unserem Find-SQL statt dem normalen gefüttert.
                      
          $this->data DataBaseController::executeSQL(
                          
          $this->find." ".$this->getOrderBy()." LIMIT ".$this->loop_offset.", ".$this->maxitems
                      
          );
                  } else {
                      if (
          $this->ismm) {
                          
          $this->data DataBaseController::executeSQL(
                              
          $sql "SELECT t2.* FROM ".$this->mmtablename." AS t1 INNER JOIN ".$this->class->getTableName(
                                  ).
          " AS t2 ON t1.".$this->mmconnectorname."id = t2.id ".$this->getWhere()." ".$this->getOrderBy(
                                  ).
          " LIMIT ".$this->loop_offset.", ".$this->maxitems." "
                          
          );
                      } else {
                          
          $this->data DataBaseController::executeSQL(
                              
          "SELECT * FROM ".$this->class->getTableName()." ".$this->getWhere()." ".$this->getOrderBy(
                              ).
          " LIMIT ".$this->loop_offset.", ".$this->maxitems
                          
          );
                      }
                  }
              } 
          urlencode oder rawurlencode wird bisher im gesamten Projekt nicht verwendet.
          Das ist dann die gesamte Strecke von $this->find, $this->data wird dann noch im Iterator zerpflückt und dann passt das.

          Noch ein Beispiel für den find-Input:
          PHP-Code:
          $dos->find(
                  
          $string,
                  array(
                      
          "customer",
                      
          "related",
                      
          "minute_taker.username",
                      
          "present_staff.username",
                      
          "minute",
                      
          "conclusion",
                      
          "comments.title",
                      
          "comments.text",
                  )
              ); 

          Kommentar


          • #6
            Das beantwortet dann hoffentlich auch deine Frage nach dem $where[].
            So funktioniert das System:
            Die Models werden auf die Datenbank gemappt und durch "DataObject"s repräsentiert.
            In diesem Fall habe ich ein DataObjectSet ($dos) aus mehreren DataObjects einer Klasse. Dort möchte ich $string finden und zwar entweder direkt in der Klasse (customer, related, minute, conclusion) oder in deren Verknüpften Klassen (durch has_one, -many oder many_many Relationships) (minute_taker.username, present_staff.username, comments.title, comments.text).
            Dazu wird für ersteres ein Select geschrieben (Hier relevant) und für jede Relationship ebenfalls ein Select (hier nicht relevant, da nicht im Code) Diese werden dann per UNION zusammengeführt und in $this->find gespeichert.
            Wenn ich jetzt durch das DataObject iterate, wird statt den normalen Daten für die Klasse eben nur die Row zurückgegeben, die den Suchkriterien entsprechen.

            Und das funktioniert. Also die Code ist nicht kaputt. Ich finde die Sachen wunderbar. Nur, wenn es eben ein Suchstring ist, der beginnt wie ein ASCII Encoding-Schlüssel endet, wird das einfach replaced, da ich ja mit "LIKE '%Benutzer%'" suche.
            Ich hatte das Problem bei LIKE nur sonst noch nie (vielleicht auch Zufall) und ich finde auch im Web nichts darüber. Deshalb irritiert mich das so sehr.

            Kommentar


            • #7
              Zitat von Auran Beitrag anzeigen
              $this->find wird noch zwei Mal im Iterator aufgerufen:
              Nein, das sind keine Methodenaufrufe sondern nur Zugriffe auf eine Property, die genauso heißt.

              Zitat von Auran Beitrag anzeigen
              Noch ein Beispiel für den find-Input:
              PHP-Code:
              $dos->find(
              $string,
              array(
              "customer",
              "related",
              "minute_taker.username",
              "present_staff.username",
              "minute",
              "conclusion",
              "comments.title",
              "comments.text",
              )
              ); 
              Und woher kommt $string? Ist die Kodierung hier schon defekt?

              Kommentar


              • #8
                Nein, das sind keine Methodenaufrufe sondern nur Zugriffe auf eine Property, die genauso heißt.
                Davon sprach ich auch nicht. Ich meinte, dass ja der SQL-Code in der Property $this->find gespeichert wird und diese Property auch noch zwei Mal im Iterator aufgerufen wird.

                Und woher kommt $string? Ist die Kodierung hier schon defekt?
                Gute Richtung, das ist mir vorher gar nicht eingefallen: Das Ganze ist ja auch noch größer:
                • Ich habe ein Suchfeld mit einem Feld für die Stichwortsuche
                • Wird dies abgesendet, wird eine Tabelle mit Pagination erstellt, die die Daten bei Seitenwechsel selbst nachlädt.
                • Dazu wird $string = $_POST['string'] einer JS var übergeben (bei echo "%".$string."%"; kommt "%Benutzer%" raus also passt die Kodierung hier):
                PHP-Code:
                $table "<script type='text/javascript'>
                    var search_string = "
                .json_encode($string).";
                </script>"

                • In meinem Template initialisiere ich den Table dann mit
                HTML-Code:
                <script type="text/javascript">
                var table = new ActiveTable($('#table.activetable'), (typeof search_string !== "undefined" ? search_string : ""));
                </script>
                • Der Constructor der class ActiveTable nimmt "object" und "string" entgegen, also das jQuery Object des Tables und einen Search Query.
                • Zur Sicherheit habe ich hier ebenfalls "this.search = (string !== undefined ? string : "");"
                • Nachdem der Table soweit gebaut ist hole ich mir erstmal die Anzahl aller Einträge, was aber die gleiche Funktion triggert wie wenn ich die Einträge selbst hole:
                Code:
                ActiveTable.prototype.getCountFromModel = function () {
                    let $this = this;
                    let deferred = $.Deferred();
                    $.ajax({
                        type: 'post',
                        dataType: 'json',
                        url: ROOT + "rest/getCountFromModel",
                        data: {model: $this.model, search: $this.search}
                    }).done(function (data) {
                        deferred.resolve(data.count);
                    }).fail(function () {
                        deferred.reject();
                    });
                    return deferred.promise();
                };
                • Wenn ich am Endpunkt dieser Anfrage nun direkt echo "%".$_POST['search']."%"; mache, erhalte ich "ľnutzer%", also scheint die Kodierung hier bereits defekt zu sein, interessanterweise muss sich das aber nochmal ändern, schließlich erhalte ich am Ende noch ein ganz anderes Ergebnis
                • Als nächstes erstelle ich mir erstmal besagtes DataObjectSet und dann kommt wieder die Variable ins Spiel: Wenn das Model die Funktion "getSearchFromModel" aufweist, wird diese mit dem DataObjectSet und der Search-Variable aufgerufen.
                • Inhalt der Funktion ist dann eben dies hier:
                PHP-Code:
                public static function getSearchFromModel($dos$string)
                    {
                        
                $dos->find(
                            
                $string,
                            array(
                                
                "customer",
                                
                "related",
                                
                "minute_taker.username",
                                
                "present_staff.username",
                                
                "charged_offences.name",
                                
                "convicted_offences.name",
                                
                "minute",
                                
                "conclusion",
                                
                "comments.title",
                                
                "comments.text",
                            )
                        );

                        return 
                $dos;
                    } 
                • Ein echo an dieser Stelle ergibt wieder "ľnutzer%"
                • Tatsächlich habe ich dieses Ergebnis auch am Anfang der find Funktion, auch wenn ich am ende print_r($where); mache, aber sobald ich print_r($sql) mache bzw. print_r(implode("",$where); erhalte ich "¾nutzer%"
                • Könnte also das implode da nochmal was machen?
                So wie das für mich nun aussieht, kommt das Ganze dadurch, dass ich das durch JS schicke, oder? Das ist mir vorher gar nicht eingefallen, dass das daran liegen könnte. Könnt ihr mir da dennoch weiterhelfen auch wenn das dann JS ist?

                //Edit: Danke für die bisherige Hilfe, die genau das war was sie sein sollte, eine Hilfe zur Selbsthilfe. Ich hab dank euch wenigstens wieder Anhaltspunkte gefunden wodurch ich die Ursache isolieren konnte.

                Kommentar


                • #9
                  Warum schreibst du das so umständlich? Das ist doch ein vollkommen sinnloser und überflüssiger Wechsel.
                  PHP-Code:
                  $where[] = $this->class->getTableName().".".$haystack[0]." LIKE '%".$needle."%'"
                  Schreib das doch so!
                  PHP-Code:
                  $where[] = $this -> class -> getTableName() . ".{ $haystack[ 0 ] } LIKE '%{ $needle }%' "
                  Wieso heißen die Bezeichner Nadel und Heuhaufen? Ist das eine Bauernhofanwendung?

                  Warum wird DataBaseController::executeSQL statisch aufgerufen in einer OOP-Anwendung?

                  bitcoin.de <- Meine Freelancerwährung

                  Kommentar


                  • #10
                    Hallo Alpha,

                    Da ich erstere Schreibweise gewohnt bin, ist mir das übersichtlicher, weil ich es getrennter habe, was Text und was Var ist. Aber ich werd mal drüber nachdenken das vllt. umzustellen. Sieht rein visuell besser aus. Ändert an der Encyption allerdings nichts.

                    Eine Schreibweise, die ich mir von der PHP-Doku übernommen habe. Ich kann sie natürlich auch $searchstring und $searchitem nennen oder $potato und $banana. Ich habe mich jedoch für diese Bezeichnung entschieden.

                    Kommentar


                    • #11
                      Ich habe jetzt noch überprüft ob es mich weiterbringt, wenn ich bereits beim Ajax-Call die %% setze. Übertragen wird "%Benutzer%", aber es kommt eben direkt bei Seitenaufruf des Calls als "¾nutzer%" (jetzt wieder? Ich weiß nicht was das mit "ľnutzer%" und "¾nutzer%" auf sich hat) an.
                      Ich hab in den Request-Header gesehen, dort steht "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" und auch der Response Header lautet "Content-Type: text/html; charset=utf-8" (Type text/html, da ich ja was echo und kein reines JSON zurückgebe)

                      Ich werde jetzt mal das noch durch Postman durchjagen und schauen ob ich da noch eine Idee rausholen kann.

                      Kommentar


                      • #12
                        Lösung gefunden, kann übersprungen werden

                        Okay, was also interessant ist, ist, dass ich über Postman keine Probleme habe. Folgende Einstellungen habe ich:
                        • Post-Request
                        • die entsprechende URL
                        • der Body
                          • x-www-form-urlencoded
                          • "model" und "search" entsprechend gesetzt
                        • Als Header wird halt automatisch Content-Type: application/x-www-form-urlencoded
                        Und da kam gerade der Gedankenblitz: Ich habe bei meiner Ajax-Request contentType: 'application/x-www-form-urlencoded' gesetzt und es funktioniert.

                        Vorher war der Header also automatisch mit charset=utf-8, jetzt ohne.

                        Was aber total seltsam ist, ist ,dass bei meiner zweiten Request
                        Code:
                        ActiveTable.prototype.getItemsFromModel = function () {
                            let $this = this;
                            let deferred = $.Deferred();
                            $.ajax({
                                type: "POST",
                                dataType: "json",
                                url: ROOT + "rest/getItemsFromModel",
                                data: {
                                    model: $this.model,
                                    page: $this.currentpage,
                                    items: $this.itemsperpage,
                                    search: $this.search
                                }
                            }).done(function (data) {
                                deferred.resolve(data.items, data.count);
                        
                            }).fail(function () {
                                deferred.reject();
                            });
                            return deferred.promise();
                        };
                        Das Ganze mit utf-8 funktioniert. Obwohl es der selbe String ist, nichts daran geändert wurde und sich der Aufruf sonst auch nur durch zwei weitere Parameter unterschieden hat.

                        Anfang Lösung

                        Doch HALT: Ich habe gerade die Lösung gefunden und festgestellt, dass es sich doch um etwas anderes handelt: ich habe bei der ersten Request type: "post" geschrieben. Das muss aber scheinbar groß geschrieben werden, sonst hab ich die Probleme. Mit "POST" gibt sich das ganze.

                        Sorry, ich hätte nicht gedacht, dass das Problem bei JS bzw. jQuery lag, aber dann bin ich auch etwas schlauer. Ich danke euch nochmal, dass ihr mich richtig angestupst habt, ich war an einem Punkt angekommen wo ich einfach nicht mehr wusste wo ich noch weiter testen oder suchen sollte.

                        Kommentar

                        Lädt...
                        X