├── Cache.php ├── LICENSE ├── README.md └── composer.json /Cache.php: -------------------------------------------------------------------------------- 1 | 12 | * @link https://www.awaimai.com/ 13 | * 14 | */ 15 | class Cache 16 | { 17 | /** 18 | * Redis句柄 19 | * @var Redis 20 | */ 21 | private $redis; 22 | /** 23 | * 正在调用的方法名称 24 | * @var string 25 | */ 26 | private $class; 27 | /** 28 | * 配置 29 | * @var array 30 | */ 31 | private $config = [ 32 | 'prefix' => '', 33 | 'expire' => 3600, // 缓存过期时间 34 | 'emptyExpire' => 10 // 空值的缓存过期时间 35 | ]; 36 | /** 37 | * 可用的Redis操作方法名 38 | * @var array 39 | */ 40 | private $actions = ['cache', 'clear', 'flush']; 41 | 42 | /** 43 | * 构造方法 44 | * @param Redis|null $redis 45 | */ 46 | public function __construct(Redis $redis = null, $config = []) 47 | { 48 | $this->redis = $redis; 49 | $this->config = array_merge($this->config, $config); 50 | } 51 | 52 | /** 53 | * 设置缓存时间 54 | * 55 | * @param $time int 单位秒 56 | */ 57 | public function expire($time) 58 | { 59 | $this->config['expire'] = $time; 60 | } 61 | 62 | /** 63 | * 处理缓存方法 64 | * 65 | * @param $object object 对象 66 | * @param $method string 方法名 67 | * @param $arguments array 参数列表 68 | * @return mixed 69 | * @throws InvalidArgumentException 如果方法不存在,抛出异常 70 | */ 71 | public function get($object, $name, $arguments) 72 | { 73 | if ($this->redis === null) { 74 | throw new InvalidArgumentException("未初始化Redis变量。\n"); 75 | } 76 | 77 | $this->class = get_class($object); 78 | 79 | if (strlen($name) < 5) { 80 | throw new InvalidArgumentException(sprintf("Method %s->%s does not exist", $this->class, $name)); 81 | } 82 | 83 | $method = substr($name, 0, -5); 84 | $action = substr($name, -5); 85 | if (!in_array($action, $this->actions, true) === false) { 86 | throw new InvalidArgumentException(sprintf("Method %s->%s does not exist", $this->class, $method)); 87 | } 88 | 89 | return $this->$action($object, $method, $arguments); 90 | } 91 | 92 | /** 93 | * 获取某个方法返回的数据。 94 | * 如果数据已经缓存,则直接读取缓存数据; 95 | * 如果数据未缓存,则调用实际方法获取数据。 96 | * 实际使用时,请在子类的注释加上`@method`注释,以便编辑器能够自动识别。 97 | * @return mixed 98 | * @throws InvalidArgumentException 如果方法不存在,抛出异常 99 | */ 100 | private function cache($object, $method, $arguments) 101 | { 102 | $key = $this->key($method, $arguments); 103 | 104 | $data = $this->redis->get($key); 105 | if ($data !== false) { 106 | $decodeData = json_decode($data, JSON_UNESCAPED_UNICODE); 107 | return $decodeData === null ? $data : $decodeData; 108 | } 109 | 110 | if (method_exists($object, $method) === false) { 111 | throw new InvalidArgumentException(sprintf("Method %s->%s does not exist", $this->class, $method)); 112 | } 113 | 114 | $data = call_user_func_array([$object, $method], $arguments); 115 | 116 | $expire = empty($data) ? $this->config['emptyExpire'] : $this->config['expire']; 117 | $this->redis->set($key, json_encode($data), $expire); 118 | 119 | return $data; 120 | } 121 | 122 | /** 123 | * 删除指定缓存,参数和原数据获取方法一样 124 | * @return mixed 125 | */ 126 | private function clear($object, $method, $arguments) 127 | { 128 | return $this->redis->del($this->key($method, $arguments)); 129 | } 130 | 131 | /** 132 | * 删除指定方法的所有缓存 133 | * @return bool 134 | */ 135 | private function flush($object, $method, $arguments) 136 | { 137 | $key = $this->key($method, '*'); 138 | $keys = $this->redis->keys($key); 139 | 140 | return $this->redis->del($keys); 141 | } 142 | 143 | /** 144 | * 生成缓存键名 145 | * @return string 146 | */ 147 | private function key($method, $arguments) 148 | { 149 | $class = str_replace('\\', '_', $this->class); 150 | $args = ($arguments === '*') ? '*' : md5(json_encode($arguments)); 151 | 152 | return strtolower(sprintf('%s%s:%s:%s', $this->config['prefix'], $class, $method, $args)); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Gary Meng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 1 使用方法 4 | 5 | 1. 使用composer下载 yeszao/cache 组件: 6 | ``` 7 | composer install yeszao/cache 8 | 9 | ``` 10 | 11 | 2. 在MVC框架`Model`层或者`Service`层的基类添加`__call()`方法,例如: 12 | ``` 13 | namespace app\models; 14 | use yeszao\cache; 15 | 16 | class Base 17 | { 18 | public function __call($name, $arguments) 19 | { 20 | $redis = new \Redis(); 21 | $redis->connect('redis'); 22 | 23 | // ***主要是这一行*** 24 | return (new Cache($redis))->get($this, $name, $arguments); 25 | } 26 | } 27 | ``` 28 | 其中,`yeszao\cache\Cache`对象创建时必须传入 Redis 连接句柄, 29 | 然后再通过`get`方法获取缓存。 30 | 31 | 3. 让子类继承`Base`类,例如: 32 | ``` 33 | namespace app\models; 34 | use app\models\Base; 35 | 36 | class Book extends Base 37 | { 38 | public function getById($id) 39 | { 40 | return ['id' => 100, 'title' => 'PHP documents'] 41 | } 42 | } 43 | 44 | ``` 45 | 4. 然后就可以在`Controllers`类或其他`Model`类中使用: 46 | ``` 47 | (new Book)->getById(100); // 原始的、不用缓存的调用方式,一般是读取数据库的数据。 48 | (new Book)->getByIdCache(100); // 使用缓存的调用方式,缓存键名为:app_models_book:getbyid: + md5(参数列表) 49 | (new Book)->getByIdClear(100); // 删除这个缓存 50 | (new Book)->getByIdFlush(); // 删除 getById() 方法对应的所有缓存,即删除 app_models_book:getbyid:*。这个方法不需要参数。 51 | ``` 52 | 53 | ## 2 构造函数参数 54 | 55 | `yeszao\cache\Cache`构造函数参数: 56 | - `$redis`: 必须。Redis连接对象。 57 | - `$config`: 可选。缓存的一些配置。 58 | - `$config['prefix']`: 缓存键名前缀,默认空字符串。 59 | - `$config['expire']`:缓存过期时间,默认`3600`秒。 60 | - `$config['emptyExpire']`:原函数返回空值是的缓存过期时间,默认`10`秒。 61 | 62 | ## 3. 方法 63 | 除了构造函数,主要就是2个方法。 64 | 65 | ### 3.1 `get()`方法 66 | 获取缓存,如果未缓存,则调用原对象的原方法,拿到数据后保存到Redis中,并返回数据。 67 | 有3个参数,基本就是固定的: 68 | - `$object`: 当前调用的类对象,传入`$this`。 69 | - `$name`: 欲调用的方法名称,由`__call`自动获取后传入。 70 | - `$arguments`: 欲调用的方法的参数,由`__call`自动获取后传入。 71 | 72 | ### 3.2 `expire()`方法 73 | 设置缓存过期时间。 74 | 默认的缓存过期时间是`3600`秒,也可以用`expire()`方法动态设置。 75 | 只有1个参数: 76 | - `$time`: 缓存过期时间,单位为秒。 -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yeszao/cache", 3 | "type": "library", 4 | "description": "General redis and memcache class", 5 | "keywords": [ 6 | "Cache", 7 | "Redis", 8 | "PHP" 9 | ], 10 | "homepage": "https://www.awaimai.com/", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Gary Meng", 15 | "email": "galley.meng@gmail.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=5.4.0" 20 | }, 21 | "autoload": { 22 | "psr-4": { "yeszao\\cache\\": "" } 23 | } 24 | } 25 | --------------------------------------------------------------------------------