dongzhao4036 2019-06-01 23:21
浏览 128
已采纳

如何在PDO包装器中返回execute值[重复]

This question already has an answer here:

Beneath is my PDO wrapper. I want to be able to use the run method however, I want to be able to check if the execution has been successful such as:

if($sth->execute())
{
   ...
}

However, as you can see within the wrapper, the run command only returns the prepare statement, what would be the most efficient way to achieve this?

<?php

class Database {

    const hostname = 'localhost';
    const user = 'root';
    const password = '';
    const charset = 'utf8';
    const database = 'syn_v2';

    protected static $instance;
    protected $pdo;

    protected function __construct()
    {
        $opt = array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
            PDO::ATTR_EMULATE_PREPARES => false
        );

        $dsn = sprintf('mysql:host=%s;dbname=%s;charset=%s', self::hostname, self::database, self::charset);

        $this->pdo = new PDO($dsn, self::user, self::password);
    }

    public static function instance()
    {
        if(self::$instance === null)
        {
            self::$instance = new self;
        }

        return self::$instance;
    }

    public function __call($method, $args)
    {
        return call_user_func_array(array($this->pdo, $method), $args);
    }

    public function run($sql, $args = [])
    {
        if(!$args)
        {
            return $this->query($sql);
        }

        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($args);

        return $stmt;
    }

}

?>
</div>
  • 写回答

1条回答 默认 最新

  • douri4459 2019-06-02 00:23
    关注

    Since PDOStatement::execute returns true/false and your current run method is returning a PDOStatement on success and false on failure. I suggest checking prepare and execute are not false and return the PDOStatement on success or false otherwise, as is the case with PDO::prepare and PDO::query.

    Functional Example https://3v4l.org/OnDdn

    /**
     * @return PDOStatement|false
     */
    public function run($sql, $args = [])
    {
        if (!$args) {
            return $this->pdo->query($sql);
        }
        if ($stmt = $this->pdo->prepare($sql)) {
            if ($stmt->execute($args)) {
                return $stmt;
            }
        }
    
        return false; //either prepare or execute failed
    }
    
    $db = Database::instance();
    var_dump($db->run('SELECT ?', ['foo', 'bar'])); //false
    

    An alternative approach would be to store the last execute value in a property, for later retrieval.

    Example https://3v4l.org/UbM1N

    class Database
    {
    
        protected $lastExecute;
    
       //...
    
        /**
         * @return PDOStatement|false
         */
        public function run($sql, $args = [])
        {
            if (!$args) {
                return $this->pdo->query($sql);
            }
            if ($stmt = $this->pdo->prepare($sql)) {
                $this->lastExecute = $stmt->execute($args);
            }
    
            return $stmt;
        }
    
        /**
         * @return null|bool
         */
        public function getLastExecute()
        {
           return $this->lastExecute;
        }
    }
    
    $db = Database::instance();
    $db->run('SELECT ?', ['foo', 'bar']);
    var_dump($db->getLastExecute()); //false
    

    To address the best-practices comments below in regard to the issue of determining when PDO::execute method specifically fails from within the Database::run method by using Exception handling.

    Please keep in mind Best-Practices are not about right or wrong, "they are simply recommended methods of writing code." Referring to an approach of programming that is commonly preferred in professional application development. Always use what works best for you, the environment you are developing for and your application requirements.

    Generally speaking StackOverlow is not an appropriate place to discuss or evaluate an author's application of best-practices. Those types of discussions or critiques should be reserved for CodeReview. StackOverflow is intended to answer the author's specific question, or provide a viable alternative method to accomplish what the user is asking for. Not infer the user has asked the wrong question.

    To use exceptions you need to enable PDO::ERRMODE_EXCEPTION (see below Database class).

    The issue with using try/catch with a PDO wrapper method, is that PDO will only throw a single Exception object PDOException, which does not provide you with the ability to determine which PDO method call specifically failed. Leaving you to read the PDOException::getMessage() or PDOException::getTrace(), to determine the cause.

    A simple approach would be to check the PDOException::trace for the function name that caused the exception.

    Functional Example (PHP 5.6+): https://3v4l.org/fDnBI

    try {
       $db = Database::instance();
       var_dump($db->run('SELECT ?', ['foo', 'bar'])->fetch());
    } catch(\PDOException $e) {
       if ('execute' === $e->getTrace()[0]['function']) {
           echo 'PDO::execute() failed';
           //Handle the execute exception
       }
       throw $e;
    }
    

    Please see the answer on PDO mysql: How to know if insert was successful by Your Common Sense for a more generalized approach to PDOException handling.

    The above approach prevents you from handling only specific exception types within the Database::run method, requiring you to use throw $e; after your conditional, when the exception is unexpected.
    To account for this issue, another approach would be to create custom Exception classes. You can do this by extending the base PDOException class to be compliant with other exception handling methods or to catch any of them.

    In order to catch any of the run specific exceptions, an empty interface can be used that is then implemented on the extended PDOException classes.

    interface DatabaseRunException{}
    

    Then create a new exception class for each of the specific PDO methods you would like to handle, that implements the DatabaseRunException interface.

    class PDOPrepareException extends PDOException implements DatabaseRunException{}
    
    class PDOExecuteException extends PDOException implements DatabaseRunException{}
    
    class PDOQueryException extends PDOException implements DatabaseRunException{}
    

    To use the custom exceptions to determine which PDO method failed, you need to handle the PDOException(s) within the Database::run() method and throw one of the custom exceptions.
    I have removed certain portions for brevity, commented out things that would alter your current configuration, made some best-practices and optimization changes for PHP 5.6+.

    class Database 
    {
        //...
    
        protected function __construct()
        {
            //static reduces overhead
            static $opt = [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
               // PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
               // PDO::ATTR_EMULATE_PREPARES => false
            ];
    
            $this->pdo = new PDO($dsn, self::user, self::password, $opt);
        }
    
        public static function instance()
        {
            if (null === self::$instance) {
                self::$instance = new self;
            }
    
            return self::$instance;
        }
    
        public function __call($method, $args)
        {
            //always ensure the desired method is callable!
            if (is_callable([$this->pdo, $method])) {
                //php 5.6+ variadic optimization (aka splat operator)
                return $this->pdo->$method(...$args);
    
                //php <= 5.5
                //return call_user_func_array(array($this->pdo, $method), $args);
            }
            throw new \BadMethodCallException(sprintf('Unknown method PDO::%s called!', $method));
        }
    
        public function run($sql, $args = [])
        {
            if (!$args) {
                try {
                    return $this->query($sql);
                } catch(\PDOException $e) {
                     throw new \PDOQueryException($e->getMessage(), (int) $e->getCode(), $e);
                }
            }
            try {
                $stmt = $this->prepare($sql);
            } catch(\PDOException $e) {
                throw new \PDOPrepareException($e->getMessage(), (int) $e->getCode(), $e);
            }
            try {
                $stmt->execute($args);
            } catch(\PDOException $e) {
                throw new \PDOExecuteException($e->getMessage(), (int) $e->getCode(), $e);
            }
    
            return $stmt;
        }
    
    }
    

    Functional Example (PHP 5.6+): https://3v4l.org/8QoRF

    You will now be able to handle each of the exceptions for any of the specific types.

    try {
       $db = Database::instance();
       $db->run('SELECT ?', ['foo', 'bar']);
    } catch(\PDOExecuteException $e) {
        echo 'PDO::execute() failed';
        //Handle the execute exception
        throw $e;
    }
    

    In PHP 7.1+ you can catch multiple exceptions.

    try {
       $db = Database::instance();
       $db->run('SELECT ?', ['foo', 'bar']);
    } catch(\PDOQueryException $e) {
        //Handle the query exception
        throw $e;
    } catch(\PDOPrepareException $e) {
        //Handle the prepare exception
        throw $e;
    } catch(\PDOExecuteException $e) {
        echo 'PDO::execute() failed';
        //Handle the execute exception
        throw $e;
    }
    

    In PHP <= 7.0 you can use the DatabaseRunException interface to catch and check the specific exceptions caused by Database::run() with instanceof to determine which exception was actually thrown.

    try {
       $db = Database::instance();
       $db->run('SELECT ?', ['foo', 'bar']);
    } catch(\DatabaseRunException $e) {
        if ($e instanceof \PDOQueryException) {
           //Handle the query exception
        } elseif ($e instanceof \PDOPrepareException) {
           //Handle the prepare exception
        } elseif ($e instanceof \PDOExecuteException) {
           echo 'PDO::execute() failed';
           //Handle the execute exception
        }
        throw $e;
    }
    

    As you can see, this increases the code complexity and will be up to you on determining what best suits your application needs.

    It is important to note, that variables declared in the try section will not be declared if an exception occurs prior to the declaration.

    try {
      throw new \Exception('FooBar');
      $foo = 'foo';
    } catch(\Exception $e) {
       var_dump(isset($foo)); //false
    }
    var_dump(isset($foo)); //false
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd
  • ¥20 ING91680C BLE5.3 芯片怎么实现串口收发数据
  • ¥15 无线连接树莓派,无法执行update,如何解决?(相关搜索:软件下载)
  • ¥15 Windows11, backspace, enter, space键失灵