2008-11-07 12 views
30

Estoy pensando en usar PDO en todas mis aplicaciones web futuras. En la actualidad (con lo que he aprendido de SO hasta ahora), lo que tengo en mi sitio para manejar la conexión de base de datos es una clase Singleton así:PDO try-catch uso en las funciones

class DB { 

    private static $instance = NULL; 
    private static $dsn  = "mysql:host=localhost;dbname=mydatabase;"; 
    private static $db_user = 'root'; 
    private static $db_pass = '0O0ooIl1'; 

    private function __construct() 
    { 

    } 
    private function __clone() 
    { 

    } 
    public static function getInstance() { 

     if (!self::$instance) 
     {   
      self::$instance = new PDO(self::$dsn, self::$db_user, self::$db_pass); 
      self::$instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
     } 
     return self::$instance; 
    } 
} 

y otro archivo (functions.php) con un contenido específico funciones se ve exactamente como éste:

function get_recent_activities() 
{  
    try 
    {  
     $db = DB::getInstance(); 
     // --prepare and execute query here, fetch the result-- 
     return $my_list_of_recent_activities; 
    } 
    catch (PDOException $e) 
    { 
     return "some fail-messages"; 
    } 
} 
... 

lo que significa que tengo que repetir la parte try .. catch en todas las funciones.

Mis preguntas son:

  1. ¿Cómo debería hacer más eficiente que? (por ejemplo, no tener que repetir try..catch en todas las funciones, y aún así poder devolver diferentes "mensajes de error" en cada uno)
  2. ¿Es esto una buena práctica? Todavía soy nuevo en PDO y OOP (aún hay mucho más por aprender), así que (a partir de ahora), no puedo ver ninguna desventaja o cosas que se puedan mejorar allí.

Lo siento si eso no parece claro o demasiado largo. Gracias por adelantado.

Respuesta

38

Su aplicación es muy bien, y que va a trabajar muy bien para la mayoría de los propósitos.

No es necesario colocar todas las consultas dentro de un bloque try/catch, y de hecho, en la mayoría de los casos, no desea hacerlo. La razón de esto es que si una consulta genera una excepción, es el resultado de un problema fatal como un error de sintaxis o un problema en la base de datos, y esos no son problemas que debe tener en cuenta en cada consulta que hace.

Por ejemplo:

try { 
    $rs = $db->prepare('SELECT * FROM foo'); 
    $rs->execute(); 
    $foo = $rs->fetchAll(); 
} catch (Exception $e) { 
    die("Oh noes! There's an error in the query!"); 
} 

La consulta aquí o bien trabajar correctamente o no funcionar en absoluto.Las circunstancias en las que no funcionaría nunca deberían ocurrir con regularidad en un sistema de producción, por lo que no son condiciones que deba verificar aquí. Hacerlo es en realidad contraproducente, porque los usuarios reciben un error que nunca cambiará y no recibirá un mensaje de excepción que lo alertaría sobre el problema.

En cambio, acaba de escribir esto:

$rs = $db->prepare('SELECT * FROM foo'); 
$rs->execute(); 
$foo = $rs->fetchAll(); 

En general, la única vez que usted querrá capturar y manejar una excepción de consulta es cuando se quiere hacer algo más si falla la consulta. Por ejemplo:

// We're handling a file upload here. 
try { 
    $rs = $db->prepare('INSERT INTO files (fileID, filename) VALUES (?, ?)'); 
    $rs->execute(array(1234, '/var/tmp/file1234.txt')); 
} catch (Exception $e) { 
    unlink('/var/tmp/file1234.txt'); 
    throw $e; 
} 

Usted desea escribir un manejador de excepciones simple que registra o notifica los errores de base de datos que se producen en su entorno de producción y muestra un mensaje de error amigable para los usuarios en lugar de la traza excepción. Consulte http://www.php.net/set-exception-handler para obtener información sobre cómo hacerlo.

+0

Para aclarar lo que pd dice, mi uso de la clase de error personalizada simplemente registra el error en el archivo db y me envía un correo electrónico. El usuario final nunca ve un rastro de pila u otra hostilidad. Es por eso que si hay un error, devuelvo falso y luego pruebo el valor de retorno de mi consulta para determinar qué decirle al usuario –

+0

Si cambia el SELECTO y/o otra consulta cambió la base de datos para que SELECCIONE ya no funcione: nunca saber de eso hasta que pruebe su página por su cuenta. Esto es contraproducente try/catch es necesario para cada consulta! – mgutt

3

Un par de advertencias aquí son:

  • Este código está escrito para tomar varias cuestiones de sucesión en cuenta, tales como la gestión de registro de base de datos y la configuración de base de datos.
  • Recomiendo encarecidamente que mire una solución existente antes de construir la suya propia. Muchas personas piensan por sí mismas cuando comienzan que no quieren usar un marco o biblioteca existente porque son demasiado grandes, requieren demasiado tiempo para aprender, etc., pero después de haber sido una de estas personas, no puedo afirmar con énfasis que estoy dejando mi marco personalizado y las clases de contenedor para pasar a un marco. Estoy buscando mudarme a Zend, pero hay una serie de excelentes opciones disponibles.

Oh, debo señalar que este punto ilustra cómo se podría ajustar una sola función para manejar todo el manejo de excepciones para sus consultas. No escribo try catch bloques casi en cualquier otro lugar ahora porque el seguimiento de la pila de la consulta me da toda la información que necesito para solucionar el problema y solucionarlo.

Aquí está mi actual implementación de la clase DOP envoltorio:

class DB extends PDO 
{ 
    // Allows implementation of the singleton pattern -- ndg 5/24/2008 
    private static $instance; 

    // Public static variables for configuring the DB class for a particular database -- ndg 6/16/2008 
    public static $error_table; 
    public static $host_name; 
    public static $db_name; 
    public static $username; 
    public static $password; 
    public static $driver_options; 
    public static $db_config_path; 



    function __construct($dsn="", $username="", $password="", $driver_options=array()) 
    { 
     if(isset(self::$db_config_path)) 
     { 
      try 
      { 
       if(!require_once self::$db_config_path) 
       { 
        throw new error('Failed to require file: ' . self::$db_config_path); 
       } 
      } 
      catch(error $e) 
      { 
       $e->emailAdmin(); 
      } 
     } 
     elseif(isset($_ENV['DB'])) 
     { 
      self::$db_config_path = 'config.db.php'; 

      try 
      { 
       if(!require_once self::$db_config_path) 
       { 
        throw new error('Failed to require file: ' . self::$db_config_path); 
       } 
      } 
      catch(error $e) 
      { 
       $e->emailAdmin(); 
      } 
     } 

     parent::__construct("mysql:host=" . self::$host_name . ";dbname=" .self::$db_name, self::$username, self::$password, self::$driver_options); 
     $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
     $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('QueryStatement', array($this))); 

     if(!isset(self::$error_table)) 
     { 
      self::$error_table = 'errorlog_rtab'; 
     } 
    } 

    /** 
    * Return a DB Connection Object 
    * 
    * @return DB 
    */ 
    public static function connect() 
    { 

     // New PDO Connection to be used in NEW development and MAINTENANCE development 
     try 
     { 
      if(!isset(self::$instance)) 
      { 
       if(!self::$instance = new DB()) 
       { 
        throw new error('PDO DB Connection failed with error: ' . self::errorInfo()); 
       } 
      } 

      return self::$instance; 
     } 
     catch(error $e) 
     { 
      $e->printErrMsg(); 
     } 
    } 

    /** 
    * Returns a QueryBuilder object which can be used to build dynamic queries 
    * 
    * @return QueryBuilder 
    * 
    */ 
    public function createQuery() 
    { 
     return new QueryBuilder(); 
    } 

    public function executeStatement($statement, $params = null, $FETCH_MODE = null) 
    { 
     if($FETCH_MODE == 'scalar') 
     { 
      return $this->executeScalar($statement, $params); 
     } 


     try { 
      try { 
       if(!empty($params)) 
       { 
        $stmt = $this->prepare($statement); 
        $stmt->execute($params); 
       } 
       else 
       { 
        $stmt = $this->query($statement); 
       } 
      } 
      catch(PDOException $pdo_error) 
      { 
       throw new error("Failed to execute query:\n" . $statement . "\nUsing Parameters:\n" . print_r($params, true) . "\nWith Error:\n" . $pdo_error->getMessage()); 
      } 
     } 
     catch(error $e) 
     { 
      $this->logDBError($e); 
      $e->emailAdmin(); 
      return false; 
     } 

     try 
     { 
      if($FETCH_MODE == 'all') 
      { 
       $tmp = $stmt->fetchAll(); 
      } 
      elseif($FETCH_MODE == 'column') 
      { 
       $arr = $stmt->fetchAll(); 

       foreach($arr as $key => $val) 
       { 
        foreach($val as $var => $value) 
        { 
         $tmp[] = $value; 
        } 
       }   
      } 
      elseif($FETCH_MODE == 'row') 
      { 
       $tmp = $stmt->fetch(); 
      } 
      elseif(empty($FETCH_MODE)) 
      { 
       return true; 
      } 
     } 
     catch(PDOException $pdoError) 
     { 
      return true; 
     } 

     $stmt->closeCursor(); 

     return $tmp; 

    } 

    public function executeScalar($statement, $params = null) 
    { 
     $stmt = $this->prepare($statement); 

     if(!empty($this->bound_params) && empty($params)) 
     { 
      $params = $this->bound_params; 
     } 

     try { 
      try { 
       if(!empty($params)) 
       { 
        $stmt->execute($params); 
       } 
       else 
       { 
         $stmt = $this->query($statement); 
       } 
      } 
      catch(PDOException $pdo_error) 
      { 
       throw new error("Failed to execute query:\n" . $statement . "\nUsing Parameters:\n" . print_r($params, true) . "\nWith Error:\n" . $pdo_error->getMessage()); 
      } 
     } 
     catch(error $e) 
     { 
      $this->logDBError($e); 
      $e->emailAdmin(); 
     } 

     $count = $stmt->fetchColumn(); 

     $stmt->closeCursor(); 

     //echo $count; 
     return $count;  
    } 

    protected function logDBError($e) 
    { 
     $error = $e->getErrorReport(); 

     $sql = " 
     INSERT INTO " . self::$error_table . " (message, time_date) 
     VALUES (:error, NOW())"; 

     $this->executeStatement($sql, array(':error' => $error)); 
    } 
} 

class QueryStatement extends PDOStatement 
{ 
    public $conn; 

    protected function __construct() 
    { 
     $this->conn = DB::connect(); 
     $this->setFetchMode(PDO::FETCH_ASSOC); 
    } 

    public function execute($bound_params = null) 
    { 
     return parent::execute($bound_params);   
    } 
} 
+0

Gracias, voy a ver esto más mañana por la mañana. – andyk

Cuestiones relacionadas