Yiiがpdo_をサポートしていない場合mysqlのサーバー

12372 ワード

http://www.rritw.com/a/shujuku/Mysql/20140329/442735.html
これは本当にうっとうしいことです.プロジェクトのサブプロジェクト(cms)の中で擬静的かつ迅速に完成するためにYiiを選択しました.
    各方面に用意されています.ルート規則、inx rewrite、mysql slaave…しかし、サーバーにpdoがありません.mysql駆動、その時はびっくりしました.プロジェクトはすでにオンラインになりました.再コンパイルはもうあまり実際的ではありません.ファイルを考えるしかないです.つまり、YiiはPDO接続方式を提供しているだけです.時間が迫っています.その時に思いつきました解決方法:
  • 他のサーバからphp_をコピーします.mysql.so
  • sql処理ロジックを書き換え、mysqli実現(Yiiを捨てたDAO)に変更する.
  • mysqliシミュレーションpdoを使用する.
  • 最初の方法は一番簡単ですが、そうしなかったです.php-fpmを再起動する必要があります.成功と安定性を保証するのは難しいです.
    二つ目は同僚の勧めで、コードの量はとても大きいです.それに、こうすれば後戻りが難しくなります.もう一度テストしなければなりません.
    第三のは悪くない解決策です.そしてYiiは指定pdoClassを支持しています.フレームと既存のコードに侵入性がなく、短期間でも実現できます.だからこれにします.
    データベース読み取り専用なので、あまり多く考えず、すべての方法を実現するつもりもなく、YiiのDAO処理部分を読んで、この二つのクラスを簡単に書きました.
    <?php
    /**
     *   mysqli  PDO           PDO_mysql -_-||
     * @author xl
     * create on 2014-3-26
     */
    class PDO_Mysql{//extends PDO
        
        private $handle = NULL;
        
        private $tmpParams = array();
        
        const MYSQL_ATTR_USE_BUFFERED_QUERY = 1000;
        const MYSQL_ATTR_LOCAL_INFILE       = 1001;
        const MYSQL_ATTR_INIT_COMMAND       = 1002;
        const MYSQL_ATTR_READ_DEFAULT_FILE  = 1003;
        const MYSQL_ATTR_READ_DEFAULT_GROUP = 1004;
        const MYSQL_ATTR_MAX_BUFFER_SIZE    = 1005;
        const MYSQL_ATTR_DIRECT_QUERY       = 1006;
    
        public function __construct($connectionString,$username,$password,$options=array()){ 
            //    
            preg_match('/host=([\w\.]+);dbname=(\w+)/i', $connectionString,$matches);
            if(count($matches)<3){
                throw new PDOException('connectionString is invalid');
            }
            $this->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_STR
        );   
       
    
        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 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;
            }
        }
        
        public function bindValue($parameter,$value,$type){
            return $this->bindParam($parameter, $value, $type);
        }
        
        public function setStateSql($sql){
            $this->_pql = $sql;
        }
    
    
        public function execute($options=array()){
            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->num_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で2行追加しました.
    /**
     *         pdo_mysql    ,     ,       ,   components    ;
     *       PDO_Mysql  ,  
     */
    if(!in_array('mysql', PDO::getAvailableDrivers())){ 
        $config = require($config);
        $config['components']['db']['pdoClass'] = 'PDO_Mysql';
    }
    途中で言わなければならない問題がありました.YiiのCDbDataReaderはIteratorインターフェースを実現しました.foreachを使って全期間を使います.PDOSTtement:fetch()は入手できない時にブートに戻らなければなりません.NULLに戻るのは死のサイクルです.
    最後に、偽静的な問題を提起します.フレームのルートの方式に対して、インゲンの設定pathinfoはサポートしています.面倒なだけではなく、一定のリスクがあります.実は、必要ないです.rewriteでいいです.
    rewrite ^/html/(.*)$ /html/index.php?r=$1;
    他の関連コードと配置は共有できません.
    PS: stackoverflowは本当にいいところです.たくさんの問題を解決しました.例えば、このrefValuesの方法です.