使用Redis缓存非字符串类型数据

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);

$redis = new Redis();
$redis->connect('localhost');
$redis->auth('************');

/**
 * 设置缓存
 *
 * @param string $key 缓存KEY
 * @param mixed $value 缓存VALUE(支持各种数据类型,不需要预先转成字符串)
 * @param int $timeout 有效时间(单位:秒),若设为0则永久有效
 * @return bool true=设置缓存成功|false=设置缓存失败
 */
function set_cache(string $key, mixed $value, int $timeout): bool
{
    global $redis;

    // 重要说明:不管缓存VALUE是什么数据类型,都将其作为数组元素构造一个数组
    $array = [$value];
    $json = json_encode($array);

    return $redis->set($key, $json, $timeout);
}

/**
 * 获取缓存
 *
 * @param string $key 缓存KEY
 * @return mixed 缓存VALUE(设置缓存时是什么类型,获取到的就是什么类型)
 */
function get_cache(string $key): mixed
{
    global $redis;

    // 在设置缓存时对数据进行了json_encode()处理,所以这里要json_decode()
    $json = $redis->get($key);
    $array = json_decode($json, true);

    return $array[0];
}

$array = ['name' => '张三', 'gender' => '男', 'age' => 18];

//========== 直接保存数组 ==========//
$redis->set('array', $array, 3600);
var_dump($redis->get('array')); // string(5) "Array"


//========== 直接保存整数 ==========//
$redis->set('int', 9527, 3600);
var_dump($redis->get('int')); // string(4) "9527"


//========== 直接保存浮点数 ==========//
$redis->set('float', 3.14, 3600);
var_dump($redis->get('float')); // string(4) "3.14"


//========== 直接保存false ==========//
$redis->set('true', true, 3600);
var_dump($redis->get('true')); // string(1) "1"


//========== 直接保存false ==========//
$redis->set('false', false, 3600);
var_dump($redis->get('false')); // string(0) ""


//========== 直接保存null ==========//
$redis->set('null', null, 3600);
var_dump($redis->get('null')); // string(0) ""


set_cache('array', $array, 3600);
var_dump(get_cache('array')); // array(3) { ["name"]=> string(6) "张三" ["gender"]=> string(3) "男" ["age"]=> int(18) }


set_cache('int', 9527, 3600);
var_dump(get_cache('int')); // int(9527)


set_cache('float', 3.14, 3600);
var_dump(get_cache('float')); // float(3.14)


set_cache('true', true, 3600);
var_dump(get_cache('true')); // bool(true)


set_cache('false', false, 3600);
var_dump(get_cache('false')); // bool(false)


set_cache('null', null, 3600);
var_dump(get_cache('null')); // NULL


//========== 总结 ==========//
// 1、Redis只能保存字符串,如果直接将非字符串数据保存进去,那么取出数据时会变成字符串,尤其是数组,所有key/value内容都会丢失。
// 2、由于Redis只能保存字符串,所以想保存非字符串数据就必须对VALUE进行编码转成字符串,而最佳实现方式就是把VALUE作为数组元素,
//    构造一个数组并对数组进行JSON编码,这样不仅仅是数组,其它如int、float、bool等类型数据也能保存,并且在获取VALUE时不会丢失
//    VALUE的数据类型信息。
// 3、需要注意的是如果指定的缓存KEY不存在Redis::get()会返回false,这时如果缓存数据本身就是false就会有歧义,这种情况可根据实际
//    需求制定规则,例如不允许缓存null但允许缓存false,如果缓存不存在则返回null。(PS:可用Redis::exists()判断缓存是否存在)



  封装版

<?php
declare(strict_types=1);

namespace MaNong;

// 自定义类的类名也是Redis,所以需要给PHP扩展的Redis类设置别名
use Redis as PHPRedis;

use JsonException;
use RedisException;

use function is_array;
use function is_string;

ini_set('display_errors', 'On');
error_reporting(-1);

/**
 * Redis类(单例模式)
 */
class Redis
{
    private ?PHPRedis $redis = null; // Redis实例(PHP扩展)

    private static ?Redis $instance = null; // Redis实例(自定义Redis类,非PHP扩展的Redis类)

    /**
     * 将构造方法声明为private防止通过new关键字创建实例
     */
    private function __construct()
    {
    }

    /**
     * 将克隆方法声明为private防止复制实例
     */
    private function __clone()
    {
    }

    /**
     * 设置缓存
     *
     * @param string $key 缓存KEY
     * @param mixed $value 缓存VALUE,除了resource类型之外,可以为任何数据类型
     * @param int $timeout 有效时间,单位为秒,若设为0则永久有效
     * @return bool true=设置缓存成功|false=设置缓存失败
     */
    public static function set(string $key, mixed $value, int $timeout): bool
    {
        // 本方法规定有效时间为0表示永久有效,但Redis规定null才是永久有效,所以需要处理一下
        $expire = $timeout > 0 ? $timeout : null;

        // Redis的缓存VALUE只能为string类型,所以需要先编码再保存
        $json = self::encode($value);

        return is_string($json) && self::getInstance()->redis->set($key, $json, $expire);
    }

    /**
     * 获取缓存
     *
     * @param string $key 缓存KEY
     * @return mixed 缓存VALUE(设置缓存时是什么类型,获取到的就是什么类型),若缓存不存在则返回null
     */
    public static function get(string $key): mixed
    {
        $value = null;

        $json = self::getInstance()->redis->get($key);
        if (is_string($json)) {
            $array = self::decode($json);
            if (is_array($array) && isset($array[0])) {
                $value = $array[0];
            }
        }

        return $value;
    }

    /**
     * 获取本类实例
     *
     * @return Redis 本类实例(自定义Redis类,非PHP扩展的Redis类)
     */
    private static function getInstance(): Redis
    {
        if (self::$instance === null) {
            self::$instance = new self();

            self::$instance->redis = new PHPRedis();

            try {
                $connect = self::$instance->redis->connect('localhost');
            } catch (RedisException $e) {
                exit('Redis服务器连接失败:' . $e->getMessage());
            }

            try {
                $auth = self::$instance->redis->auth('************');
            } catch (RedisException $e) {
                exit('Redis服务器认证失败:' . $e->getMessage());
            }

            if (!$connect || !$auth) {
                exit('Redis服务器连接(或认证)失败');
            }
        }

        return self::$instance;
    }

    /**
     * 对变量进行JSON编码
     *
     * @param mixed $value 待编码的value,除了resource类型之外,可以为任何数据类型
     * @return string|false 成功则返回JSON编码的string或者在失败时返回false
     */
    private static function encode(mixed $value): string|false
    {
        $encode = false;

        try {
            $encode = json_encode([$value], JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        } catch (JsonException $e) {
            unset($e);
        }

        return $encode;
    }

    /**
     * 对JSON格式的字符串进行解码
     *
     * @param string $json 待解码的json string格式的字符串
     * @return mixed 返回编码的数据,若解码失败将会返回null
     */
    private static function decode(string $json): mixed
    {
        $decode = null;

        try {
            $decode = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
        } catch (JsonException $e) {
            unset($e);
        }

        return $decode;
    }
}

/**
 * 打印变量的相关信息
 *
 * @param mixed $value 要打印的表达式
 * @param mixed ...$values 更多要打印的表达式
 * @return void echo
 */
function v(mixed $value, mixed ...$values): void
{
    ob_start(); // 打开输出控制缓冲
    var_dump($value);
    echo ob_get_clean(); // 从缓冲区获取var_dump()的内容,然后清空缓冲区

    foreach ($values as $v) {
        v($v); // 递归
    }
}

// 测试用的缓存KEY和缓存VALUE
$key = 'profile';
$value = ['name' => '张三', 'gender' => '男', 'birth' => 2003];

// 设置缓存
$ok = Redis::set($key, $value, 0);
echo '设置缓存' . ($ok ? '成功' : '失败') . PHP_EOL; // 设置缓存成功

// 获取缓存
$profile = Redis::get($key . '');
v($profile); // array(3) { ["name"]=> string(6) "张三" ["gender"]=> string(3) "男" ["birth"]=> int(2003) }

Copyright © 2024 码农人生. All Rights Reserved