~~DISCUSSION~~ ======[PHP] YPDOStmt: Erweiterung der PDOStatement Klasse====== Man kennt das Problem. Mit PDO kann man sehr schön mit bindParam() oder bindValue() arbeiten. Jedoch zum debugen ist das nicht gerade der Luxus, da man sich das SQL-Statement nicht ausgeben lassen kann (siehe [[:php:tutorials:debugmysqliqueries]]]). Wenn man also ein test-SQL haben will, dann muss man das schon selber parsen. Dazu kann man die PDOStatement Klasse erweitern. Hier ist mal mein Versuch. Die Beschreibungen befinden sich im Code. =====Anwendungsbeispiele===== Um zu zeigen was das Ding macht, beginne ich mit Anwendungsbeispielen. Der Code selber befindet sich am Schluss der Seite. ====Mit ? Parametern indexieren==== ===Initializieren=== Erstellen des Statements ist eigentlcih wie normal. Einfach vorher dem PDO-Objekt die eigene Statement-Klasse zuordnen setAttribute(PDO::ATTR_STATEMENT_CLASS, array('YPDOStmt', array($pdo))); //SQL-Statement mit ? Parametern $sql_indexed = << ===Beispiele des indexierten SQLs=== ==Beispiel 1) Indexierte Paramter == Indexiert und mit einzelner Parameterbindung. Ausgebenes SQL: letzter Lauf try{ $stmt = $pdo->prepare($sql_indexed); $stmt->bindValue(1, $idsS, PDO::PARAM_STR); //Als Value $stmt->bindParam(2, $id, PDO::PARAM_INT); //und als Bind Variable $stmt->execute(); //Das ausgeführte SQL anzeigen echo $stmt->executedSql; } catch(Exception $e){ echo $e->getMessage(); echo nl2br($e->getTraceAsString()); } SELECT t.ID_tipp, t.username, t.ID_partien, '319,320,321' FROM tipps_1213 t WHERE FIND_IN_SET(t.ID_partien, '319,320,321') AND t.punkte='1' ==Beispiel 2) Bind Variable== Nach der Asuführung des ersten Beispiels, beide Variablen mal ändern. Nur die Bind-Variable ($id) wird neu übernommen. Ausgebenes SQL: letzter Lauf try{ $idsS = 'A,B,C'; $id = 5; $stmt->execute(); //Das ausgeführte SQL anzeigen echo $stmt->executedSql; } catch(Exception $e){ echo $e->getMessage(); echo nl2br($e->getTraceAsString()); } SELECT t.ID_tipp, t.username, t.ID_partien FROM tipps_1213 t WHERE FIND_IN_SET(t.ID_partien, '319,320,321') AND t.punkte='5' ==Beispiel 3) SQLgeparstes SQL ausgeben ohne es auszuführen== Bind-Variable nochmal anpassen und nur das SQL ausgeben ohne auszuführen try{ $id = 999; //Das SQL anzeigen ohne auszuführen echo $stmt->getSql(); } catch(Exception $e){ echo $e->getMessage(); echo nl2br($e->getTraceAsString()); } SELECT t.ID_tipp, t.username, t.ID_partien FROM tipps_1213 t WHERE FIND_IN_SET(t.ID_partien, '319,320,321') AND t.punkte='999' ====Mit benannten Parametern==== ===Initializieren=== //Connection erstellen. $pdo ist das PDO-Objekt require_once 'connectPDO.php'; //YPDOStmt einbinden require_once 'YPDOStmt.php'; // ! WICHTIG ! PDO-Statementklasse durch die YPDOStmt-Klasse ersetzen $pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('YPDOStmt', array($pdo))); //SQL-Statement mit benamsten Parametern $sql_named = << ===Beispiel=== Benannte Variablen, Valueübergabe im execute(). Ausgebenes SQL: letzter Lauf try{ $stmt = $pdo->prepare($sql_named); $stmt->execute(array(':ids'=>$idsS, ':id'=>$id)); //Das ausgeführte SQL anzeigen echo $stmt->executedSql; } catch(Exception $e){ echo $e->getMessage(); echo nl2br($e->getTraceAsString()); } SELECT t.ID_tipp, t.username, t.ID_partien, '319,320,321' FROM tipps_1213 t WHERE FIND_IN_SET(t.ID_partien, '319,320,321') AND t.punkte='1' ====Mit Fehler im SQL-Statement==== ===Initializieren=== Hier baue ich ein Fehler ins SQL-Statement ein. Zum Debuggen möchte ich das generierte SQL-Statement haben. Das Error-Object aus YPDOStmt beinhaltet das bereits im Fehlertext setAttribute(PDO::ATTR_STATEMENT_CLASS, array('YPDOStmt', array($pdo))); //SQL-Statement mit Fehler. vor der letzten Spalte fehlt ein Komma $sql_named = << ===Beispiel=== Ich führe das Statement extra ohn echo aus, damit die Ausgabe wirklich nur aus dem Error-Object stammt. try{ $stmt = $pdo->prepare($sql_named); $stmt->execute(array(':ids'=>$idsS, ':id'=>$id)); } catch(Exception $e){ echo $e->getMessage(); echo nl2br($e->getTraceAsString()); } Und die Fehlerausgabe. Das SQL kann man analog zu [[php:tutorials:debugmysqlqueries]] in phpMyAdmin oder HeidiSQL testen. SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.ID_partien FROM tipps_1213 t WHERE FIND_IN_SET(t.ID_partien, '319,320,321') ' at line 1 parsed SQL: SELECT t.ID_tipp, t.username t.ID_partien FROM tipps_1213 t WHERE FIND_IN_SET(t.ID_partien, '319,320,321') AND t.punkte='1' #0 C:\xampp\htdocs\test\test4.php(41): YPDOStmt->execute(Array) #1 {main} =====Code===== Der COde der Klassen setAttribute(PDO::ATTR_STATEMENT_CLASS, array('YPDOStmt', array($pdo))); */ class YPDOStmt extends PDOStatement{ /** * Error-Format(printf). Parameter 1 => PDO-Fehlermeldung, Parameter 2 => SQL-Statement * @var String */ const C_DEFAULT_ERROR_FORMAT = '%1$s
parsed SQL:

%2$s

'; public $error_format = self::C_DEFAULT_ERROR_FORMAT; /** * zuletzt ausgeführtes SQL * @var String */ public $executedSql; protected $params = array(); protected $pdo; /** * @see PDOStatement::__construct() */ protected function __construct($pdo) { $this->pdo = $pdo; } /** * bindParam überschreiben, damit der Parameter gespeichert wird. * @see PDOStatement::bindParam() */ public function bindParam($parameter, &$variable, $data_type = null , $length = null, $driver_options = null){ parent::bindParam($parameter, $variable, $data_type, $length, $driver_options); if(is_numeric($parameter)) $parameter = $parameter-1; $this->params[$parameter] = new YPDOParam($this->pdo, $parameter, $variable, $data_type, $length, $driver_options); } /** * bindValue überschreiben, damit der Parameter gespeichert wird. * @see PDOStatement::bindValue() */ public function bindValue($parameter, $value, $data_type = null){ parent::bindValue($parameter, $value, $data_type); if(is_numeric($parameter)) $parameter = $parameter-1; $this->params[$parameter] = new YPDOParam($this->pdo, $parameter, $value, $data_type); } /** * execute überschreiben, damit das SQL-Statement mit den Aktuellen Variableninhalten gespeichert wird * Die PDOException wird um das SQL-Staement erweitert * @see PDOStatement::execute() */ public function execute($input_parameters = null){ //Es wurden Parameter mitgegeben. Diese also noch erfassen if(is_array($input_parameters)){ $this->params = array(); foreach(array_keys($input_parameters) as $key){ //$input_parameters[$key] anstelle von $value aus einer normalen foreach() übergeben, //da das YPDOParam eine Refernz erstellt. Ansonsten hätten am Ende alle Parameter den Wert des letzten Values $this->params[$key] = new YPDOParam($this->pdo, $key , $input_parameters[$key]); } } //SQL genereiren und speichern $this->executedSql = $this->getSql(); //parent->execute ausführen try{ parent::execute($input_parameters); }catch (PDOException $e){ //Den Fehler um das SQL-Statement erweitern $msg = sprintf($this->error_format, $e->getMessage(), $this->executedSql); throw new PDOException($msg, (int)$e->getCode(), $e); } } /** * SQL-Statement mit den aktuellen Variabelninhalten erstellen * @return String */ public function getSql(){ $sql = $this->queryString; if(count($this->params) > 0){ reset($this->params); //Der Query-String hat ? als Platzhalter if(is_numeric(key($this->params))){ foreach($this->params as $key => &$param){ $sql = preg_replace('/\?/', $param->getValue(), $sql, 1); } //Der Query-String hat Variablen als Platzhalter }else{ foreach($this->params as $key => $param){ $sql = preg_replace("/(?getValue(), $sql); } } } return $sql; } /** * Gibt alle Parameters zurück * @return Array */ public function getParams(){ return $this->params; } /** * gibt einen einzelnen Parameter zurück * @param Key oder Index * @return YPDOParam */ public function getParam($iKey){ return $this->params[$iKey]; } } /** * Parameter-Klasse mit allen Informationen zu PDO-Bind Params * @author Stefan Erb */ class YPDOParam{ protected $pdo; /** * @param PDO PDO-DB-Connection * @see PDOStatement::bindParam() */ public function __construct(&$pdo, $parameter, &$variable, $data_type = null , $length = null, $driver_options = null){ $this->pdo = &$pdo; $this->parameter = $parameter; $this->variable = &$variable; $this->data_type = $data_type; $this->length = $length; $this->driver_options = $driver_options; } /** * @return Query-String mit PDO geparst. Nimmt die Aktuellen Werte der referenzierten Variable */ public function getValue(){ return $this->pdo->quote($this->variable, $this->data_type); } } ?>
{{tag>PHP MySQL Library}}