开启Memcache缓存后后台社区QQ群搜索不到帖子的问题排查
问题描述:
此问题发现于Discuz! X2.5版本。
开启了Memcache缓存,后台社区QQ群推送消息页面按tid搜索不到帖子或搜索出来的帖子不对,如图。
问题分析:
找到source\admincp\cloud\cloud_qqgroup.php文件,有如下代码:
if($srchtid) { $threads = C::t('forum_thread')->fetch_all_by_tid_displayorder($srchtid, 0); }
这里会调用fetch_all_by_tid_displayorder方法,找到source\class\table\table_forum_thread.php文件,有如下代码:
public function fetch_all_by_tid_displayorder($tids, $displayorder = null, $glue = '>=', $fids = array(), $closed = null) { $data = array(); if(!empty($tids)) { $data = $this->fetch_all_by_tid($tids); $fids = $fids && !is_array($fids) ? array($fids) : $fids; foreach($data as $tid => $value) { if($displayorder !== null && !(helper_util::compute($value['displayorder'], $displayorder, $glue))) { unset($data[$tid]); } elseif(!empty($fids) && !in_array($value['fid'], $fids)) { unset($data[$tid]); } elseif($closed !== null && $value['closed'] != $closed) { unset($data[$tid]); } } } return $data; }
注意这个函数的参数:$tids应该为数组。这里调用了fetch_all_by_tid方法,还是这个文件,有如下代码:
public function fetch_all_by_tid($tids, $start = 0, $limit = 0, $tableid = 0) { $data = array(); if(($data = $this->fetch_cache($tids)) === false || count($tids) != count($data)) { if(is_array($data) && !empty($data)) { $tids = array_diff($tids, array_keys($data)); } if($data === false) $data = array(); if(!empty($tids)) { $parameter = array($this->get_table_name($tableid), $tids); $query = DB::query("SELECT * FROM %t WHERE tid IN(%n)".DB::limit($start, $limit), $parameter); while($value = DB::fetch($query)) { $data[$value['tid']] = $value; $this->store_cache($value['tid'], $value, $this->_cache_ttl); } } } return $data; }
注意这个函数的参数:$tids应该为数组。这里又调用到fetch_cache和store_cache方法。找到source\class\discuz\discuz_table.php文件,里面有这两个方法的定义,
public function fetch_cache($ids, $pre_cache_key = null) { $data = false; if($this->_allowmem) { if($pre_cache_key === null) $pre_cache_key = $this->_pre_cache_key; $data = memory('get', $ids, $pre_cache_key); } return $data; } public function store_cache($id, $data, $cache_ttl = null, $pre_cache_key = null) { $ret = false; if($this->_allowmem) { if($pre_cache_key === null) $pre_cache_key = $this->_pre_cache_key; if($cache_ttl === null) $cache_ttl = $this->_cache_ttl; $ret = memory('set', $id, $data, $cache_ttl, $pre_cache_key); } return $ret; }
这两个方法最终调用缓存类的get和set方法,找到source\class\discuz\discuz_memory.php文件,有如下代码:
public function get($key, $prefix = '') { static $getmulti = null; $ret = false; if($this->enable) { if(!isset($getmulti)) $getmulti = method_exists($this->memory, 'getMulti'); $this->userprefix = $prefix; if(is_array($key)) { if($getmulti) { $ret = $this->memory->getMulti($this->_key($key)); //格式化数组的KEY,去掉表前缀 if($ret !== false && !empty($ret)) { $_ret = array(); foreach((array)$ret as $_key => $value) { $_ret[$this->_trim_key($_key)] = $value; } $ret = $_ret; } } else { $ret = array(); $_ret = false; //循环取值 foreach($key as $id) { if(($_ret = $this->memory->get($this->_key($id))) !== false && isset($_ret)) { $ret[$id] = $_ret; } } } //无值返回false if(empty($ret)) $ret = false; } else { $ret = $this->memory->get($this->_key($key)); if(!isset($ret)) $ret = false; } } return $ret; } public function set($key, $value, $ttl = 0, $prefix = '') { $ret = false; if($value === false) $value = ''; if($this->enable) { $this->userprefix = $prefix; $ret = $this->memory->set($this->_key($key), $value, $ttl); } return $ret; }
注意get方法里,有对$key进行是否为数组的判断,如下代码:
if(is_array($key)) {
这里如果是数组则返回的结果为二维数组,key => value的格式;反之返回的结果是一维数组,只有value。
通过记录log发现source\admincp\cloud\cloud_qqgroup.php文件调用fetch_all_by_tid_displayorder方法的返回结果为一维数组,只有value,而正常来说fetch_all_by_tid_displayorder方法返回的结果应该是二维数组结构。
所以推断是由于source\admincp\cloud\cloud_qqgroup.php文件调用fetch_all_by_tid_displayorder方法时传递的$srchtid参数类型非数组导致的问题,进一步分析发现是由于Discuz! 所有要求参数为数组的地方没有进行是否是数组类型的判断导致。
解决方法:
不给出暂时的解决方法了,静待Discuz! 修复。