CacheAheadAspect.php 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Cache\Aspect;
  12. use Hyperf\Cache\Annotation\CacheAhead;
  13. use Hyperf\Cache\AnnotationManager;
  14. use Hyperf\Cache\CacheManager;
  15. use Hyperf\Cache\Driver\KeyCollectorInterface;
  16. use Hyperf\Coroutine\Coroutine;
  17. use Hyperf\Di\Aop\AbstractAspect;
  18. use Hyperf\Di\Aop\ProceedingJoinPoint;
  19. class CacheAheadAspect extends AbstractAspect
  20. {
  21. public array $classes = [];
  22. public array $annotations = [
  23. CacheAhead::class,
  24. ];
  25. public function __construct(protected CacheManager $manager, protected AnnotationManager $annotationManager)
  26. {
  27. }
  28. public function process(ProceedingJoinPoint $proceedingJoinPoint)
  29. {
  30. $className = $proceedingJoinPoint->className;
  31. $method = $proceedingJoinPoint->methodName;
  32. $arguments = $proceedingJoinPoint->arguments['keys'];
  33. $now = time();
  34. /**
  35. * @var CacheAhead $annotation
  36. */
  37. [$key, $ttl, $group, $annotation] = $this->annotationManager->getCacheAheadValue($className, $method, $arguments);
  38. $driver = $this->manager->getDriver($group);
  39. $callback = static function () use ($proceedingJoinPoint, $driver, $annotation, $key, $now, $ttl) {
  40. $result = $proceedingJoinPoint->process();
  41. if (! in_array($result, (array) $annotation->skipCacheResults, true)) {
  42. $driver->set(
  43. $key,
  44. [
  45. 'expired_time' => $now + $ttl - $annotation->aheadSeconds,
  46. 'data' => $result,
  47. ],
  48. $ttl
  49. );
  50. if ($driver instanceof KeyCollectorInterface && $annotation instanceof CacheAhead && $annotation->collect) {
  51. $driver->addKey($annotation->prefix . 'MEMBERS', $key);
  52. }
  53. }
  54. return $result;
  55. };
  56. [$has, $result] = $driver->fetch($key);
  57. // If the cache exists, return it directly.
  58. if ($has && isset($result['expired_time'], $result['data'])) {
  59. if (
  60. $now > $result['expired_time']
  61. && $driver->getConnection()->set($key . ':lock', '1', ['NX', 'EX' => $annotation->lockSeconds])
  62. ) { // If the cache is about to expire, refresh the cache.
  63. Coroutine::create($callback);
  64. }
  65. return $result['data'];
  66. }
  67. // If the cache does not exist, execute the callback and cache the result.
  68. if ($annotation->runAsync) {
  69. Coroutine::create($callback);
  70. return null;
  71. }
  72. return $callback();
  73. }
  74. }