PDO et query exécuté

Mammouth du PHP | 568 Messages

13 avr. 2010, 15:17

Bonjour à tous,

Cela fait un moment que j'utilise PDO que je couple avec un mode DEBUG afin d'afficher toutes les requêtes exécuté dans un script, tout fonctionne bien sauf le principal à savoir comment récupérer la query généré par PDO avec les paramètres.

Avez-vous déjà fait ça?

Une idée?

Ci-dessous, la surcharge PDO que j'utilie pour sqlserver:
class mssqlPDO extends PDO {
		
		/*
		public function __autoload() { }		
		public function __construct() {	}		
		public function __destruct() { }		
		public function __clone() {	}		
		public function __call($method, $arguments) {	}		
		public function __set($nom, $valeur) { }		
		public function __get($nom) {	}
		*/
		
		//redefine method for sqlserver
		public function lastInsertId($name = '') {
			$sql_scope = "select CONVERT(INT, SCOPE_IDENTITY()) as lastInsertId";
			$stmt_scope = parent::prepare($sql_scope);
			//exec sql
			if($stmt_scope->execute()) {
				return $stmt_scope->fetch(PDO::FETCH_OBJ)->lastInsertId;
			}
			return false;
		}
		
		//log des query prepare
		public function prepare($sql, $driver_options = array()) {
			global $GROUPES_LIBELLE;
			if(DEBUG) {
				echo "<div style=\"background-color:#5298d4; color:#FFFFFF; margin:10px; padding:5px\"><strong>DEBUG:</strong><hr>$sql</div>";	
			}	
			return parent::prepare($sql, $driver_options = array());
		}
	}
Et le code HTML généré lors d'un simple SELECT:
SELECT id, nom, prenom, email FROM [user] WHERE id = :id;
Concrètement, j'aimerais affiché:
SELECT id, nom, prenom, email FROM [user] WHERE id = 845; //par exemple

ViPHP
ViPHP | 5462 Messages

13 avr. 2010, 15:35

	
$query 	= $pdo->prepare("SELECT * FROM test WHERE id = :id AND test LIKE ':test'");
	
$data 	= array(':id' => 4, ':test' => 'hello');
$result	= $query->execute($data);
	
echo str_replace(array_keys($data), array_values($data), $query->queryString);

//SELECT * FROM test WHERE id = 4 AND test LIKE 'hello'
pas tiptop mais bon... a creuser

Mammouth du PHP | 568 Messages

13 avr. 2010, 15:37

$query 	= $pdo->prepare("SELECT * FROM test WHERE id = :id");
	
$data 	= array(':id' => 4);
$result	= $query->execute($data);
	
echo str_replace(array_keys($data), array_values($data), $query->queryString);

//SELECT * FROM test WHERE id = 4
pas tiptop mais bon... a creuser
J'y avais déjà pensé, mais ça me va pas...

Le but est de surchrager une des méthode de PDO pour arriver au résultat escompter, comme ça je ne le fais QUE dans ma class, je ne veux pas alourdir toutes mes class.

ViPHP
ViPHP | 5462 Messages

13 avr. 2010, 15:40

sauf que c'est pas au moment de 'prepare' qu'il faut le faire mais au moment de 'execute' :wink:

Mammouth du PHP | 568 Messages

13 avr. 2010, 15:48

sauf que c'est pas au moment de 'prepare' qu'il faut le faire mais au moment de 'execute' :wink:
Oui, je sais aussi... :/

Pour le moment j'essaye simplement de surcharger la méthode execute de ma classe mssqlPDO comme ceci:
public function execute($input_parameters = array()) {
			print_r($input_parameters);
			
			return parent::execute($input_parameters);
		}
Mais ça fait rien.

EDIT: bon déjà je dois surcharger PDOStatement et non pas ma class PDO apparemment.

ViPHP
ViPHP | 5462 Messages

13 avr. 2010, 15:59

EDIT: bon déjà je dois surcharger PDOStatement et non pas ma class PDO apparemment.
yep j'allais te le dire :wink:

Mammouth du PHP | 568 Messages

13 avr. 2010, 16:06

EDIT: bon déjà je dois surcharger PDOStatement et non pas ma class PDO apparemment.
yep j'allais te le dire :wink:
J'avance un tout petit peu,

Pour surcharger le PDOStatement, il faut utiliser l'attribut:
setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array($this)));
Le hic, c'est que j'utilise un multiton, donc j'adapte mon code, comme ci-dessous:
self::$instance[$name]->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array(self::$instance[$name])));
Jusque la tout va bien, si je test, je me retrouve avec l'erreur suivante:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: user-supplied statement does not accept constructor arguments' in C:\site\wwwroot_ieo\GestionPortailClient\class\db.class.php:83 Stack trace: #0 C:\site\wwwroot_ieo\GestionPortailClient\class\db.class.php(83): PDO->prepare('SELECT T1.id, n...', Array) #1 C:\site\wwwroot_ieo\GestionPortailClient\class\user.class.php(193): mssqlPDO->prepare('SELECT T1.id, n...') #2 C:\site\wwwroot_ieo\GestionPortailClient\index.php(78): user->getUser('POLIYO00') #3 {main} thrown in C:\site\wwwroot_ieo\GestionPortailClient\class\db.class.php  on line 83
Et donc apparement, il faut surcharger la méthode __construct de la class myPDOStatement comme suis:
public $dbh;
    protected function __construct($dbh) {
        $this->dbh = $dbh;
    }
Et la ça fonctionne, le seul hic est que je vais de voir reprendre toute mes class, car j'utilise bindParam au lieu de passer le tableau des clés / valeurs à la méthode execute :/

ViPHP
ViPHP | 5462 Messages

13 avr. 2010, 16:11

pas mal le coup du PDO::ATTR_STATEMENT_CLASS

Mammouth du PHP | 568 Messages

13 avr. 2010, 16:14

pas mal le coup du PDO::ATTR_STATEMENT_CLASS
Vi, je connaissais pas ^^

Mais bon un peu galère quand il faut se retaper 50 class...

Mammouth du PHP | 568 Messages

13 avr. 2010, 16:38

Finalement, c'est pas résolut, enfin la surcharge fonctionne, mais maintenant, toutes les query eécuté avec bindParam ne fonctionne plus :/

Je cherche...

ViPHP
ViPHP | 5462 Messages

13 avr. 2010, 16:42

ton MyPDOStatement etend PDOStatement ?

Mammouth du PHP | 568 Messages

13 avr. 2010, 16:50

ton MyPDOStatement etend PDOStatement ?
oui

ViPHP
ViPHP | 5462 Messages

13 avr. 2010, 16:53

ducoup je vois pas bien ton soucis avec le bindParam :roll:

Mammouth du PHP | 568 Messages

14 avr. 2010, 11:10

ducoup je vois pas bien ton soucis avec le bindParam :roll:
Je suis finalement arrivé à mes fins.

Le problème du bindParam venait du faite que la méthode execute avec comme paramètre $input_parameters plantait quand ce paramètre était vide (d'ailleurs pas bien normal mais bon), je le répète le but n'était pas de modifier toutes mes classes, mais d'adapté mes class PDO pour pouvoir logguer la query final avec les valeurs.

Ce que j'ai fait, j'ai créé une class myPDOStatement avec un paramètre privé $preBindingStatement qui va stocker la query puis la modifié au fur et à mesure que je bind les paramètre, si jamais, les paramètres sont passés directement dans la méthode execute, c'est quasiment pareil, puisque je parcours les paramètres et fais la même opération que pour le bind.

Voici donc la class final myPDOStatement:
class myPDOStatement extends PDOStatement {
		
		private $preBindingStatement;

		private function __construct($stmt) {
    	$this->stmt = $stmt;
			$this->preBindingStatement = $this->queryString;
    }
		
		public function bindParam($stmt_parameter, $stmt_value, $dataType = PDO::PARAM_STR) {        
        $this->preBindingStatement = str_replace("{$stmt_parameter}", "'{$stmt_value}'", $this->preBindingStatement);
				return parent::bindParam($stmt_parameter, $stmt_value, $dataType);
    }
		
		private function logQuery() {
			echo "<div style=\"background-color:#5298d4; color:#FFFFFF; margin:10px; padding:5px\"><strong>DEBUG:</strong><hr>".$this->preBindingStatement."</div>";
		}
		
		public function execute($input_parameters = array()) {
			
			if(count($input_parameters)>0) {
				$this->preBindingStatement = $this->queryString;
				foreach($input_parameters as $stmt_parameter => $stmt_value) {
					$this->preBindingStatement = str_replace("{$stmt_parameter}", "'{$stmt_value}'", $this->preBindingStatement);
				}
				$result = parent::execute($input_parameters);
			} else {
				$result = parent::execute();
			}			
			
			if(DEBUG) {
				$this->logQuery();
			}
			
			return $result;
		}
	}
Ce qui me permet d'avoir visuellement toutes les requêtes d'une page si j'active le mode DEBUG, pratique pour optimiser l'ensemble.

http://yfrog.com/72testmg

En passant vous avez des techniques de ce genre?