2008-11-07 32 views
30

Tôi đang nghĩ đến việc sử dụng PDO trong tất cả ứng dụng web trong tương lai của mình. Hiện nay (sử dụng những gì tôi đã học được từ SO cho đến nay), những gì tôi có trong trang web của tôi để xử lý kết nối cơ sở dữ liệu là một lớp Singleton như thế này:PDO try-catch usage trong các hàm

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; 
    } 
} 

và một tập tin (functions.php) với nội dung cụ thể các chức năng trông giống hệt như sau:

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"; 
    } 
} 
... 

có nghĩa là tôi phải lặp lại phần try .. catch trong tất cả các chức năng.

Câu hỏi của tôi là:

  1. Làm thế nào tôi nên làm mà hiệu quả hơn? (ví dụ: không phải lặp lại try..catch trong tất cả các chức năng, nhưng vẫn có thể trả về "thông báo lỗi" khác nhau trên mỗi chức năng)
  2. Đây có phải là thực tiễn tốt không? Tôi vẫn còn mới ở PDO và OOP (vẫn còn rất nhiều thứ để học), vì vậy (tính đến bây giờ), tôi không thể thực sự thấy bất kỳ bất lợi hoặc những thứ có thể được cải thiện trong đó.

Tôi xin lỗi nếu điều đó có vẻ không rõ ràng hoặc quá dài. Cảm ơn trước.

Trả lời

38

thực hiện của bạn là tốt, và nó sẽ làm việc hoàn toàn tốt cho hầu hết các mục đích.

Không cần đặt mọi truy vấn bên trong khối thử/nắm bắt và thực tế trong hầu hết các trường hợp bạn thực sự không muốn. Lý do cho điều này là nếu một truy vấn tạo ra một ngoại lệ, đó là kết quả của một vấn đề gây tử vong như một lỗi cú pháp hoặc một vấn đề cơ sở dữ liệu, và đó không phải là vấn đề mà bạn nên tính toán với mọi truy vấn mà bạn làm.

Ví dụ:

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!"); 
} 

Các truy vấn ở đây sẽ một trong hai hoạt động đúng hoặc không làm việc ở tất cả.Những hoàn cảnh mà nó sẽ không hoạt động ở tất cả nên không bao giờ xảy ra với bất kỳ quy định trên một hệ thống sản xuất, vì vậy họ không phải là điều kiện mà bạn nên kiểm tra ở đây. Làm như vậy thực sự phản tác dụng, bởi vì người dùng của bạn gặp lỗi sẽ không bao giờ thay đổi và bạn không nhận được thông báo ngoại lệ sẽ cảnh báo bạn về vấn đề này.

Thay vào đó, chỉ cần viết này:

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

Nói chung, thời gian duy nhất mà bạn sẽ muốn bắt và xử lý một ngoại lệ truy vấn là khi bạn muốn làm một cái gì đó khác nếu truy vấn thất bại. Ví dụ:

// 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; 
} 

Bạn sẽ muốn viết trình xử lý ngoại lệ đơn giản ghi nhật ký hoặc thông báo lỗi cơ sở dữ liệu cho người dùng thay vì theo dõi ngoại lệ. Xem http://www.php.net/set-exception-handler để biết thông tin về cách thực hiện điều đó.

+0

Thứ hai những gì pd nói, việc sử dụng lớp lỗi tùy chỉnh của tôi chỉ đơn giản là ghi lại lỗi cho db và gửi cho tôi một email. Người dùng cuối không bao giờ thấy dấu vết ngăn xếp hoặc sự không thân thiện khác. Đó là lý do tại sao nếu có lỗi tôi trả về false và sau đó kiểm tra giá trị trả về của truy vấn để xác định điều gì sẽ cho người dùng biết –

+0

Nếu bạn thay đổi SELECT và/hoặc truy vấn khác đã thay đổi cơ sở dữ liệu để SELECT không hoạt động nữa: Bạn chưa bao giờ biết về điều đó cho đến khi bạn tự mình kiểm tra trang của mình. Điều này phản tác dụng. try/catch là cần thiết cho mọi truy vấn! – mgutt

3

Một vài số khó khăn ở đây là:

  • Mã này được ghi vào mất vài vấn đề di sản vào tài khoản như cơ sở dữ liệu khai thác gỗ và cấu hình cơ sở dữ liệu quản lý.
  • Tôi thực sự khuyên bạn nên xem xét giải pháp hiện có trước khi xây dựng giải pháp của riêng bạn. Rất nhiều người nghĩ đến bản thân khi họ bắt đầu rằng họ không muốn sử dụng khung hoặc thư viện hiện có vì chúng quá lớn, đòi hỏi quá nhiều thời gian để học, v.v ... nhưng sau khi là một trong những người tôi không thể nhấn mạnh rằng tôi rời khỏi khung tùy chỉnh của tôi và các lớp bao bọc để chuyển sang một khung công tác. Tôi đang tìm cách chuyển sang Zend nhưng có một số lựa chọn tuyệt vời có sẵn.

Ồ, tôi nên chỉ ra rằng điểm này minh họa cách người ta có thể bọc một hàm duy nhất để xử lý tất cả xử lý ngoại lệ cho truy vấn của bạn. Tôi không viết cố gắng nắm bắt khối gần như bất cứ nơi nào khác bây giờ bởi vì ngăn xếp dấu vết từ truy vấn mang lại cho tôi tất cả các thông tin mà tôi cần phải gỡ lỗi các vấn đề và sửa chữa nó.

Đây là hiện tại lớp PDO wrapper thực hiện của tôi:

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

Xin cảm ơn, tôi sẽ xem thêm sáng ngày mai. – andyk