项目中引进了ThinkPHP3.2.3的模型层,发现当SQL语句出错时,系统抛出的异常不是我想要的效果,打开文件 ThinkPHP\Library\Think\Db\Driver.class.php,找到如下代码:
/** * 执行语句 * * @access public * @param string $str sql指令 * @param boolean $fetchSql 不执行,只是获取SQL * @return mixed */public function execute($str,$fetchSql=false) { $this->initConnect(true); if ( !$this->_linkID ) return false; $this->queryStr = $str; if(!empty($this->bind)){ $that = $this; $this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return '\''.$that->escapeString($val).'\''; },$this->bind)); } if($fetchSql){ return $this->queryStr; } //释放前次的查询结果 if ( !empty($this->PDOStatement) ) $this->free(); $this->executeTimes++; N('db_write',1); // 兼容代码 // 记录开始执行时间 $this->debug(true); $this->PDOStatement = $this->_linkID->prepare($str); if(false === $this->PDOStatement) { $this->error(); return false; } foreach ($this->bind as $key => $val) { if(is_array($val)){ $this->PDOStatement->bindValue($key, $val[0], $val[1]); }else{ $this->PDOStatement->bindValue($key, $val); } } $this->bind = array(); $result = $this->PDOStatement->execute(); // 当有PDO内部异常时,此处就会直接抛出PHP异常,后面的代码不会被执行,页面被中断 $this->debug(false); if ( false === $result) { $this->error(); return false; } else { $this->numRows = $this->PDOStatement->rowCount(); if(preg_match("/^\s*(INSERT\s+INTO|REPLACE\s+INTO)\s+/i", $str)) { $this->lastInsID = $this->_linkID->lastInsertId(); } return $this->numRows; }}
注意:
看到上面代码中的片段
$result = $this->PDOStatement->execute();
当上述PDO语句有异常时(如 SQL语句中数据表不存在),此处就会直接抛出PHP异常,后面的代码不会被执行,页面被中断,这不是我们想要的结果;
我们是希望PDO异常能够寄存到某个变量中,并且页面不会被中断,异常信息留到后面处理(希望异常在 $this->error() 中被处理),这也是ThinkPHP作者的设计意图,所以该驱动类文件(ThinkPHP\Library\Think\Db\Driver.class.php)需要调整下,找到如下代码调整:
// PDO连接参数protected $options = array( PDO::ATTR_CASE => PDO::CASE_LOWER, // 需要改为 PDO::CASE_NATURAL,表示 字段名按照原始的方式显示 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 需要改为 PDO::ERRMODE_SILENT,方便用 errorInfo()方法 来获取异常信息 PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, PDO::ATTR_STRINGIFY_FETCHES => false,);
参考: