استخدام PDO لمحاولة الالتقاط في الوظائف

StackOverflow https://stackoverflow.com/questions/272203

  •  07-07-2019
  •  | 
  •  

سؤال

أفكر في استخدام PDO في جميع تطبيقات الويب المستقبلية الخاصة بي.حاليًا (باستخدام ما تعلمته من 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 جزء في جميع الوظائف.

أسئلتي هي:

  1. كيف يجب أن أجعل ذلك أكثر كفاءة؟(على سبيل المثال.عدم الاضطرار إلى تكرار try..catch في جميع الوظائف، ومع ذلك لا يزال قادرًا على إرجاع "رسائل فشل" مختلفة في كل منها)
  2. هل هذه بالفعل ممارسة جيدة؟ما زلت جديدًا في PDO وOOP (لا يزال هناك الكثير لأتعلمه)، لذلك (حتى الآن)، لا أستطيع حقًا رؤية أي عيوب أو أشياء يمكن تحسينها هناك.

أنا آسف إذا كان ذلك يبدو غير واضح أو طويل جدًا.شكرا لك مقدما.

هل كانت مفيدة؟

المحلول

التنفيذ الخاص بك جيد تمامًا، وسيعمل بشكل جيد لمعظم الأغراض.

ليس من الضروري وضع كل استعلام داخل كتلة محاولة/التقاط، وفي الواقع، في معظم الحالات، لا ترغب في ذلك.والسبب في ذلك هو أنه إذا قام الاستعلام بإنشاء استثناء، فهذا نتيجة لمشكلة فادحة مثل خطأ في بناء الجملة أو مشكلة في قاعدة البيانات، وهذه ليست مشكلات يجب أن تأخذها في الاعتبار مع كل استعلام تقوم به.

على سبيل المثال:

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 للحصول على معلومات حول كيفية القيام بذلك.

نصائح أخرى

هناك بعض التحذيرات هنا:

  • تمت كتابة هذا الرمز لأخذ العديد من المشكلات القديمة في الاعتبار مثل تسجيل قاعدة البيانات وإدارة تكوين قاعدة البيانات.
  • أوصي بشدة أن تنظر إلى الحل الحالي قبل بناء الحل الخاص بك.يعتقد الكثير من الأشخاص في أنفسهم عندما يبدأون أنهم لا يريدون استخدام إطار عمل أو مكتبة موجودة لأنها كبيرة جدًا، وتتطلب الكثير من الوقت للتعلم، وما إلى ذلك، ولكن بعد أن كنت أحد هؤلاء الأشخاص، لا أستطيع أن أذكر بشكل قاطع بما فيه الكفاية أنني سأترك إطار العمل المخصص وفئات التغليف للانتقال إلى إطار العمل.إنني أتطلع إلى الانتقال إلى Zend ولكن هناك عددًا من الخيارات الممتازة المتاحة.

أوه، يجب أن أشير إلى أن هذه النقطة توضح كيف يمكن للمرء تغليف دالة واحدة للتعامل مع جميع عمليات معالجة الاستثناءات لاستفساراتك.لا أكتب محاولة التقاط الكتل في أي مكان آخر تقريبًا الآن لأن تتبع المكدس من الاستعلام يمنحني جميع المعلومات التي أحتاجها لتصحيح المشكلة وإصلاحها.

إليك تطبيق فئة غلاف 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);          
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top