常用知识 - count的细节

  • 作者:KK

  • 发表日期:2016.8.8


var_export([
	'count([132, 21, 3836, 7874])' => count([132, 21, 3836, 7874]), //4  数组
	'count(["xx", "yy", "zz"])' => count(["xx", "yy", "zz"]), //3  数组
	
	//非数组,一般都是1
	'count(false)' => count(false), //1
	'count(true)' => count(true), //1
	'count(0)' => count(0), //1
	'count(1)' => count(1), //1
	'count(3)' => count(3), //1
	'count("abc")' => count("abc"), //1
	'count($obj)' => count(new stdClass()), //1  对象哦
	
	//null的话就是0
	'count(null)' => count(null), //0
]);

官方的count函数手册返回值里提到:如果 var 不是数组类型或者实现了Countable 接口的对象,将返回 1,有一个例外,如果 var 是 NULL 则结果是 0(相关阅读:官方手册 - Countable接口

所以估计你要抽空审查一下以前用过的count函数了,只要count的参数不是个数组就会出问题


常见场景:

  1. db类的查询代码是这样设计的:

    public function select(){
    	$data = $this->query($this->buildSql());
    	if($data){
    		return $data;
    	}else{
    		return false;
    	}
    }
    

    然后调用的地方就这么写:

    $userList = $db->table('user')->where(['sex' => 2])->select();
    if(count($userList) == 0){
    	showMessage('没有数据');
    }
    

    于是其实这个count的结果永远不会为0,至少是1

    • 解决办法:

      $userList = $db->table('user')->where(['sex' => 2])->select();
      if(!$userList){
      	showMessage('没有数据');
      }
      		
      //或者
      if(empty($userList)){
      	showMessage('没有数据');
      }
      

  2. 跟场景1有点类似,但确实也很常见。这一般是封装了一个函数去获取某种业务数据的数据列表,而不是直接调用db去查询,主要是函数的逻辑是:如果没有数据就返回false

    /**
     * 一个获取今天或最近7天新注册用户的列表
     * @param int $beforeTime 距离现在多少秒,默认1个星期
     * @return array|boolean
     */
    function getNewUserList($beforeTime = 604800){
    	$userList = $db->table('user')->where(['add_time' => time() - $beforeTime])->select();
    	if(!$userList){
    		return false;
    	}else{
    		return $userList;
    	}
    }
    

    //在模板里:

    <?php
    $newUserNums = count(getNewUserList());
    if($newUserNums){ ?>
    	<label>一共有<?php echo $newUserNums; ?>个新用户<label>
    <?php }else{ ?>
    	<label>这一周来都没有新用户<label>
    <?php } ?>
    

    结果就是:永远都不会提示“这一周来都没有新用户”

    • 解决办法:

      /**
       * 一个获取今天或最近7天新注册用户的列表
       * @param int $beforeTime 距离现在多少秒,默认1个星期
       * @return array
       */
      function getNewUserList($beforeTime = 604800){
      	$userList = $db->table('user')->where(['add_time' => time() - $beforeTime])->select();
      	if(!$userList){
      		return []; //无论有没有数据,返回的都是一个数组,只是那是一个空数组而已,表示里面没有元素,也可以表达“列表里没有新用户”的意思,所以我们不能认为所有函数在不能按照预期得到相应的数据时都返回false,在数据列表这些地方,可以返回空数组,表示空列表,也是空嘛
      	}else{
      		return $userList;
      	}
      }
      

    致我的团队成员们:这就是为什么我总是要求 获取列表 这类的业务方法必须总是返回数组的原因了


  3. 被胡乱伪造的请求参数绕过简单的判断逻辑:

    /*
    这是一个文章批量删除接口,服务端预期如下:
    $_POST = [
    	'ids' => [888, 999, 101010]
    ]
    	
    然而实际上遭遇非预期格式的请求参数就报错了:
    $_POST = [
    	'ids' => [
    		'搞屎你1' => 11,
    		'搞屎你2' => 22,
    		'搞屎你X' => [1, 2, 3, 45, 5, 5, 543],
    	]
    ];
    */
    
    $ids = post('ids');
    if(count($ids) == 0){
    	showError('请选择要删除的文章');
    }
    	
    $db->table('article')->where(['in', 'id', $ids])->delete();
    
    • 解决办法:

      $ids = (array)post('ids'); //用强转或is_array判断
      if(count($ids) == 0){
      	showError('请选择要删除的文章');
      }
      		
      foreach($ids as &$id){
      	$id = (int)$id; //强转子元素也很重要,或者你用is_numerice来判断
      }
      		
      $db->table('article')->where(['in', 'id', $ids])->delete();