Я подумываю использовать PDO во всех своих будущих Webapp. В настоящее время (используя то, что я узнал из SO до сих пор), то, что у меня есть на моем сайте для обработки соединения с базой данных, является класс Singleton следующим образом:
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
во всех функциях и, тем не менее, все еще может возвращать разные «сообщения об try..catch
» на каждом из них) Извините, если это кажется неясным или слишком длинным. Заранее спасибо.
Ваша реализация просто прекрасна, и она будет отлично работать для большинства целей.
Нет необходимости ставить каждый запрос внутри блока 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 для получения информации о том, как это сделать.
Несколько предостережений здесь:
О, я должен указать, что этот момент иллюстрирует, как можно обернуть одну функцию для обработки всей обработки исключений для ваших запросов. Я не пишу блок try catch почти где-нибудь еще, потому что трассировка стека из запроса дает мне всю информацию, необходимую мне для отладки проблемы и ее исправления.
Вот моя текущая реализация класса оболочки PDO:
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); } }