前段经常看同事的接口bug是报这个错:“unserialize() Error at offset 76 of 176 bytes",
然后清理下缓存就不报错了,虽然觉得诡异,但由于事情比较多,也没时间去深究。
今天在处理关于客服系统的问题的时候,也遇到了这个问题,就索性探一探究竟。
首先可以确定的是,报错的地方是在缓存cache()的底层逻辑,背景是客服系统的框架是tp5,
客户系统的框架是tp6;于是对比了下两个版本的cache,
发现tp6的cache在存入、取出缓存的驱动(Driver.php)上做了很大的改动
tp5的存入取出代码如下:

/**
 * 序列化数据
 * @access protected
 * @param  mixed $data
 * @return string
 */
protected function serialize($data)
{
    if (is_scalar($data) || !$this->options['serialize']) {
        return $data;
    }

    $serialize = self::$serialize[0];

    return self::$serialize[2] . $serialize($data);
}

/**
 * 反序列化数据
 * @access protected
 * @param  string $data
 * @return mixed
 */
protected function unserialize($data)
{
    if ($this->options['serialize'] && 0 === strpos($data, self::$serialize[2])) {
        $unserialize = self::$serialize[1];

        return $unserialize(substr($data, self::$serialize[3]));
    } else {
        return $data;
    }
}

$serialize 参数的值

/**
 * 序列化方法
 * @var array
 */
protected static $serialize = ['serialize', 'unserialize', 'think_serialize:', 16];

tp6的存入取出代码如下:

/**
 * 序列化数据
 * @access protected
 * @param mixed $data 缓存数据
 * @return string
 */
protected function serialize($data): string
{
    if (is_numeric($data)) {
        return (string) $data;
    }

    $serialize = $this->options['serialize'][0] ?? "serialize";

    return $serialize($data);
}

/**
 * 反序列化数据
 * @access protected
 * @param string $data 缓存数据
 * @return mixed
 */
protected function unserialize(string $data)
{
    if (is_numeric($data)) {
        return $data;
    }

    $unserialize = $this->options['serialize'][1] ?? "unserialize";

    return $unserialize($data);
}

看到这里,大概就知道为什么在tp6获取cache值的时候会报这个错了。
tp5的存入值是否加serialize序列化是判断传入$data是否是标量is_scalar,
而tp6是判断是否为数值is_numeric,导致两边数据不一致。
知道了原因,解决方法就个显神通了,当前的方式比较粗暴,直接修改底层框架,
在tp6 unserialize的方法里面修改判断是否反序列化的逻辑,代码如下:

    /**
     * 反序列化数据
     * @access protected
     * @param string $data 缓存数据
     * @return mixed
     */
    protected function unserialize(string $data)
    {
        //判断$data是否为序列化字符串,如果不是直接返回不需要反序列化
        if (!preg_match( '/^[asO]:[0-9]+:/s', $data)) {
            return $data;
        }

        $unserialize = $this->options['serialize'][1] ?? "unserialize";

        return $unserialize($data);
    }