add imi-swoole/src/Db/Driver/Swore/Statement.php

主题:

Imi框架将默认的PdoMysqlDriver引擎切换为 SwooleMysqlDriver引擎之后报错

[PHP]
Version: v8.0.17
Swoole: v4.8.8
imi: v2.1.6 (0d8219e)
Timezone: PRC
Opcache: Not

imiphp/imi-swoole    v2.1.7

报错内容:

类似 expects 11 parameter, 10 given ,就是少了参数

追踪源头:

1、Imi\Db\Query::buildUpdateSql、buildInsertSql等SQL构建时,无论参数是否为null,均会构建在sql语句中,例如:

update `system_crontab` set `name` = :name,`type` = :type,`target` = :target,`params` = :params,`rule` = :rule,`singleton` = :singleton,`status` = :status,`remark` = :remark,`update_op` = :update_op,`update_time` = :update_time where `id` = :p1 limit 1

这里总共11个参数

2、Imi\Swoole\Db\Driver\Swoole\Statement::execute(array $inputParameters = null): bool 在处理避免客户端提交超过DB Model的数据时,对参数进行了处理,如下:

$sqlParamsMap = $this->sqlParamsMap;
if ($sqlParamsMap)
{
  foreach ($sqlParamsMap as $index => $paramName)
  {
    if (isset($inputParameters[$paramName]))
    {
    	$bindValues[$index] = $inputParameters[$paramName];
    }
    elseif (isset($inputParameters[$key = ':' . $paramName]))
    {
    	$bindValues[$index] = $inputParameters[$key];
    }
  }
}

这样,当传输的 inputParameters 中包含 null,就会被isset 给过滤掉,例如客户端提交的参数为:

{
  ":name":"urlCrontab",
  ":type":"curl",
  ":target":"http:\/\/127.0.0.1:9501\/123456",
  ":params":null,  <== 注意这里的,不可避免的有空值,除非在控制器进行特殊处理;
  ":rule":"59 *\/1 * * * *",
  ":singleton":"1",
  ":status":"1",
  ":remark":"\u8bf7\u6c42127.0.0.1",
  ":update_op":1,
  ":update_time":1649513242,
  ":p1":1
}

这里总共11个参数,与前面Query构建的sql是一致的。

经过上面的过滤函数之后,数据就变成了:

{
  "0":"urlCrontab",
  "1":"curl",
  "2":"http:\/\/127.0.0.1:9501\/123456",
  <== 注意这里,第三个参数数据丢了
  "4":"59 *\/1 * * * *",
  "5":"1",
  "6":"1",
  "7":"\u8bf7\u6c42127.0.0.1",
  "8":1,
  "9":1649513242,
  "10":1
}

最终导致报错,数据与sql对于需求的参数数量不一致。

解决方案

Imi\Swoole\Db\Driver\Swoole\Statement的execute函数,第189行修改

$sqlParamsMap = $this->sqlParamsMap;
if ($sqlParamsMap)
{
  foreach ($sqlParamsMap as $index => $paramName)
  {
    if (isset($inputParameters[$paramName]))
    {
      $bindValues[$index] = $inputParameters[$paramName];
    }
    elseif (isset($inputParameters[$key = ':' . $paramName]))
    {
      $bindValues[$index] = $inputParameters[$key];
    }else{
      // for inputParameters paramName : null
      $bindValues[$index] = null;
    }
  }
}

因为php函数中isset不仅是检测参数是否存在,也同样包含参数为null,而参数存在为null是正确的,所以最后要给他补回来。

最终,整个程序的test都能正确运行。