Mit Regex Prepared Statement analysieren

Parantatatam

mag Cookies & Kekse
Hallo ihr da,

ich bastle gerade an einer Funktion, die mir aus einem Prepared Statement eine endgültige Abfrage generiert. Ihr kennt es bestimmt aus der MySQLi_STMT-Klasse. Da ich diese nicht verwenden will und kann, da ich einen Adapter baue, der mit verschiedenen Datenbanken auskommt. Dabei ist folgendes zu beachten: gesucht sollen Fragezeichen (?), welche allerdings nicht in einfachen oder doppelten Anführungszeichen stehen und welche nicht escapet sind (also kein Slash davor), wobei allerdings zu beachten ist, dass ein Slash vor einem Fragezeichen steht, auch escaped werden kann, dadurch aber auch das Fragezeichen nicht mehr escapet wird und wieder als Gegendstand der Suche zu betrachten ist. Außerdem soll nach benannten Variablen gesucht werden, welche den normalen Regeln eines Variablennamen entsprechen, allerdings durch einen Doppelpunkt eingeführt werden. Hier gilt auch wieder die Ausnahme mit den Anführungszeichen und der Maske.

Falls es möglich ist, wäre es auch schön, wenn nicht zwei oder mehr Variablen und/oder Fragezeichen und/oder Zahlen und/oder Zeichenketten (Werte in Anführungszeichen) hintereinander folgen ohne das eine anderes Sprachelement (beispielsweise AND) dazwischen ist.

Auch wenn das momentan so klingt, als wöllte ich diese Aufgabe gänzlich an euch weitergeben, will ich sagen, dass es nicht so ist. Das Problem meinerseits ist jedoch, dass ich schon daran scheitere Werte auszuschließen, die nicht dem Suchmuster entsprechen (er findet bei mir \?, "?", '?' und ?, obwohl er nur ? finden soll).

Grüße
 
Mit ([^\\]|\\\\|)? solltest du alle finden. Um auszuschließen das sie in einem String sind müsstest du in dem Teilstring vom Beginn bis zur Fundstelle des Regex nach " suchen und zählen ob eine gerade Anzahl davon vorhanden sind.
 
Hallo Sven,

dir sei versichert, dass ich kein Neuling hinsichtlich PHP bin und mir das PDO sehr wohl bekannt ist. Allerdings gibt es da, genauso wie bei MySQLi, einige Dinge die mir nicht so gefallen und deshalb von mir ausgebessert werden wollen, was allerdings leichter ist, wenn ich mir gleich eine saubere Lösung schreibe.
 
Ich kann PDO nur bedingt empfehlen. Wir sind im Praxisbetrieb über eigenartiges Verhalten gestoßen (nicht reproduzierbare Fehler bei Abfragen):
HY000
2014
Cannot execute queries while other unbuffered queries are active.
Consider using PDOStatement::fetchAll(). Alternatively, if your code
is only ever going to run against mysql, you may enable query
buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.

Und das ist wohl nicht wirklich ein eine LÖSUNG für ein Problem. Wir nutzen nun seit einiger Zeit einen eigenen jedoch syntaxisch an PDO angelehnten Datenbank Layer. PDO ist nicht zuletzt wegen dieses Verhaltens in meinen Augen für den Produktivbetrieb nicht reif.

Grüße BN
 
Hallo Sven,

an wen ging die Frage, ob er das Resultat seiner Arbeit Öffentlichkeit zur Einsicht bereit stellen wird? An BN oder an mich? Falls es um mich geht, so sei dir versichert, dass ich es nicht der Öffentlichkeit vorenthalten werde.
 
Hallo ihr da,

ich hätte erstmal eine Vorablösung zu meinem Problem, welche hinsichtlich des regulären Ausdruckes noch verbesserungswürdig ist, da zwar Fragzeichen, welche direkt auf ein Anführungszeichen folgen ignoriert werden, allerdings wenn das Fragezeichen im Kontext mit anderen Zeichen steht, wird das Fragezeichen wieder als Platzhalter erkannt. Aber ansonsten funktioniert meine Version gut. Bin natürlich für Vorschläge offen.
PHP:
<?php
function prepare($sql, array $parameters = array()) {
    $pattern = '/"[^"]*"|\'[^\']*\'|\\\?|\\\:[[:alpha:]_][[:alnum:]_]*/';
    $alias   = $sql;
    if(preg_match_all($pattern, $alias, $match, PREG_OFFSET_CAPTURE)) {
        foreach($match[0] as $replace) {
            $length = strlen($replace[0]);
            if($replace[0] === '\\')
                $alias = substr_replace($alias, str_repeat('-', $length + 1), $replace[1], $length + 1);
            else
                $alias = substr_replace($alias, str_repeat('-', $length), $replace[1], $length);
        }
    }
    $pattern = '/(?<!(\"|\'|\\\\))(\?|\:[[:alpha:]_][[:alnum:]_]*)/';
    if(preg_match_all($pattern, $alias, $match, PREG_OFFSET_CAPTURE)) {
        $placeholders = array();
        $i = 0;
        if(count($match[0]) !== count($parameters))
            throw new Exception('Number of variables doesn\'t match number of parameters in prepared statement');
        foreach($match[0] as $placeholder) {
            if($placeholder[0][0] === ':' && isset($parameters[($name = substr($placeholder[0], 1))]))
                $placeholders[] = array($placeholder[0], $placeholder[1], $parameters[$name]);
            elseif(isset($parameters[$i]))
                $placeholders[] = array($placeholder[0], $placeholder[1], $parameters[$i++]);
            else
                throw new Exception('Number of variables doesn\'t match number of parameters in prepared statement');
        }								
        $offset = 0;
        $length = 0;
        foreach($placeholders as $placeholder) {
            switch(gettype($placeholder[2])) {
                case 'boolean':
                    $replace = (string) (int) $placeholder[2];
                    break;
                case 'integer':
                    $replace = (string) $placeholder[2];
                    break;
                case 'double':
                case 'float':
                    $replace = (string) $placeholder[2];
                    break;
                case 'string':
                    $replace = '"' . $placeholder[2] . '"';
                    break;
                case 'array':
                case 'object':
                    $replace = '"' . serialize($placeholder[2]) . '"';
                    break;
                case 'resource':
                    $replace = (string) (int) $placeholder[2];
                    break;
                case 'NULL':
                default:
                    $replace = 'NULL';
                    break;
            }
            $length  = strlen($placeholder[0]);
            $sql     = substr_replace($sql, $replace, $placeholder[1] + $offset, $length);
            $offset += strlen($replace) - $length;
        }
    }		
    return $sql;
}
?>

Edit: Ich habe jetzt eine Lösung gefunden für das noch bestehende Problem: ein zweiter regulärer Ausdruck, welcher den zweiten Ausdruck vorbereitet in dem es die ungewollten Teile "schwärzt", besser gesagt jedes Zeichen durch einen Bindestrich ersetzt. Somit wird immernoch die richtige Position der anderen Teile gefunden, aber eben nur noch die, der gewollten.
 
Zuletzt bearbeitet:
Ich habe eine Frage zu deinem ursprünglichen Problem, warum du überhaupt auf die Idee gekommen bist, sowas - nicht-triviales - zu stemmen:

Was an PDO hat dir nicht gefallen? Ich meine, wo gibt es Fallstricke, die mir bisher noch nicht über den Weg gekommen sind?

Nur damit ich darauf vorbereit bin, und das es mal jemand dokumentiert hat.
 
Zurück