2008-11-07 20 views
30

Mam na myśli używanie PDO we wszystkich moich przyszłych aplikacjach internetowych. Obecnie (za pomocą czego się nauczyłem z tak do tej pory), co mam w miejscu do obsługi połączenia z bazą danych jest klasą Singleton tak:Używanie PDO w funkcjach w funkcjach

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

i inny plik (functions.php) o zawartości specyficznych funkcje patrząc dokładnie tak jak ten:

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

co oznacza, że ​​muszę powtórzyć try .. catch udział we wszystkich funkcjach.

Moje pytania są następujące:

  1. W jaki sposób należy zrobić to bardziej wydajny? (np. nie muszą powtarzać try..catch we wszystkich funkcjach, a mimo to wciąż mogą zwracać różne "komunikaty o błędach" na każdym z nich)
  2. Czy to już jest dobra praktyka? Wciąż jestem nowy w PDO i OOP (jeszcze wiele do nauczenia się), więc (od teraz) nie widzę żadnych wad ani rzeczy, które można poprawić.

Przykro mi, jeśli to wydaje się niejasne lub zbyt długie. Z góry dziękuję.

Odpowiedz

38

Twoja implementacja jest dobrze, a będzie działać doskonale do większości zastosowań.

Nie jest konieczne umieszczanie każdego zapytania wewnątrz bloku try/catch, aw rzeczywistości w większości przypadków nie chcesz tego robić. Powodem tego jest to, że jeśli zapytanie generuje wyjątek, jest to wynikiem śmiertelnego problemu, takiego jak błąd składniowy lub problem z bazą danych, i nie są to problemy, które powinieneś rozliczać przy każdym zapytaniu, które robisz.

Na przykład:

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

Zapytanie tutaj albo będzie działać poprawnie lub nie działa w ogóle.Okoliczności, w których w ogóle by się nie sprawdziły, nigdy nie powinny występować z regularnością systemu produkcyjnego, więc nie są to warunki, które powinieneś sprawdzić tutaj. Takie działanie przynosi skutek odwrotny do zamierzonego, ponieważ użytkownicy otrzymują błąd, który nigdy się nie zmieni, i nie otrzymasz komunikatu wyjątku, który ostrzegłby Cię o problemie.

Zamiast tego, po prostu napisz to:

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

W ogóle, tylko czas, że będziemy chcieli, aby złapać i obsłużyć wyjątek zapytanie jest, kiedy chcesz zrobić coś innego, jeśli kwerenda nie powiedzie się. Na przykład:

// 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ędziemy chcieli, aby napisać prostą obsługę wyjątków, które rejestruje lub powiadomienia o błędach baz danych, które występują w środowisku produkcyjnym i wyświetla komunikat o błędzie przyjazne dla użytkowników, zamiast śledzenia wyjątków. Zobacz http://www.php.net/set-exception-handler, aby dowiedzieć się, jak to zrobić.

+0

Na sekundę, co pd mówi, moje użycie niestandardowej klasy błędów po prostu rejestruje błąd w db i wysyła mi e-mail. Użytkownik końcowy nigdy nie widzi śladów stosu lub innych nieprzyjemności. Dlatego jeśli wystąpił błąd, zwracam wartość false, a następnie przetestuj wartość zwracaną przez moje zapytanie, aby określić, co powiedzieć użytkownikowi: –

+0

Jeśli zmienisz SELECT i/lub inne zapytanie zmieniło bazę danych, to SELECT już nie działa: Ty nigdy o tym wiedz, dopóki sam nie przetestujesz swojej strony. Jest to nieproduktywne. try/catch jest niezbędny dla każdego zapytania! – mgutt

3

Kilka zastrzeżeń oto:

  • Ten kod jest napisane, aby kilka starszych pod uwagę kwestie takie jak zarządzanie rejestrowania bazy danych i konfiguracji bazy danych.
  • Zdecydowanie zaleca się zapoznanie się z istniejącym rozwiązaniem przed zbudowaniem własnego. Wielu ludzi myśli sobie, gdy zaczynają, że nie chcą używać istniejącej struktury lub biblioteki, ponieważ są zbyt duże, wymagają zbyt wiele czasu na naukę itp., Ale po tym, jak byłem jednym z tych ludzi, nie mogę stwierdzić wystarczająco mocno, że opuszczam moje niestandardowe ramy i otwieram klasy, aby przejść do frameworka. Szukam przejścia do Zend, ale istnieje wiele doskonałych dostępnych opcji.

Chciałbym zaznaczyć, że ten punkt ilustruje, w jaki sposób można zawijać jedną funkcję, aby obsłużyć wszystkie operacje obsługi wyjątków dla zapytań. Nie piszę, próbuję catch blocks prawie nigdzie indziej, ponieważ śledzenie stosu z kwerendy daje mi wszystkie informacje, które są mi potrzebne do debugowania problemu i naprawienia go.

Oto moja obecna implementacja klasy PDO wrapper:

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

Dzięki, popatrzę na to bardziej jutro rano. – andyk

Powiązane problemy