├── LICENSE ├── README.md ├── composer.json └── src ├── Adapter └── RedisAdapter.php ├── Annotation ├── Mapping │ ├── CacheClear.php │ ├── CachePut.php │ └── CacheRemember.php └── Parser │ ├── CacheClearParser.php │ ├── CachePutParser.php │ └── CacheRememberParser.php ├── Aspect ├── CacheClearAspect.php ├── CachePutAspect.php └── CacheRememberAspect.php ├── AutoLoader.php ├── Cache.php ├── CacheAbleTrait.php ├── CacheClearListener.php ├── CacheManager.php ├── Exception └── LockTimeoutException.php ├── Helper └── Str.php ├── Lock ├── Lock.php ├── LockContract.php ├── LockTrait.php └── RedisLock.php └── Register └── CacheRegister.php /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cache 缓存 2 | 3 | 1\. 介绍 4 | ---------------- 5 | * 完全遵循PSR16 cache组件,支持传统静态调用,注入调用,注解调用,事件绑定等。只需一个闭包即可支持分布式原子锁。 6 | 7 | 2\. 配置 8 | ---------------- 9 | 10 | 2.1 composer 11 | ``` 12 | composer require devweyes/cache 13 | ``` 14 | 15 | 2.2 无需额外配置,```Autoload.php```中包含默认配置(可覆盖 key名需相同)。 16 | 17 | ```php 18 | [ 27 | 'class' => \Jcsp\Cache\CacheManager::class, 28 | 'adapter' => bean(Cache::ADAPTER), 29 | 'lockAdapter' => Cache::LOCK 30 | ], 31 | //cache选择器 redis 32 | Cache::ADAPTER => [ 33 | 'class' => \Swoft\Cache\Adapter\RedisAdapter::class, 34 | 'redis' => bean('redis.pool'), 35 | 'prefix' => config('name') . ':', 36 | 'serializer' => bean(Cache::SERIALIZER), 37 | ], 38 | //cache原子锁配置 39 | Cache::LOCK => [ 40 | 'class' => \Jcsp\Cache\Lock\RedisLock::class, 41 | 'redis' => bean('redis.pool'), 42 | 'prefix' => 'lock:' 43 | ], 44 | //cache序列化 45 | Cache::SERIALIZER => [ 46 | 'class' => PhpSerializer::class 47 | ], 48 | ]; 49 | 50 | ``` 51 | 52 | 3\. 使用 53 | ---------------- 54 | 55 | 3.1 基本使用 56 | 57 | ```php 58 | get(); 94 | }); 95 | //获取并删除 96 | $value = Cache::pull('key'); 97 | 98 | //数据永久存储 需要调用delete清除 99 | Cache::forever('key', 'value'); 100 | 101 | //缓存不存在则查库 查库数据再永久存入缓存 需要调用delete清除 102 | $value = Cache::rememberForever('users', function () { 103 | return DB::table('users')->get(); 104 | }); 105 | } 106 | /** 107 | * 注入方式调用 108 | */ 109 | public function di() 110 | { 111 | //缓存30秒 112 | $this->cache->set('key','value', 30); 113 | 114 | //缓存获取 115 | $value = $this->cache->get('key'); 116 | 117 | //缓存清除 118 | $this->cache->delete('key'); 119 | 120 | //30秒缓存 缓存不存在则查库 查库数据再存入缓存 121 | $value = $this->cache->remember('users', 30, function () { 122 | return DB::table('users')->get(); 123 | }); 124 | //获取并删除 125 | $value = $this->cache->pull('key'); 126 | 127 | //数据永久存储 需要调用delete清除 128 | $this->cache->forever('key', 'value'); 129 | 130 | //缓存不存在则查库 查库数据再永久存入缓存 需要调用delete清除 131 | $value = $this->cache->rememberForever('users', function () { 132 | return DB::table('users')->get(); 133 | }); 134 | } 135 | 136 | 137 | /** 138 | * 注解方式调用 139 | * 缓存30秒,否则从function拿 140 | * 141 | * key为空则以class@action作为key,忽略参数 142 | * key支持参数传入,key规则其他注解通用 143 | * 当putListener不为空,触发缓存写入则触发此事件 144 | * 145 | * @CacheRemember(ttl=30, key="cache1_#{id}", putListener="CACHE_PUT") 146 | */ 147 | public function cache1($id) 148 | { 149 | // TODO something 150 | } 151 | 152 | /** 153 | * 每次都触发写入缓存 154 | * 当clearListener不为空,调用此事件则清除缓存 155 | * 156 | * @CachePut(ttl=30, clearListener="CACHE_CLEAR") 157 | */ 158 | public function cache2($id) 159 | { 160 | // TODO something 161 | } 162 | /** 163 | * 每次都触发清除缓存 164 | * position标识清除操作的位置,执行前或执行后 165 | * 166 | * @CacheClear(position=Cache::ASP_AFTER) 167 | */ 168 | public function cache3($id) 169 | { 170 | // TODO something 171 | } 172 | } 173 | ``` 174 | 3.2 事件支持 175 | 176 | * 缓存清除事件,事件方式清除,无需关心key名。仅支持注解CachePut与CacheRemember。 177 | 178 | ```php 179 | use Jcsp\Cache\Cache; 180 | /** 181 | * @param string $event clearListener对应字符串 182 | * @param array $args 参数键值对,与方法参数对应,如['id'=>1] 183 | */ 184 | Cache::clearTrigger(string $event, array $args = []) 185 | ``` 186 | 187 | 188 | 3.3 原子锁 189 | 190 | * 暂只支持reids驱动 191 | * 常见场景:分布式定时任务。前提需连接同一缓存服务器 192 | 193 | ```php 194 | get()) { 197 | // 获取锁定10秒... 198 | 199 | Cache::lock('foo')->release(); 200 | } 201 | 202 | ``` 203 | ```php 204 | get(function () { 207 | // 锁无限期获取并自动释放... 208 | }); 209 | ``` 210 | ```php 211 | block(5)) { 214 | // 等待最多5秒后获取锁定... 215 | }); 216 | ``` 217 | ```php 218 | block(5, function () { 221 | // 等待最多5秒后获取锁定... 222 | }); 223 | 224 | ``` 225 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devweyes/cache", 3 | "description": "cache for swoft", 4 | "license": "MIT", 5 | "keywords": [ 6 | "cache", 7 | "swoft" 8 | ], 9 | "autoload": { 10 | "psr-4": { 11 | "Jcsp\\Cache\\": "src/" 12 | } 13 | }, 14 | "require": { 15 | "php": ">7.1", 16 | "swoft/cache": "~2.0.0", 17 | "swoft/stdlib": "~2.0.0", 18 | "swoft/bean": "~2.0.0" 19 | }, 20 | "repositories": [ 21 | { 22 | "type": "composer", 23 | "url": "https://mirrors.aliyun.com/composer/" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/Adapter/RedisAdapter.php: -------------------------------------------------------------------------------- 1 | redis = $redis; 42 | } 43 | 44 | /** 45 | * @param string $key 46 | * 47 | * @return bool 48 | */ 49 | public function has($key): bool 50 | { 51 | $cacheKey = $this->getCacheKey($key); 52 | 53 | return (bool)$this->redis->exists($cacheKey); 54 | } 55 | 56 | /** 57 | * @param string $key 58 | * @param mixed $value 59 | * @param null|integer $ttl 60 | * 61 | * @return bool 62 | */ 63 | public function set($key, $value, $ttl = null): bool 64 | { 65 | $cacheKey = $this->getCacheKey($key); 66 | 67 | $ttl = $this->formatTTL($ttl); 68 | $value = $this->getSerializer()->serialize($value); 69 | 70 | return (bool)$this->redis->set($cacheKey, $value, $ttl); 71 | } 72 | 73 | /** 74 | * @param string $key 75 | * 76 | * @return bool 77 | */ 78 | public function delete($key): bool 79 | { 80 | $cacheKey = $this->getCacheKey($key); 81 | 82 | return $this->redis->del($cacheKey) === 1; 83 | } 84 | 85 | /** 86 | * @param iterable|array $values 87 | * @param null|integer $ttl 88 | * 89 | * @return bool 90 | */ 91 | public function setMultiple($values, $ttl = null): bool 92 | { 93 | $ttl = $this->formatTTL($ttl); 94 | 95 | return $this->redis->mset($values, $ttl); 96 | } 97 | 98 | /** 99 | * @param array $keys 100 | * 101 | * @return bool 102 | */ 103 | public function deleteMultiple($keys): bool 104 | { 105 | $keys = $this->checkKeys($keys); 106 | 107 | return $this->redis->del(...$keys) === count($keys); 108 | } 109 | 110 | /** 111 | * {@inheritDoc} 112 | */ 113 | public function get($key, $default = null) 114 | { 115 | $cacheKey = $this->getCacheKey($key); 116 | 117 | $value = $this->redis->get($cacheKey); 118 | if ($value === false) { 119 | return $default; 120 | } 121 | 122 | return $this->getSerializer()->unserialize((string)$value); 123 | } 124 | 125 | /** 126 | * Wipes clean the entire cache's keys. 127 | * 128 | * @return bool True on success and false on failure. 129 | */ 130 | public function clear(): bool 131 | { 132 | return true; 133 | } 134 | 135 | /** 136 | * {@inheritDoc} 137 | */ 138 | public function getMultiple($keys, $default = null) 139 | { 140 | $rows = []; 141 | $list = $this->redis->mget((array)$keys); 142 | 143 | foreach ($list as $item) { 144 | $rows[] = $this->getSerializer()->unserialize($item); 145 | } 146 | 147 | return $rows; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Annotation/Mapping/CacheClear.php: -------------------------------------------------------------------------------- 1 | key = $values['value']; 43 | } elseif (isset($values['key'])) { 44 | $this->key = $values['key']; 45 | } 46 | if (isset($values['position'])) { 47 | $this->position = $values['position']; 48 | } 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | public function getKey(): string 55 | { 56 | return $this->key; 57 | } 58 | /** 59 | * @return string 60 | */ 61 | public function getPosition(): string 62 | { 63 | return $this->position; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Annotation/Mapping/CachePut.php: -------------------------------------------------------------------------------- 1 | key = $values['value']; 51 | } elseif (isset($values['key'])) { 52 | $this->key = $values['key']; 53 | } 54 | if (isset($values['val'])) { 55 | $this->val = $values['val']; 56 | } 57 | if (isset($values['ttl'])) { 58 | $this->ttl = (int)$values['ttl']; 59 | } 60 | if (isset($values['clearListener'])) { 61 | $this->clearListener = $values['clearListener']; 62 | } 63 | } 64 | 65 | /** 66 | * @return string 67 | */ 68 | public function getKey(): string 69 | { 70 | return $this->key; 71 | } 72 | 73 | /** 74 | * @return string 75 | */ 76 | public function getVal(): string 77 | { 78 | return $this->val; 79 | } 80 | 81 | /** 82 | * @return int 83 | */ 84 | public function getTtl(): int 85 | { 86 | return $this->ttl; 87 | } 88 | /** 89 | * @return string 90 | */ 91 | public function getClearListener(): string 92 | { 93 | return $this->clearListener; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Annotation/Mapping/CacheRemember.php: -------------------------------------------------------------------------------- 1 | key = $values['value']; 50 | } elseif (isset($values['key'])) { 51 | $this->key = $values['key']; 52 | } 53 | if (isset($values['ttl'])) { 54 | $this->ttl = (int)$values['ttl']; 55 | } 56 | if (isset($values['putListener'])) { 57 | $this->putListener = $values['putListener']; 58 | } 59 | if (isset($values['clearListener'])) { 60 | $this->clearListener = $values['clearListener']; 61 | } 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | public function getPutListener(): string 68 | { 69 | return $this->putListener; 70 | } 71 | 72 | /** 73 | * @return string 74 | */ 75 | public function getClearListener(): string 76 | { 77 | return $this->clearListener; 78 | } 79 | 80 | /** 81 | * @return string 82 | */ 83 | public function getKey(): string 84 | { 85 | return $this->key; 86 | } 87 | /** 88 | * @return int 89 | */ 90 | public function getTtl(): int 91 | { 92 | return $this->ttl; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Annotation/Parser/CacheClearParser.php: -------------------------------------------------------------------------------- 1 | getKey(); 34 | $position = $annotationObject->getPosition(); 35 | $data = [ 36 | $key, $position 37 | ]; 38 | CacheRegister::register($data, $this->className, $this->methodName, 'cacheClear'); 39 | return []; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Annotation/Parser/CachePutParser.php: -------------------------------------------------------------------------------- 1 | getKey(); 33 | $val = $annotationObject->getVal(); 34 | $ttl = $annotationObject->getTtl(); 35 | $clearListener = $annotationObject->getClearListener(); 36 | $data = [ 37 | $key, $val, $ttl, $clearListener 38 | ]; 39 | CacheRegister::register($data, $this->className, $this->methodName, 'cachePut'); 40 | if (!empty($clearListener)) { 41 | CacheRegister::registerClearData($data, $this->className, $this->methodName); 42 | } 43 | return []; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Annotation/Parser/CacheRememberParser.php: -------------------------------------------------------------------------------- 1 | getKey(); 35 | $ttl = $annotationObject->getTtl(); 36 | $putListener = $annotationObject->getPutListener(); 37 | $clearListener = $annotationObject->getClearListener(); 38 | $data = [ 39 | $key, $ttl, $putListener, $clearListener 40 | ]; 41 | CacheRegister::register($data, $this->className, $this->methodName, 'cacheRemember'); 42 | if (!empty($clearListener)) { 43 | CacheRegister::registerClearData($data, $this->className, $this->methodName); 44 | } 45 | return []; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Aspect/CacheClearAspect.php: -------------------------------------------------------------------------------- 1 | getClassName(); 50 | $methodName = $proceedingJoinPoint->getMethod(); 51 | $argsMap = $proceedingJoinPoint->getArgsMap(); 52 | 53 | $has = CacheRegister::has($className, $methodName, 'cacheClear'); 54 | 55 | if ($has) { 56 | [$key, $position] = CacheRegister::get($className, $methodName, 'cacheClear'); 57 | $prefix = $key ? '' : "$className@$methodName"; 58 | $key = CacheRegister::formatedKey($prefix, $argsMap, $key); 59 | } 60 | 61 | if ($has && $position === CacheStatic::ASP_BEFORE) { 62 | $this->redis->delete($key); 63 | } 64 | $result = $proceedingJoinPoint->proceed(); 65 | // After around 66 | if ($has && $position === CacheStatic::ASP_AFTER) { 67 | $this->redis->delete($key); 68 | } 69 | return $result; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Aspect/CachePutAspect.php: -------------------------------------------------------------------------------- 1 | getClassName(); 49 | $methodName = $proceedingJoinPoint->getMethod(); 50 | $argsMap = $proceedingJoinPoint->getArgsMap(); 51 | 52 | $has = CacheRegister::has($className, $methodName, 'cachePut'); 53 | $has && ([$key, $val, $ttl, ] = CacheRegister::get($className, $methodName, 'cachePut')); 54 | 55 | $result = $proceedingJoinPoint->proceed(); 56 | // After around 57 | if ($has) { 58 | $data = $result; 59 | $prefix = $key ? '' : "$className@$methodName"; 60 | $key = CacheRegister::formatedKey($prefix, $argsMap, $key); 61 | if (!empty($val)) { 62 | $data = $val; 63 | } 64 | $this->redis->set($key, $data, (int)$ttl); 65 | } 66 | return $result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Aspect/CacheRememberAspect.php: -------------------------------------------------------------------------------- 1 | getClassName(); 49 | $methodName = $proceedingJoinPoint->getMethod(); 50 | $argsMap = $proceedingJoinPoint->getArgsMap(); 51 | 52 | $has = CacheRegister::has($className, $methodName, 'cacheRemember'); 53 | 54 | if (!$has) { 55 | return $proceedingJoinPoint->proceed(); 56 | } 57 | 58 | [$key, $ttl, $putListener,] = CacheRegister::get($className, $methodName, 'cacheRemember'); 59 | 60 | $prefix = $key ? '' : "$className@$methodName"; 61 | $key = CacheRegister::formatedKey($prefix, $argsMap, $key); 62 | 63 | return $this->redis->remember( 64 | $key, 65 | (int)$ttl, 66 | static function () use ($proceedingJoinPoint, $putListener, $key, $ttl) { 67 | $result = $proceedingJoinPoint->proceed(); 68 | if (!empty($putListener)) { 69 | Swoft::trigger($putListener, $key, $result, $ttl); 70 | } 71 | return $result; 72 | } 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/AutoLoader.php: -------------------------------------------------------------------------------- 1 | __DIR__, 37 | ]; 38 | } 39 | 40 | /** 41 | * Metadata information for the component 42 | * 43 | * @return array 44 | */ 45 | public function metadata(): array 46 | { 47 | $jsonFile = dirname(__DIR__).'/composer.json'; 48 | 49 | return ComposerJSON::open($jsonFile)->getMetadata(); 50 | } 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | public function beans(): array 56 | { 57 | return [ 58 | /*** cache 配置 ************************/ 59 | Cache::MANAGER => [ 60 | 'class' => CacheManager::class, 61 | 'adapter' => bean(Cache::ADAPTER), 62 | 'lockAdapter' => Cache::LOCK 63 | ], 64 | Cache::ADAPTER => [ 65 | 'class' => \Jcsp\Cache\Adapter\RedisAdapter::class, 66 | 'redis' => bean('redis.pool'), 67 | 'prefix' => 'cache_', 68 | 'serializer' => bean(Cache::SERIALIZER), 69 | ], 70 | Cache::LOCK => [ 71 | 'class' => \Jcsp\Cache\Lock\RedisLock::class, 72 | 'redis' => bean('redis.pool'), 73 | 'prefix' => 'lock:' 74 | ], 75 | Cache::SERIALIZER => [ 76 | 'class' => PhpSerializer::class 77 | ], 78 | ]; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Cache.php: -------------------------------------------------------------------------------- 1 | {$method}(...$arguments); 58 | } 59 | /** 60 | * @return CacheManager 61 | */ 62 | public static function manager(): CacheManager 63 | { 64 | return Swoft::getBean(self::MANAGER); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/CacheAbleTrait.php: -------------------------------------------------------------------------------- 1 | get($key); 21 | 22 | if ($value !== null) { 23 | return $value; 24 | } 25 | $this->set($key, $value = $callback(), $ttl); 26 | 27 | return $value; 28 | } 29 | 30 | /** 31 | * Store an item in the cache indefinitely. 32 | * 33 | * @param string $key 34 | * @param mixed $value 35 | * @return bool 36 | */ 37 | public function forever($key, $value): bool 38 | { 39 | return $this->set($key, $value, -1); 40 | } 41 | 42 | /** 43 | * Get an item from the cache, or execute the given Closure and store the result forever. 44 | * 45 | * @param string $key 46 | * @param \Closure $callback 47 | * @return mixed 48 | */ 49 | public function rememberForever($key, Closure $callback) 50 | { 51 | $value = $this->get($key); 52 | 53 | if ($value !== null) { 54 | return $value; 55 | } 56 | $this->forever($key, $value = $callback()); 57 | 58 | return $value; 59 | } 60 | 61 | /** 62 | * Retrieve an item from the cache and delete it. 63 | * 64 | * @param string $key 65 | * @param mixed $default 66 | * @return mixed 67 | */ 68 | public function pull($key, $default = null) 69 | { 70 | return tap($this->get($key, $default), function () use ($key) { 71 | $this->delete($key); 72 | }); 73 | } 74 | 75 | /** 76 | * Get a lock instance. 77 | * 78 | * @param $key 79 | * @param int $seconds 80 | * @param string $owner 81 | * @return LockContract 82 | */ 83 | public function lock($key, int $ttl = 0, $value = ''): LockContract 84 | { 85 | /** @var LockContract $lock */ 86 | $lock = BeanFactory::getBean(Cache::LOCK); 87 | $lock->reset($key, $ttl, $value); 88 | return $lock; 89 | } 90 | 91 | /** 92 | * 缓存清除事件 93 | * @param string $event 94 | * @param array $args 95 | * @param null $target 96 | * @return mixed 97 | * @throws \Swoft\Bean\Exception\ContainerException 98 | */ 99 | public function clearTrigger(string $event, array $args = [], $target = null) 100 | { 101 | return \Swoft::trigger(Cache::CLEAR_EVENT, $target, $event, $args); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/CacheClearListener.php: -------------------------------------------------------------------------------- 1 | getParams(); 25 | 26 | if (!empty($args) && count($args) === 2) { 27 | $data = CacheRegister::getClearData(); 28 | $data = $data[$args[0]] ?? null; 29 | if (!empty($data) && is_array($data)) { 30 | $this->clear($args, $data); 31 | } 32 | } 33 | } 34 | 35 | /** 36 | * clear cache 37 | * @param array $args 38 | * @param array $data 39 | */ 40 | public function clear(array $args, array $data): void 41 | { 42 | $argsMap = $args[1] ?? []; 43 | $key = $data['data'][0] ?? ''; 44 | $prefix = $key ? '' : $data['className'] . '@' . $data['methodName']; 45 | $key = CacheRegister::formatedKey($prefix, $argsMap, $key); 46 | Cache::delete($key); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/CacheManager.php: -------------------------------------------------------------------------------- 1 | adapter->has($key); 35 | } 36 | 37 | /** 38 | * @param string $key 39 | * @param mixed $value 40 | * @param null $ttl 41 | * @return bool 42 | */ 43 | public function set($key, $value, $ttl = null): bool 44 | { 45 | return $this->adapter->set($key, $value, $ttl); 46 | } 47 | 48 | /** 49 | * @param string $key 50 | * @param null $default 51 | * @return mixed 52 | * @throws \Psr\SimpleCache\InvalidArgumentException 53 | */ 54 | public function get($key, $default = null) 55 | { 56 | return $this->adapter->get($key, $default); 57 | } 58 | 59 | /** 60 | * @param string $key 61 | * @return bool|mixed 62 | * @throws \Psr\SimpleCache\InvalidArgumentException 63 | */ 64 | public function delete($key) 65 | { 66 | return $this->adapter->delete($key); 67 | } 68 | 69 | /** 70 | * Wipes clean the entire cache's keys. 71 | * 72 | * @return bool True on success and false on failure. 73 | */ 74 | public function clear(): bool 75 | { 76 | return $this->adapter->clear(); 77 | } 78 | 79 | /** 80 | * @param iterable $keys 81 | * @param null $default 82 | * @return array 83 | * @throws \Psr\SimpleCache\InvalidArgumentException 84 | */ 85 | public function getMultiple($keys, $default = null): array 86 | { 87 | return $this->adapter->getMultiple($keys, $default); 88 | } 89 | 90 | /** 91 | * @param iterable $values 92 | * @param null $ttl 93 | * @return bool 94 | */ 95 | public function setMultiple($values, $ttl = null): bool 96 | { 97 | return $this->adapter->setMultiple((array)$values, $ttl); 98 | } 99 | 100 | /** 101 | * @param iterable $keys 102 | * @return bool 103 | */ 104 | public function deleteMultiple($keys): bool 105 | { 106 | return $this->adapter->deleteMultiple((array)$keys); 107 | } 108 | 109 | /** 110 | * @return CacheAdapterInterface 111 | */ 112 | public function getAdapter(): CacheAdapterInterface 113 | { 114 | return $this->adapter; 115 | } 116 | 117 | /** 118 | * @param CacheAdapterInterface $adapter 119 | */ 120 | public function setAdapter(CacheAdapterInterface $adapter): void 121 | { 122 | $this->adapter = $adapter; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Exception/LockTimeoutException.php: -------------------------------------------------------------------------------- 1 | all(); 53 | } elseif (! is_array($target)) { 54 | return value($default); 55 | } 56 | $result = []; 57 | foreach ($target as $item) { 58 | $result[] = self::dataGet($item, $key); 59 | } 60 | return in_array('*', $key) ? Arr::collapse($result) : $result; 61 | } 62 | if (Arr::accessible($target) && Arr::exists($target, $segment)) { 63 | $target = $target[$segment]; 64 | } elseif (is_object($target) && isset($target->{$segment})) { 65 | $target = $target->{$segment}; 66 | } else { 67 | return value($default); 68 | } 69 | } 70 | return $target; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Lock/Lock.php: -------------------------------------------------------------------------------- 1 | name = $name; 44 | $this->owner = $owner; 45 | $this->seconds = $seconds; 46 | } 47 | /** 48 | * Attempt to acquire the lock. 49 | * 50 | * @return bool 51 | */ 52 | abstract public function acquire(): bool; 53 | 54 | /** 55 | * Release the lock. 56 | * 57 | * @return bool 58 | */ 59 | abstract public function release(): bool; 60 | 61 | /** 62 | * Returns the owner value written into the driver for this lock. 63 | * 64 | * @return string 65 | */ 66 | abstract protected function getCurrentOwner(): string; 67 | 68 | /** 69 | * Attempt to acquire the lock. 70 | * 71 | * @param callable|null $callback 72 | * @return mixed 73 | */ 74 | public function get($callback = null) 75 | { 76 | $result = $this->acquire(); 77 | 78 | if ($result && is_callable($callback)) { 79 | try { 80 | return $callback(); 81 | } finally { 82 | $this->release(); 83 | } 84 | } 85 | 86 | return $result; 87 | } 88 | 89 | /** 90 | * Attempt to acquire the lock for the given number of seconds. 91 | * 92 | * @param int $seconds 93 | * @param callable|null $callback 94 | * @return bool 95 | * 96 | * @throws LockTimeoutException 97 | */ 98 | public function block($seconds, $callback = null): bool 99 | { 100 | $starting = $this->currentTime(); 101 | 102 | while (!$this->acquire()) { 103 | usleep(250 * 1000); 104 | 105 | if ($this->currentTime() - $seconds >= $starting) { 106 | throw new LockTimeoutException; 107 | } 108 | } 109 | 110 | if (is_callable($callback)) { 111 | try { 112 | return $callback(); 113 | } finally { 114 | $this->release(); 115 | } 116 | } 117 | 118 | return true; 119 | } 120 | 121 | /** 122 | * Returns the current owner of the lock. 123 | * 124 | * @return string 125 | */ 126 | public function owner(): string 127 | { 128 | return $this->owner; 129 | } 130 | 131 | /** 132 | * Determines whether this lock is allowed to release the lock in the driver. 133 | * 134 | * @return bool 135 | */ 136 | protected function isOwnedByCurrentProcess(): bool 137 | { 138 | return $this->getCurrentOwner() === $this->owner; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Lock/LockContract.php: -------------------------------------------------------------------------------- 1 | lockAdapter->release(); 21 | } 22 | /** 23 | * Releases this lock in disregard of ownership. 24 | * 25 | * @return void 26 | */ 27 | public function forceRelease(): void 28 | { 29 | $this->lockAdapter->forceRelease(); 30 | } 31 | /** 32 | * Attempt to acquire the lock. 33 | * 34 | * @param callable|null $callback 35 | * @return mixed 36 | */ 37 | public function get($callback = null) 38 | { 39 | return $this->lockAdapter->get($callback); 40 | } 41 | 42 | /** 43 | * Attempt to acquire the lock for the given number of seconds. 44 | * 45 | * @param int $seconds 46 | * @param callable|null $callback 47 | * @return bool 48 | */ 49 | public function block($seconds, $callback = null): bool 50 | { 51 | return $this->lockAdapter->block($seconds, $callback); 52 | } 53 | 54 | /** 55 | * Returns the current owner of the lock. 56 | * 57 | * @return string 58 | */ 59 | public function owner(): string 60 | { 61 | return $this->lockAdapter->owner(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Lock/RedisLock.php: -------------------------------------------------------------------------------- 1 | redis->setnx($this->name, $this->owner); 35 | 36 | if ($result && $this->seconds > 0) { 37 | $this->redis->expire($this->name, $this->seconds); 38 | } 39 | 40 | return $result; 41 | } 42 | 43 | /** 44 | * Release the lock. 45 | * 46 | * @return bool 47 | */ 48 | public function release(): bool 49 | { 50 | return (bool)$this->redis->eval($this->releaseLockScript(), [$this->name, $this->owner], 1); 51 | } 52 | 53 | /** 54 | * Releases this lock in disregard of ownership. 55 | * 56 | * @return void 57 | */ 58 | public function forceRelease(): void 59 | { 60 | $this->redis->del($this->name); 61 | } 62 | 63 | /** 64 | * Returns the owner value written into the driver for this lock. 65 | * 66 | * @return string 67 | */ 68 | protected function getCurrentOwner(): string 69 | { 70 | return $this->redis->get($this->name); 71 | } 72 | 73 | /** 74 | * Get the Lua script to atomically release a lock. 75 | * 76 | * KEYS[1] - The name of the lock 77 | * ARGV[1] - The owner key of the lock instance trying to release it 78 | * 79 | * @return string 80 | */ 81 | private function releaseLockScript(): string 82 | { 83 | return <<<'LUA' 84 | if redis.call("get",KEYS[1]) == ARGV[1] then 85 | return redis.call("del",KEYS[1]) 86 | else 87 | return 0 88 | end 89 | LUA; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Register/CacheRegister.php: -------------------------------------------------------------------------------- 1 | 64) { 111 | CLog::warning('The cache key length is too long. The key is ' . $key); 112 | } 113 | return $key; 114 | } 115 | } 116 | --------------------------------------------------------------------------------