Подтвердить что ты не робот

Использование try-catch PDO в функциях

Я подумываю использовать 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 во всех функциях и все же все еще способно возвращать разные "сообщения об ошибке" на каждом из них)
  • Это уже хорошая практика? Я все еще новичок в PDO и OOP (еще многое предстоит узнать), поэтому (на данный момент) я не вижу никаких недостатков или вещей, которые можно улучшить там.

Извините, если это кажется неясным или слишком длинным. Спасибо заранее.

4b9b3361

Ответ 1

Ваша реализация просто прекрасна, и она будет отлично работать для большинства целей.

Не нужно ставить каждый запрос внутри блока try/catch, и на самом деле в большинстве случаев вы на самом деле не хотите. Причина этого заключается в том, что если запрос генерирует исключение, это результат фатальной проблемы, такой как синтаксическая ошибка или проблема с базой данных, и это не проблемы, которые вы должны учитывать при каждом запросе, который вы делаете.

Например:

try {
    $rs = $db->prepare('SELECT * FROM foo');
    $rs->execute();
    $foo = $rs->fetchAll();
} catch (Exception $e) {
    die("Oh noes! There 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 для получения информации о том, как это сделать.

Ответ 2

Несколько предостережений:

  • Этот код написан для учета нескольких унаследованных проблем, таких как ведение журнала базы данных и управление конфигурацией базы данных.
  • Я бы настоятельно рекомендовал вам взглянуть на существующее решение, прежде чем создавать свои собственные. Многие люди думают про себя, когда начинают, что они не хотят использовать существующую инфраструктуру или библиотеку, потому что они слишком большие, требуют слишком много времени для обучения и т.д., Но после того, как я был одним из этих людей, я не может заявить достаточно решительно, что я оставляю свои пользовательские рамки и классы-оболочки для перехода к структуре. Я хочу переехать в Zend, но есть ряд отличных вариантов.

О, я должен указать, что этот момент иллюстрирует, как можно обернуть одну функцию для обработки всей обработки исключений для ваших запросов. Я не пишу блок 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);          
    }
}