2008-11-07 2 views
30

저는 앞으로 모든 webapp에서 PDO를 사용할 생각입니다. 내용 별과PDO에서 try-catch 사용법을 사용합니다.

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

및 다른 파일 (functions.php) : 현재 제가 데이터베이스 연결을 처리 할 수있는 내 사이트에있는 것은 싱글 톤이 같은 클래스 (내가 지금까지에서 배운 내용을 사용) 함수는 정확히 하나처럼 보이는 :

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

나는 모든 기능에 try .. catch 부분을 반복해야 할 것을 의미한다.

내 질문은 :

  • 어떻게 더 효율적인 것을 을

    해야
      ? (예 : 모든 기능에서 try..catch을 반복 할 필요가없고 그래도 각각 다른 "실패 메시지"를 반환 할 필요가 없음)
    1. 이것은 이미 좋은 방법입니까? 나는 여전히 PDO와 OOP (아직 배울 점이 많다.)에서 새로운 편이다. 그래서 (지금 현재) 나는 거기에서 개선 될 수있는 단점이나 일들을 실제로 볼 수 없다.

    불분명하거나 너무 길어서 죄송합니다. 미리 감사드립니다.

  • 답변

    38

    귀하의 구현은 잘이며, 대부분의 목적을 위해 완벽하게 잘 작동합니다 :

    여기에 내 현재 PDO 래퍼 클래스의 구현입니다.

    모든 쿼리를 try/catch 블록에 넣을 필요는 없으며 사실 대부분의 경우 실제로 원하지 않습니다. 그 이유는 쿼리가 예외를 생성하는 경우 구문 오류 또는 데이터베이스 문제와 같은 치명적인 문제의 결과이며 이러한 쿼리는 사용자가 수행하는 모든 쿼리에 대해 고려해야하는 문제가 아니기 때문입니다. 예를 들어

    :

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

    쿼리는 여기 제대로 작동하거나 또는 전혀 작동하지.전혀 작동하지 않는 상황은 프로덕션 시스템의 규칙에 따라 발생해서는 안되기 때문에 여기에서 확인해야하는 조건이 아닙니다. 이렇게하면 실제로는 비생산적입니다. 사용자가 결코 변경되지 않는 오류를 얻고 문제를 경고하는 예외 메시지를받지 못하기 때문입니다.

    대신, 그냥 쓰기 :

    일반적으로
    $rs = $db->prepare('SELECT * FROM foo'); 
    $rs->execute(); 
    $foo = $rs->fetchAll(); 
    

    쿼리가 실패 할 경우 다른 뭔가를 할 때, 당신이 잡아 쿼리 예외를 처리 할 것이다 유일한 시간입니다. 예를 들면 :

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

    당신은 로그인 또는 프로덕션 환경에서 발생하는 데이터베이스 오류를 통지하고 사용자 대신 예외 추적에 친숙한 오류 메시지를 표시하는 간단한 예외 핸들러를 작성하는 것이 좋습니다. 이를 수행하는 방법에 대한 정보는 http://www.php.net/set-exception-handler을 참조하십시오.

    +0

    두 번째로 pd가 말한 것으로, 커스텀 에러 클래스의 나의 사용법은 단순히 db에 에러를 기록하고 이메일을 보낸다. 최종 사용자는 스택 추적 또는 기타 불만족을 보지 못합니다. 그래서 오류가 있으면 false를 반환 한 다음 내 쿼리의 반환 값을 테스트하여 사용자에게 무엇을 알릴 지 결정합니다. –

    +0

    SELECT를 변경하거나 SELECT가 더 이상 작동하지 않도록 다른 쿼리가 데이터베이스를 변경 한 경우 당신이 당신 자신의 페이지를 테스트 할 때까지 그것에 대해 알아라. 이것은 비생산적입니다. try/catch는 모든 쿼리에 필요합니다! – mgutt

    3

    여기서주의의 몇은 다음과 같습니다

    • 이 코드는 같은 데이터베이스 로깅 및 데이터베이스 구성 관리로 계정에 여러 개의 기존 문제를 취할 기록됩니다.
    • 나만의 솔루션을 만들기 전에 기존 솔루션을 살펴 보는 것이 좋습니다. 많은 사람들은 너무 크고 배우기에는 너무 많은 시간이 필요하기 때문에 기존 프레임 워크 나 라이브러리를 사용하고 싶지는 않지만 처음 시작할 때 자신을 생각합니다. 그러나이 사람들 중 한 사람인 후에 나는 사용자 정의 프레임 워크와 래퍼 클래스를 프레임 워크로 옮겨 놓을만큼 충분히 강조 할 수 없다. 나는 Zend로 옮기려고 노력하고 있지만 훌륭한 선택이 많이있다.

    아,이 점은 쿼리에 대한 모든 예외 처리를 처리하는 단일 함수를 래핑하는 방법을 보여줍니다. 나는 쿼리를 스택 트레이스하기 때문에 문제를 디버그하고 수정하는 데 필요한 모든 정보를 얻을 수 있기 때문에 거의 모든 곳에서 try catch 블록을 작성하지 않습니다.

    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

    감사합니다. 내일 아침에 더 자세히 살펴 보겠습니다. – andyk

    관련 문제