Pit: mysql: Umstieg von mysql_ auf PDO

Hallo,

ich steige mit und mit von mysql_ auf PDO um. Nun habe ich zum einfügen in mysql eine Funktion geschrieben, die einzusetzende Werte prüft und ggf. korrigiert, rauswirft oder kontextgerecht behandeln soll.

function cleanMyInput($Variable, $Type)
  {
   switch($Type)
    {
     case 'int':
     settype($Variable, 'Integer');
                        break;
                        case 'string':
                        $Variable = mysql_real_escape_string($Variable);
                        break;
     usw.

Beispielquery:


$query = UPDATE table SET 
A = '".cleaninput($eingabeA,'string')."', 
B = '".cleaninput($eingabeB,'string')."' 
WHERE ID = ".cleaninput($eingabeID,'int').";

Und genau hier habe ich dann mein PDO-Problem. Meine einzusetzenden Strings werden in PDO nicht behandelt, anders als in mysql_.

Meine Suche nach einem PDO-Äquivalent ergaben, dass ich die Query hierzu umschreiben müßte, was ich ansich nicht wirklich wollte.

Daher meine Frage, ob es einen Ersatz für mysql_real_escape_string gibt, der meine Query unverändert lassen kann?

Pit

  1. Tach!

    Meine einzusetzenden Strings werden in PDO nicht behandelt, anders als in mysql_.

    Der Satz hat Interpretationsspielraum. Meinst du, dass es in PDO nicht nötig sei, oder dass deine Funktion nicht mehr richtig arbeitet?

    Meine Suche nach einem PDO-Äquivalent ergaben, dass ich die Query hierzu umschreiben müßte, was ich ansich nicht wirklich wollte.

    Prinzipiell kann PDO auch selbst erstellte Querys verarbeiten. Prepared Statements sind nicht zwingend nötig. Allerdings sollten sie trotzdem bevorzugt werden, weil man sich das Quoten und Maskieren und vielleicht noch Typumwandeln sparen kann, und das am Ende auch noch prinzipbedingt keine SQL-Injection zulässt.

    Daher meine Frage, ob es einen Ersatz für mysql_real_escape_string gibt, der meine Query unverändert lassen kann?

    PDO hat auch für händisch erstellte Querys eine Funktion, namens PDO::quote(). Anders als die mysql(i)-Funktionen quotiert diese Funktion auch und maskiert nicht nur.

    dedlfix.

    1. Hi,

      Der Satz hat Interpretationsspielraum. Meinst du, dass es in PDO nicht nötig sei, oder dass deine Funktion nicht mehr richtig arbeitet?

      Letzteres.

      Prinzipiell kann PDO auch selbst erstellte Querys verarbeiten. Prepared Statements sind nicht zwingend nötig. Allerdings sollten sie trotzdem bevorzugt werden, weil man sich das Quoten und Maskieren und vielleicht noch Typumwandeln sparen kann, und das am Ende auch noch prinzipbedingt keine SQL-Injection zulässt.

      Nutze ich auchab und an, aber nicht immer und für diesen Fall gilt meine Frage.

      Daher meine Frage, ob es einen Ersatz für mysql_real_escape_string gibt, der meine Query unverändert lassen kann?

      PDO hat auch für händisch erstellte Querys eine Funktion, namens PDO::quote(). Anders als die mysql(i)-Funktionen quotiert diese Funktion auch und maskiert nicht nur.

      Und wie sähe dann die Beispielquery aus?

      Pit

      1. Tach!

        Und wie sähe dann die Beispielquery aus?

        Du nimmst $was_auch_immer_dein_PDO_objekt_ist->quote() statt mysql_real_escape_string() und lässt die Anführungszeichen weg.

        Am Ende muss sich ein syntaktisch korrektes SQL-Statement entstehen. Das kannst du ja zur Kontrolle ausgeben lassen, auch für den Fall, dass einfache und doppelte Anführungszeichen in der Eingabe sind.

        dedlfix.

        1. Hi dedlfix,

          Du nimmst $was_auch_immer_dein_PDO_objekt_ist->quote() statt mysql_real_escape_string() und lässt die Anführungszeichen weg.

          Ich werds mal ausprobieren und melde mich dann bei Bedarf nochmal wieder, ok? Wird aber frühestens morgen früh werden, danke erstmal für die Hilfe.

          Pit

          1. Hi dedlfix,

            Du nimmst $was_auch_immer_dein_PDO_objekt_ist->quote() statt mysql_real_escape_string() und lässt die Anführungszeichen weg.

            Ok, das funktioniert. Ein Problem bleibt das trotzdem: Da ich sukzessiv auf PDO umstellen möchte, bleibt mir vermutlich nichts anderes übrig, als die Funktion umzubenennen und scripteweise umzustellen, da ansonsten "doppelt gequotet" wird, oder? Soll heißen, jedes script, bei dem ich die Anführungszeichen nicht weglasse und dennoch quote() anwende, wird einen Fehler ergeben. Oder kennst Du hierfür einen Ausweg?

            Pit

            1. Tach!

              Da ich sukzessiv auf PDO umstellen möchte, bleibt mir vermutlich nichts anderes übrig, als die Funktion umzubenennen und scripteweise umzustellen, da ansonsten "doppelt gequotet" wird, oder?

              Ja.

              Soll heißen, jedes script, bei dem ich die Anführungszeichen nicht weglasse und dennoch quote() anwende, wird einen Fehler ergeben. Oder kennst Du hierfür einen Ausweg?

              Du kannst die Funktion ganz weglassen, wenn du auf Prepared Statements umstellst. Das wäre mein bevorzugter Weg, dass man die Angelegenheit gleich mal richtig modernisiert.

              Sie für beide Varianten zu schreiben bringt es nicht. Du müsstest irgendwie unterscheiden, ob es ein Alt-Aufruf oder ein neuer ist. Um den Alt-Code nicht ändern zu müssen, wäre das ein optionaler Parameter, der im Defaultfall dafür sorgt, dass entweder der alte Zweig aufgerufen wird, oder dass du die doppelten Quotes eliminierst. Das bedeutet aber auch, dass für alle Neu-Verwendungen dier Parameter gesetzt sein muss. Und wenn du mit dem Umschreiben komplett fertig bist, ist er überflüssig.

              dedlfix.

              1. Hi dedlfix,

                Du kannst die Funktion ganz weglassen, wenn du auf Prepared Statements umstellst. Das wäre mein bevorzugter Weg, dass man die Angelegenheit gleich mal richtig modernisiert.

                Warum ist das eigentlich so, dass Prepared Statements das von Hause aus mitbringen?

                Sie für beide Varianten zu schreiben bringt es nicht. Du müsstest irgendwie unterscheiden, ob es ein Alt-Aufruf oder ein neuer ist. Um den Alt-Code nicht ändern zu müssen, wäre das ein optionaler Parameter, der im Defaultfall dafür sorgt, dass entweder der alte Zweig aufgerufen wird, oder dass du die doppelten Quotes eliminierst. Das bedeutet aber auch, dass für alle Neu-Verwendungen dier Parameter gesetzt sein muss. Und wenn du mit dem Umschreiben komplett fertig bist, ist er überflüssig.

                Meine Funktion cleanMyInput() nimmt ja als Parameter den Inhalt sowie den "Typ" entgegen, also int, string, bool, float, o.ä. Meine Idee war nun, schlicht einen Parameter stringPDO hiunzuzunehmen und dann sukkzessive die Scripte umzuschreiben. Und wenn ich fertig bin, kann der Parameter string komplett entfallen.

                Pit Ich dachte eher daran, schli

                1. Tach!

                  Warum ist das eigentlich so, dass Prepared Statements das von Hause aus mitbringen?

                  Das ist prinzipbedingt. Bei Prepared Statements geht zuerst das Statement mit Platzhaltern auf die Reise. Die Werte werden anschließend separat übertragen. Dabei brauchen sie keine Aufbereitung, weil sie ja nicht in den Text-Kontext des SQL-Statements gebracht werden. Du übergibst sie als Roh-Wert per Binding oder über den execute()-Parameter und der Datenbanktreiber kümmert sich um einen problemlosen Transport.

                  Es kann sein, dass PDO die Prepared Statements nur simuliert. (Bei den meisten SQL-Datenbanksystemen sind sie jedoch mittlerweile direkt nutzbar.) Aber dann ist es auch Aufgabe von PDO das Einfügen korrekt vorzunehmen, so dass du dich nicht weiter darum kümmern musst, als die Roh-Werte zu übergeben.

                  Meine Funktion cleanMyInput() nimmt ja als Parameter den Inhalt sowie den "Typ" entgegen, also int, string, bool, float, o.ä.

                  Den Typ kann man auch aus dem Wert in der Variable ermitteln, den muss man nicht händisch übergeben und dabei aufpassen, dass er zum Wert passt.

                  Meine Idee war nun, schlicht einen Parameter stringPDO hiunzuzunehmen und dann sukkzessive die Scripte umzuschreiben. Und wenn ich fertig bin, kann der Parameter string komplett entfallen.

                  Dein Problem ist, dass du nur für die Übergangsphase eine Unterscheidung zwischen Alt- und Neu-Vorgehen brauchst. Oder du musst entweder vorher alle Alt-Aufrufe anpassen oder hinterher alle Neu-Aufrufe.

                  Aber wenn ich mir das mal genau überlege, hast du dir durch diese Funktion gar keinen großen Vorteil erschaffen gegenüber dem direkten Behandeln an Ort und Stelle. Letzteres ist auch nur ein einzelner Funktionsaufruf. (Für Zahlen kann man mit intval() statt settype() einen Integer erzwingen.)

                  dedlfix.

                  1. Hi,

                    danke für die Erklärungen zu den Prepared Statemenst.

                    Den Typ kann man auch aus dem Wert in der Variable ermitteln, den muss man nicht händisch übergeben und dabei aufpassen, dass er zum Wert passt.

                    Ja, aber ganu das will ich doch gar nicht. Wenn ich einen Integer erwarte, will ich aus einem übergebenen String doch nicht herauslesen, dass es ein String ist, oder? Sondern ich wandle in einen Integer um, der im Zweifelsfall den wert 0 annimt.

                    Aber wenn ich mir das mal genau überlege, hast du dir durch diese Funktion gar keinen großen Vorteil erschaffen gegenüber dem direkten Behandeln an Ort und Stelle.

                    Stimmt fast. Denn in meiner Funktion kann ich ja noch viel mehr mach, bspw. MIN und MAX Werte festlegen, festlegen, ob ein float-Wert nur positiv oder auch negativ sein darf, einen String auf Zeichen untersuchen, die ich haben oder nicht haben will, usw.

                    (Für Zahlen kann man mit intval() statt settype() einen Integer erzwingen.)

                    Worin liegt der Unterschiede der beiden Funktionen eigentlich?

                    Pit

                    1. Tach!

                      Wenn ich einen Integer erwarte, will ich aus einem übergebenen String doch nicht herauslesen, dass es ein String ist, oder? Sondern ich wandle in einen Integer um, der im Zweifelsfall den wert 0 annimt.

                      Ja, kann man mit einem einfachen intval() erledigen. Aber andererseits schadet es auch nicht, wenn du einen (syntaktisch korrekten) String zum DBMS schickst, auch wenn das nur einen Zahlenwert erwartet. Die Konvertierung (zu notfalls 0 oder NULL) findet dann im DBMS statt.

                      Ob 0 oder NULL passende Werte sind, kannst du eh nicht mit einer globalen Funktion entscheiden, das muss fachlich je Fall entschieden werden.

                      Aber wenn ich mir das mal genau überlege, hast du dir durch diese Funktion gar keinen großen Vorteil erschaffen gegenüber dem direkten Behandeln an Ort und Stelle.

                      Stimmt fast. Denn in meiner Funktion kann ich ja noch viel mehr mach, bspw. MIN und MAX Werte festlegen, festlegen, ob ein float-Wert nur positiv oder auch negativ sein darf, einen String auf Zeichen untersuchen, die ich haben oder nicht haben will, usw.

                      Ja, fachliche Prüfungen. Sind aber kaum mit einer globalen eierlegenden Wollmilch-Prüffunktion sinnvoll abzuhandeln. Da sind kleinere Einheiten, die genau auf ein Thema spezialisiert sind, einfacher zu warten und zu verstehen.

                      (Für Zahlen kann man mit intval() statt settype() einen Integer erzwingen.)

                      Worin liegt der Unterschiede der beiden Funktionen eigentlich?

                      intval() arbeitet funktional, settype() hingegen prozedural. Das heißt, dass du den Rückgabewert von intval() gleich in einem Ausdruck verwenden kannst, settype() aber ein eigenständiger Aufruf sein muss. Außerdem wird die Variable umgeschrieben, bei intval() hingegen im Original belassen.

                      dedlfix.

                      1. Hi dedlfix,

                        vielen Dank für die Erklärungen, hat mir sehr geholfen.

                        Pit