代码说

code is poetry

代码说    
碎碎念:勇敢不是无所畏惧,而是心怀恐惧,却依然向前。  换一换

解决TP3.2中新增数据库表字段后查询无效的问题

作者:coderzheng 发布于:2017-2-15 17:02 Wednesday 分类:php  阅读模式

前段时间在TP3.2中做开发时碰到一个诡异的问题,数据库的某个表新增了一个字段,然后在查询条件中新增了这部分条件代码,如图:

奇怪的是,刷新页面之后这段代码似乎对查询结果没有任何影响,后来经过同事提醒,才知道数据库结构变动之后要主动清除TP框架的缓存。问题虽然得到了解决,但是我仍心有余悸:TP3.2究竟对数据库查询做了什么,为什么缓存会对实时查询产生影响?
经过仔细排查,最后我们跟踪到了这里:
public function select($options=array()) {
        $pk   =  $this->getPk();
        if(is_string($options) || is_numeric($options)) {
            // 根据主键查询
            if(strpos($options,',')) {
                $where[$pk]     =  array('IN',$options);
            }else{
                $where[$pk]     =  $options;
            }
            $options            =  array();
            $options['where']   =  $where;
        }elseif (is_array($options) && (count($options) > 0) && is_array($pk)) {
            // 根据复合主键查询
            $count = 0;
            foreach (array_keys($options) as $key) {
                if (is_int($key)) $count++; 
            } 
            if ($count == count($pk)) {
                $i = 0;
                foreach ($pk as $field) {
                    $where[$field] = $options[$i];
                    unset($options[$i++]);
                }
                $options['where']  =  $where;
            } else {
                return false;
            }
        } elseif(false === $options){ // 用于子查询 不查询只返回SQL
        $options['fetch_sql'] = true;
        }
        // 分析表达式
        $options    =  $this->_parseOptions($options); ...
在_parseOptions方法中:
protected function _parseOptions($options=array()) {
        if(is_array($options))
            $options =  array_merge($this->options,$options);

        if(!isset($options['table'])){
            // 自动获取表名
            $options['table']   =   $this->getTableName();
            $fields             =   $this->fields;
        }else{
            // 指定数据表 则重新获取字段列表 但不支持类型检测
            $fields             =   $this->getDbFields();
        }

        // 数据表别名
        if(!empty($options['alias'])) {
            $options['table']  .=   ' '.$options['alias'];
        }
        // 记录操作的模型名称
        $options['model']       =   $this->name;

        // 字段类型验证
        if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) {
            // 对数组查询条件进行字段类型检查
            foreach ($options['where'] as $key=>$val){
                $key            =   trim($key);
                if(in_array($key,$fields,true)){
                    if(is_scalar($val)) {
                        $this->_parseType($options['where'],$key);
                    }
                }elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){
                    if(!empty($this->options['strict'])){
                        E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']');
                    } 
                    unset($options['where'][$key]);
                }
            }
        }
...
在这部分程序中,我们发现,传入的一部分查询条件被过滤了( unset($options['where'][$key])),原因是我们新增的字段被存储到$options['where']的key中,但是这个key并没有存在于$fields中。接下来我们来追踪$options的来源:
我们看到:$options数组的内容是在执行select操作时执行的。那么$fields这个变量的内容又是在哪儿初始化的呢?


从上图可知,程序在实例化数据库时,就已经初始化了$fields变量。到这里,所有的谜团都解开了。TP会将数据库的表字段信息缓存在文件中,然后当PHP程序执行到相关的数据库操作时,会直接从缓存文件中获取相关的表字段信息,然后进行验证和检查。由于我们新增了字段,却没有更新缓存,新增加的部分查询条件自然就"丢失"了。







OVER.

标签: php 疑难杂症

你可以发表评论、引用到你的网站或博客,或通过RSS 2.0订阅这个博客的所有文章。
上一篇: 夕愁想花  |  下一篇:一次处理一个业务逻辑