Page 1 sur 1

Un PDO "simplifié"

Posté : 13 janv. 2009, 14:36
par katagoto
Bonjour à toutes et à tous,

Voilà, j'utilise intensément PDO, j'avais fais une classe ci-dessous qui gérait les erreurs uniquement :
<?php
/**
 * Classe de "étendu" de PDO
 * 
 * @author Katagoto
 */
class TPDO
{
	/**
     * L'instance de PDO 
     * 
     * @var PDO 
     */ 
	private $lien;

	/**
	 * NHombre de requêtes
	 *
	 * @var int
	 */

	public $NbRequete;

	/**
	 * Nombre de temps total pour le sql
	 *
	 * @var int
	 */

	public $Temps;

	/**
	 * Si le mode transactionnel est activé
	 *
	 * @var bool
	 */

	public $transaction = true;

     /**
     * Constructeur de sql 
     * 
     * Tente une connexion au serveur de BD 
     * 
     * @param string $url 
     * @param string $login 
     * @param string $password 
     * @param string $nom 
     */ 
	public function __construct($url, $login, $password, $nom)
	{
                    global $template;
		try
		{
			$temps = microtime(true);
			$this->lien = new PDO('postgresql:host='.$url.';dbname='.$nom, $login, $mdp);
			$this->Temps += ($temps - microtime(true));
		}

		catch(Exception $e)
		{
			$dernier = apc_fetch('bug_connexion_sql', $existe);

			if (DEBUG || ($existe && $dernier > (time()-(15*60))))
			{
				apc_store('bug_connexion_sql', time());

				@mail(LIST_MAIL,
				'Avertissement de sécurité la Connexion à la Base de Données du site est impossible',
				@file_get_contents(ABS_DIR.'templates/fr/bug/sql/mail/connexion_impossible.html').$e->getMessage().'</p><p>Description :'.$e->__toString().'</div></body></html>');
			}


			$template->parse('bug/sql/connexion_impossible.html');
			exit();
		}

	}

     /**
     * Surcharge des méthodes PDO 
     * 
     * @param string $m nom de la méthode 
     * @param array $a arguments à passer à la méthode 
     * @throws BadMethodCallException si la méthode $m n'existe pas 
     */ 
	public function __call($m, $a)
	{
                   global $template;
		if(method_exists($lien, $m))
		{

			try
			{
				$lien->beginTransaction();
				$this->NbRequete++;
				$temps = microtime(true);
				$copie =& $this->lien->$m($a);
				$this->Temps += ($temps - microtime(true));
				$this->lien->closeCursor();
				if ($lien->commit())
				{
					throw PDOException('Exécution impossible d\'une requête :'.var_dump($a));
				}
				return $copie;
			}
			catch (PDOException $e)
			{
				if ($this->transaction)
				{
					$this->lien->rollBack();

					$dernier = apc_fetch('bug_requete_sql', $existe);

					if (DEBUG || ($existe && $dernier > (time()-(15*60))))
					{
						apc_store('bug_requete_sql', time());
						@mail(LIST_MAIL,
						'Avertissement de sécurité une requête à la Base de Données du site est impossible',
						@file_get_contents(ABS_DIR.'templates/fr/bug/sql/mail/requete_impossible.html').$e->__toString().'</div></body></html>');
					}

					else
					{
						throw new BadMethodCallException('La méthode PDO->$m() n\'existe pas!');
					}
					$template->parse('bug/sql/requete_impossible.html');

				}
			}
		}
	}
	
	/**
	 * Surcharge de PDO::prepare
	 *
	 * @param str $requete
	 * @return TPDOStatement
	 */
	
	public function prepare($requete)
	{
                   global $template;
			try
			{
				$lien->beginTransaction();
				$this->NbRequete++;
				$temps = microtime(true);
				$copie =& new PDOStatement($requete);
				$this->Temps += ($temps - microtime(true));
				return $copie;

			}
			catch (PDOException $e)
			{

					if (DEBUG || ($existe && $dernier > (time()-(15*60))))
					{
						apc_store('bug_requete_sql', time());
						@mail(LIST_MAIL,
						'Avertissement de sécurité une requête à la Base de Données du site est impossible',
						@file_get_contents(ABS_DIR.'templates/fr/bug/sql/mail/requete_impossible.html').$e->__toString().'</div></body></html>');
					}

					else
					{
						throw new BadMethodCallException('La méthode PDO->$m() n\'existe pas!');
					}
					$template->parse('bug/sql/requete_impossible.html');

				}

	}

}
Seulement, cette classe à un vis : elle ne fonctionne que pour PDO...
100% de nos requêtes étant préparées, comment faire pour qu'elle englobe PDOStatement et ainsi comptabilise le temps ? Y a-t'il des choses à améliorer/changer ?

Par avance merci de votre aide...

Posté : 13 janv. 2009, 17:40
par Hywan
Hey :),

Il faut faire des wrappers pour les classes PDO et PDOStatement. Grosso modo, tu passeras toujours par un classe qui ne fait que rediriger les appels vers PDO et pareil pour PDOStatement. De cette façon, tu caches PDO, tu captures les erreurs qui remontent, bref t'es plus libre.

Posté : 13 janv. 2009, 19:22
par katagoto
Un petit indice SVP m'sieur ?

Par avance merci de votre aide

Posté : 13 janv. 2009, 19:32
par Hywan
Très rapidement :
class Wrapper_Pdo {

    protected $_pdo = null;



    public function __construct ( $arg1, ..., $argn ) {

        $this->_pdo = new PDO($arg1, ..., $argn);
    }

    public function exec ( $statement ) {

        return $this->_pdo->exec($statement);
    }

    public function query ( $statement ) {

        try {

            $statement = $this->_pdo->query($statement);
        }
        catch ( PDOStatement $e ) {

            $e->raiseError();
        }

        return new Wrapper_PdoStatement($statement);
    }

    ...
}
Et :
class Wrapper_PdoStatement {

    protected $_statement = null;



    public function __construct ( PDOStatement $statement ) {

        $this->_statement = $statement;
    }

    public function execute ( Array $parameters = array() ) {

        return $this->_statement->execute($parameters);
    }

    ...
}
Tout simplement. Tu peux tout récupérer. C'est une sorte de Programmation Orientée Aspect si tu connais.

Posté : 13 janv. 2009, 19:56
par katagoto
Non, et c'est ce que je pensais faire :/

Donc il y a pas plus simple, dommage

Merci

Posté : 13 janv. 2009, 20:03
par Hywan
Disons que c'est simple quand même … très peu de code, léger, facile à maintenir etc., et c'est le plus abstrait possible. Comprends le plus complet possible. Tu gères tout les cas en faisant ça, et tu peux intervenir où tu veux :).

Posté : 14 janv. 2009, 13:41
par Calimero
En français, on appelle simplement cela l'encapsulation ;-)

Posté : 14 janv. 2009, 14:03
par Hywan
L'encapsulation est le simple fait d'avoir des données avec un accès différent selon le contexte (public, protégé, privé). On a donc des méthodes pour accéder à ces données.

Le wrapper (l'emballage littéralement) pousse les choses plus loin avec des prémices de programmation orienté aspect. C'est surtout le fait de rediriger les appels, de les envelopper dans une nouvelle action, qui est important.

Évidemment que l'on a de l'encapsulation mais je tiens à faire la différence car l'emballage est plus poussé … ou du moins, il sert pas tout à fait à la même chose. L'encapsulation est une notion vraiment basique de la programmation orientée objet qu'on ne saurait comparer à l'emballage à niveau égal ;-).
Si on parle d'emballage, on voit la solution mais aussi le problème. Si on parle d'encapsulation, on voit une solution possible parmi beaucoup de problèmes. On n'est moins précis.

Mais la remarque est juste :).