博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
当Yii遇上不支持pdo_mysql的服务器
阅读量:7235 次
发布时间:2019-06-29

本文共 10827 字,大约阅读时间需要 36 分钟。

hot3.png

    (2014-10-9,在使用过程中仍发现不少问题,已迁移至https://github.com/xiilei/php-functions/tree/master/yii,不定期维护,下列代码不再更新)    

    这真是一件很郁闷的事情,项目的一个子项目(cms)中需要实现伪静态并且为了快速完成,选择了Yii,

    各方面都已准备好,路由规则,nginx rewrite,mysql slave ...但就是没有注意到,服务器没有pdo_mysql驱动,当时就震惊了! 项目已经上线,再编译已是不怎么实际,只好思考解决方案,也就是在这个时候才发现Yii只提供了PDO连接方式;时间很急,当时想到的解决办法:

  1. 从其他服务器复制过来php_mysql.so;
  2. 重写sql处理逻辑,改为mysqli实现(抛弃Yii的DAO);
  3. 使用mysqli模拟pdo;

第一种方式最简单,但没有这么做,很难保证成功和稳定性;

第二种是同事推荐的,代码量是很大,而且这样做了之后就很难回头,还要重新测试;

第三种倒是一个不错的解决方案,并且Yii支持指定pdoClass,对框架和原有代码都没有侵入性,短时间内也可以实现,所以就这个了;

由于对数据库只读,所以没有考虑太多,也不打算所有的方法都实现,就阅读了Yii的 DAO处理部分,简单写了这两个class

handle = new mysqli($matches[1],$username,$password,$matches[2]); //$options } public function beginTransaction(){ return $this->handle->autocommit(FALSE); } public function commit(){ return $this->handle->commit(); } public function rollBack(){ return $this->handle->rollback(); } public function errorCode(){ return $this->handle->errno; } public function errorInfo(){ return array_values($this->handle->error_list); } public function setAttribute($attribute, $value, &$source = null) { switch($attribute) { case PDO::ATTR_AUTOCOMMIT: $value = $value ? 1 : 0; if(!$this->handle->autocommit($value)) { throw new PDOException('set autocommit faild'); } return true; case PDO::ATTR_TIMEOUT: $value = intval($value); if($value > 1 && $this->handle->options( MYSQLI_OPT_CONNECT_TIMEOUT, $value)) { $source[PDO::ATTR_TIMEOUT] = $value; return true; } break; case self::MYSQL_ATTR_LOCAL_INFILE: $value = $value ? true : false; if($this->handle->options(MYSQLI_OPT_LOCAL_INFILE, $value)) { $source[self::MYSQL_ATTR_LOCAL_INFILE] = $value; return true; } break; case self::MYSQL_ATTR_INIT_COMMAND: if($value && $this->handle->options( MYSQLI_INIT_COMMAND, $value)) { $source[self::MYSQL_ATTR_INIT_COMMAND] = $value; return true; } break; case self::MYSQL_ATTR_READ_DEFAULT_FILE: $value = $value ? true : false; if($this->handle->options(MYSQLI_READ_DEFAULT_FILE, $value)) { $source[self::MYSQL_ATTR_READ_DEFAULT_FILE] = $value; return true; } break; case self::MYSQL_ATTR_READ_DEFAULT_GROUP: $value = $value ? true : false; if($this->handle->options(MYSQLI_READ_DEFAULT_GROUP, $value)) { $source[self::MYSQL_ATTR_READ_DEFAULT_GROUP] = $value; return true; } break; } return false; } public function getAttribute($attribute){ if(PDO::ATTR_DRIVER_NAME == $attribute){ return 'mysql'; } } public function exec($statement){ $result = $this->handle->query($statement); if(is_object($result)){ mysqli_free_result($result); return 0; } return $this->handle->affected_rows; } public static function getAvailableDrivers(){ return array('mysql'); } public function prepare($statement){ $this->tmpParams = array(); $newstatement = preg_replace_callback('/(:\w+)/i', function($matches){ $this->tmpParams[] = $matches[1]; return '?'; }, $statement); $s = $this->handle->prepare($newstatement); if($s==false) { throw new PDOException($this->handle->error); } $ostatement = new PDO_Mysql_Statement($s, $this); $ostatement->setPrepareParams($this->tmpParams); $ostatement->setStateSql($statement); return $ostatement; } public function lastInsertId(){ return $this->handle->insert_id; } public function quote($param,$parameter_type=-1){ switch($parameter_type) { case PDO::PARAM_BOOL:return $param ? 1 : 0; case PDO::PARAM_NULL:return 'NULL'; case PDO::PARAM_INT: return is_null($param) ? 'NULL' : (is_int($param) ? $param : (float)$param); default:return '\'' . $this->handle->real_escape_string($param) . '\''; } } public function close(){ $this->handle->close(); } public function disconnect(){ $this->close(); } public function __destruct() { $this->close(); }}class PDO_Mysql_Statement { private $_statement = NULL; private $_connnection = NULL; private $_pql = 'unknow'; private $_typeMap = array( 'i'=>PDO::PARAM_INT, 's'=>PDO::PARAM_STR, 'd'=>PDO::PARAM_INT ); private $prepareParams =array();// private $readyTypes = array(); private $readyValues = array(); private $_result = NULL; private $_mode = MYSQL_BOTH; public function __construct($_statement,$connnection){ $this->_statement = $_statement; $this->_connnection = $connnection; } public function getPdoType($type){ static $map=array( 'boolean'=>PDO::PARAM_BOOL, 'integer'=>PDO::PARAM_INT, 'string'=>PDO::PARAM_STR, 'NULL'=>PDO::PARAM_NULL, ); return isset($map[$type]) ? $map[$type] : PDO::PARAM_STR; } public function bindParam($parameter,$value,$type){ $type = array_search($type, $this->_typeMap); $key = array_search($parameter, $this->prepareParams); if($key!==false and $type!==false){ $this->readyTypes[$key] = $type; $this->readyValues[$key] = $value; return true; }else{ return false; } } //这里bindValue已经失去了本应该有的特性 public function bindValue($parameter,$value,$type){ return $this->bindParam($parameter, $value, $type); } public function setStateSql($sql){ $this->_pql = $sql; } //2014-9-27添加$params public function execute($params=array()){ if(!empty($params)){ foreach($params as $_k=>$_v){ $this->bindParam($_k, $_v, $this->getPdoType(gettype($_v))); } } if(!empty($this->readyTypes)){ $params =$this->readyValues; ksort($params); array_unshift($params,implode($this->readyTypes)); $tempstatement = $this->_statement; call_user_func_array(array($tempstatement,'bind_param'),$this->refValues($params)); } $this->_statement->execute(); } public function rowCount(){ return $this->_statement->affected_rows; } public function setFetchMode($mode){ $mode = $this->transformFetchMode($mode); if($mode === false){ return false; } $this->_mode = $mode; return true; } public function closeCursor(){ //$this->_result = NULL; $this->prepareParams =array(); $this->readyTypes = array(); $this->readyValues = array(); $this->_pql = 'unknow'; $this->_mode = MYSQL_BOTH; if(!empty($this->_result)){ $this->_result->free(); } $this->_result = NULL; //$this->_connnection->close(); return $this->_statement->reset(); } public function columnCount(){ return $this->_statement->field_count; } public function debugDumpParams(){ echo $this->_pql; } public function errorCode(){ return $this->_statement->errno; } public function errorInfo(){ return array_values($this->_statement->error_list); } public function setPrepareParams($params){ $this->prepareParams = $params; } public function fetch($mode=NULL){ if($this->_result==NULL){ $this->_result = $this->_statement->get_result(); } if(empty($this->_result)){ throw new PDOException($this->_statement->error); } $_mode = $this->_mode; if(!empty($mode) and ($mode = $this->transformFetchMode($mode))!=false){ $_mode = $mode; } $result = $this->_result->fetch_array($_mode); return $result === NULL ? false : $result; } public function fetchColumn($column_number=0){ $column = $this->fetch(PDO::FETCH_NUM); return $column[$column_number]; } public function fetchAll($mode=NULL){ if($this->_result==NULL){ $this->_result = $this->_statement->get_result(); } if(empty($this->_result)){ throw new PDOException($this->_statement->error); } $_mode = $this->_mode; if(!empty($mode) and ($mode = $this->transformFetchMode($mode))!=false){ $_mode = $mode; } $result = $this->_result->fetch_all($_mode); return $result === NULL ? false : $result; } public function fetchObject(){ throw new PDOException('Not supported yet'); } private function transformFetchMode($mode){ switch ($mode){ case PDO::FETCH_ASSOC : return MYSQLI_ASSOC; case PDO::FETCH_BOTH : return MYSQLI_BOTH; case PDO::FETCH_NUM : return MYSQLI_NUM; default : return false; } } private function refValues($arr){ $refs = array(); foreach($arr as $key => $value){ if($key!=0){ $refs[$key] = &$arr[$key]; }else{ $refs[$key] = $value; } } return $refs; } public function __destruct(){ if(!empty($this->_result)) { $this->_result->free(); } if(!empty($this->_statement)){ $this->_statement->close(); } } }

都是PDO的方法,就不加注释了,在index.php添加了两行;

/** * 服务器目前不支持pdo_mysql连接方式,如果支持了,请删除此处代码,并删除components相关文件; * 本地环境是用PDO_Mysql模拟,测试 */if(!in_array('mysql', PDO::getAvailableDrivers())){     $config = require($config);    $config['components']['db']['pdoClass'] = 'PDO_Mysql';}

中间还遇到一个不得不说的问题,Yii的CDbDataReader 实现了Iterator接口,当使用foreach进行遍历时,PDOStatement::fetch()在获取不到行的时候必须要返回boolean,返回NULL将是死循环;

最后也提一下伪静态的问题,对于框架路由的方式,nginx 配置pathinfo支持,不仅麻烦而且有一定的风险,其实大可不必,rewrite 就可以了

rewrite ^/html/(.*)$ /html/index.php?r=$1;

其他相关代码和配置就不便分享了;

PS: stackoverflow真是个好地方,解决了我好多问题比如这个refValues方法;

转载于:https://my.oschina.net/empty125/blog/213948

你可能感兴趣的文章
关于Linux下s、t、i、a权限
查看>>
js 获取CSS样式
查看>>
Symfony2安装时欢迎页面CSS混乱的解决方案
查看>>
Selenium-webdriver 系列Python教程(3)————如何执行一段JS
查看>>
Apple 企业开发者账号&邓白氏码申请记录
查看>>
视差动画原理分析【1】
查看>>
JavaScript基础(三):语句和符号
查看>>
分治法
查看>>
Windows下Zookeeper简单配置
查看>>
lucene4.7 过滤Filter(六) ---特殊的filter(DuplicateFilte)
查看>>
Oracle——22序列(sequence)
查看>>
Javascript全局函数
查看>>
jQuery表格插件和分页插件
查看>>
基于AOP动态切换数据源实现读写分离
查看>>
multer上传文件并在前台显示
查看>>
Android布局--FrameLayout
查看>>
makefile(四)使用变量
查看>>
[iOS开发]iOS列表单元格高度不固定
查看>>
android学习笔记之一常用控件
查看>>
nginx正向代理配置
查看>>