web-dev-qa-db-ja.com

関数でのPDO try-catchの使用法

将来のすべてのWebアプリでPDOを使用することを考えています。現在(SOこれまでに学んだことを使用して)、データベース接続を処理するために私のサイトにあるのは、このようなシングルトンクラスです:

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部分を繰り返す必要があります。

私の質問は:

  1. どうすればより効率的にする必要がありますか? (例えば、すべての関数でtry..catchを繰り返す必要はなく、それでもそれぞれの関数で異なる「失敗メッセージ」を返すことができます)
  2. これはすでに良い習慣ですか?私はまだPDOにまだ慣れておらず、OOP(まだまだ学ぶべきことはまだたくさんあります)。 。

不明または長すぎると思われる場合は、申し訳ありません。前もって感謝します。

30
andyk

実装は問題なく、ほとんどの目的で完全に機能します。

すべてのクエリを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 をご覧ください。

40
pd.

ここにいくつかの注意事項があります:

  • このコードは、データベースロギングやデータベース構成管理など、いくつかのレガシーな問題を考慮するために書かれています。
  • 独自のソリューションを構築する前に、既存のソリューションを確認することを強くお勧めします。多くの人は、サイズが大きすぎたり、学習に時間がかかりすぎたりするため、既存のフレームワークやライブラリを使用したくないと思い始めますが、そうした人の一人になった後、私はカスタムフレームワークとラッパークラスを離れてフレームワークに移動することを十分に強調することはできません。 Zendに移行したいと考えていますが、いくつかの優れた選択肢があります。

ああ、この点は、クエリのすべての例外処理を処理するために1つの関数をラップする方法を示していることを指摘する必要があります。クエリからのスタックトレースにより、問題をデバッグして修正するために必要なすべての情報が得られるため、現在、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);          
    }
}
3
Noah Goodrich