From 51b7bf3d73893115ea6284ab8b1469b5bfeee10f Mon Sep 17 00:00:00 2001 From: yuuki takezawa Date: Fri, 23 Dec 2016 04:03:26 +0900 Subject: [PATCH] append negative cache (#38) * append annotations / supported negative cache * append code * format * updated --- README.md | 1 + composer.json | 1 + src/Annotation/Cacheable.php | 3 ++ src/Console/ModulePublishCommand.php | 14 +++++----- src/ContainerInterceptor.php | 4 +-- src/Interceptor/AbstractCache.php | 16 +++++++---- src/Interceptor/CacheableInterceptor.php | 11 ++++++-- src/Interceptor/RetryOnFailureInterceptor.php | 28 ++++++++++++++----- src/Matcher/AnnotationScanMatcher.php | 27 +++++++++--------- src/RayAspectKernel.php | 2 +- tests/CacheableTest.php | 8 ++++++ tests/src/AspectCacheable.php | 14 ++++++++++ 12 files changed, 90 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index cf12b6f..bbd70dd 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ you must use the CacheableModule | driver | Accessing Cache Driver(store) | | lifetime | cache lifetime (default: 120min) | | tags | Storing Tagged Cache Items | +| negative(bool) | for null value (default: false) | ```php use Ytake\LaravelAspect\Annotation\Cacheable; diff --git a/composer.json b/composer.json index 794b029..4bed89f 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "satooshi/php-coveralls": "*", "illuminate/database": "~5.0", "illuminate/cache": "~5.0", + "illuminate/view": "~5.0", "sebastian/phpcpd": "*", "phploc/phploc": "*", "pdepend/pdepend" : "^2.2.4", diff --git a/src/Annotation/Cacheable.php b/src/Annotation/Cacheable.php index b1f33a5..a0790e1 100644 --- a/src/Annotation/Cacheable.php +++ b/src/Annotation/Cacheable.php @@ -39,4 +39,7 @@ final class Cacheable extends Annotation /** @var array $tags if use array tagging */ public $tags = []; + + /** @var bool */ + public $negative = false; } diff --git a/src/Console/ModulePublishCommand.php b/src/Console/ModulePublishCommand.php index b84734f..1324e2e 100644 --- a/src/Console/ModulePublishCommand.php +++ b/src/Console/ModulePublishCommand.php @@ -43,13 +43,13 @@ class ModulePublishCommand extends Command /** @var array package modules */ protected $modules = [ - 'CacheableModule' => 'Ytake\LaravelAspect\Modules\CacheableModule', - 'CacheEvictModule' => 'Ytake\LaravelAspect\Modules\CacheEvictModule', - 'CachePutModule' => 'Ytake\LaravelAspect\Modules\CachePutModule', - 'TransactionalModule' => 'Ytake\LaravelAspect\Modules\TransactionalModule', - 'LoggableModule' => 'Ytake\LaravelAspect\Modules\LoggableModule', - 'LogExceptionsModule' => 'Ytake\LaravelAspect\Modules\LogExceptionsModule', - 'PostConstructModule' => 'Ytake\LaravelAspect\Modules\PostConstructModule', + 'CacheableModule' => 'Ytake\LaravelAspect\Modules\CacheableModule', + 'CacheEvictModule' => 'Ytake\LaravelAspect\Modules\CacheEvictModule', + 'CachePutModule' => 'Ytake\LaravelAspect\Modules\CachePutModule', + 'TransactionalModule' => 'Ytake\LaravelAspect\Modules\TransactionalModule', + 'LoggableModule' => 'Ytake\LaravelAspect\Modules\LoggableModule', + 'LogExceptionsModule' => 'Ytake\LaravelAspect\Modules\LogExceptionsModule', + 'PostConstructModule' => 'Ytake\LaravelAspect\Modules\PostConstructModule', 'RetryOnFailureModule' => 'Ytake\LaravelAspect\Modules\RetryOnFailureModule', ]; diff --git a/src/ContainerInterceptor.php b/src/ContainerInterceptor.php index 62209a1..d0045d2 100644 --- a/src/ContainerInterceptor.php +++ b/src/ContainerInterceptor.php @@ -18,7 +18,7 @@ namespace Ytake\LaravelAspect; use Ray\Aop\Bind; -use Illuminate\Container\Container; +use Illuminate\Contracts\Container\Container; use Ytake\LaravelAspect\Annotation\PostConstruct; /** @@ -26,7 +26,7 @@ */ final class ContainerInterceptor { - /** @var Container */ + /** @var Container|\Illuminate\Container\Container */ private $container; /** diff --git a/src/Interceptor/AbstractCache.php b/src/Interceptor/AbstractCache.php index 181bd7c..53d2d75 100644 --- a/src/Interceptor/AbstractCache.php +++ b/src/Interceptor/AbstractCache.php @@ -17,11 +17,15 @@ */ namespace Ytake\LaravelAspect\Interceptor; -use Illuminate\Cache\CacheManager; use Ray\Aop\MethodInvocation; use Ray\Aop\MethodInterceptor; +use Illuminate\Cache\CacheManager; use Illuminate\Contracts\Cache\Factory; +use Doctrine\Common\Annotations\Annotation; use Ytake\LaravelAspect\Annotation\AnnotationReaderTrait; +use Ytake\LaravelAspect\Annotation\Cacheable; +use Ytake\LaravelAspect\Annotation\CacheEvict; +use Ytake\LaravelAspect\Annotation\CachePut; /** * Class AbstractCache @@ -55,13 +59,13 @@ protected function generateCacheName($name, MethodInvocation $invocation) } /** - * @param MethodInvocation $invocation - * @param $annotation - * @param $keys + * @param MethodInvocation $invocation + * @param Annotation|Cacheable|CacheEvict|CachePut $annotation + * @param array $keys * * @return array */ - protected function detectCacheKeys(MethodInvocation $invocation, $annotation, $keys) + protected function detectCacheKeys(MethodInvocation $invocation, Annotation $annotation, array $keys) { $arguments = $invocation->getArguments(); foreach ($invocation->getMethod()->getParameters() as $parameter) { @@ -91,7 +95,7 @@ protected function detectCacheRepository($annotation) /** @var Factory|CacheManager $cacheFactory */ $cacheFactory = self::$factory; $driver = (is_null($annotation->driver)) ? $cacheFactory->getDefaultDriver() : $annotation->driver; - /** @var \Illuminate\Contracts\Cache\Repository $cache */ + /** @var \Illuminate\Contracts\Cache\Repository|\Illuminate\Cache\TaggableStore $cache */ $cache = $cacheFactory->store($driver); if (count($annotation->tags)) { $cache = $cache->tags($annotation->tags); diff --git a/src/Interceptor/CacheableInterceptor.php b/src/Interceptor/CacheableInterceptor.php index 45d16a7..a1413b9 100644 --- a/src/Interceptor/CacheableInterceptor.php +++ b/src/Interceptor/CacheableInterceptor.php @@ -18,6 +18,7 @@ namespace Ytake\LaravelAspect\Interceptor; use Ray\Aop\MethodInvocation; +use Ytake\LaravelAspect\Annotation\Cacheable; /** * Class CacheableInterceptor @@ -31,12 +32,12 @@ class CacheableInterceptor extends AbstractCache */ public function invoke(MethodInvocation $invocation) { + /** @var Cacheable $annotation */ $annotation = $invocation->getMethod()->getAnnotation($this->annotation); $keys = $this->generateCacheName($annotation->cacheName, $invocation); if (!is_array($annotation->key)) { $annotation->key = [$annotation->key]; } - $keys = $this->detectCacheKeys($invocation, $annotation, $keys); // detect use cache driver $cache = $this->detectCacheRepository($annotation); @@ -45,8 +46,14 @@ public function invoke(MethodInvocation $invocation) return $cache->get($key); } $result = $invocation->proceed(); + if (!$annotation->negative) { + if ($result) { + $cache->add($key, $result, $annotation->lifetime); + } - if ($result) { + return $result; + } + if (is_null($result)) { $cache->add($key, $result, $annotation->lifetime); } diff --git a/src/Interceptor/RetryOnFailureInterceptor.php b/src/Interceptor/RetryOnFailureInterceptor.php index 7412acf..5961ccf 100644 --- a/src/Interceptor/RetryOnFailureInterceptor.php +++ b/src/Interceptor/RetryOnFailureInterceptor.php @@ -29,7 +29,7 @@ class RetryOnFailureInterceptor implements MethodInterceptor { use AnnotationReaderTrait; - /** @var int|null */ + /** @var array|null */ private static $attempt = null; /** @@ -42,30 +42,44 @@ public function invoke(MethodInvocation $invocation) { /** @var RetryOnFailure $annotation */ $annotation = $invocation->getMethod()->getAnnotation($this->annotation); - if (self::$attempt === null) { - self::$attempt = $annotation->attempts; + $key = $this->keyName($invocation); + + if (isset(self::$attempt[$key]) === false) { + self::$attempt[$key] = $annotation->attempts; } + try { - self::$attempt--; + self::$attempt[$key]--; return $invocation->proceed(); } catch (\Exception $e) { if (ltrim($annotation->ignore, '\\') === get_class($e)) { - self::$attempt = null; + self::$attempt[$key] = null; throw $e; } + $pass = array_filter($annotation->types, function ($values) use ($e) { return ltrim($values, '\\') === get_class($e); }); if ($pass !== false) { - if (self::$attempt > 0) { + if (self::$attempt[$key] > 0) { sleep($annotation->delay); return $invocation->proceed(); } } - self::$attempt = null; + self::$attempt[$key] = null; throw $e; } } + + /** + * @param MethodInvocation $invocation + * + * @return string + */ + protected function keyName(MethodInvocation $invocation) + { + return $invocation->getMethod()->class . "$" . $invocation->getMethod()->getName(); + } } diff --git a/src/Matcher/AnnotationScanMatcher.php b/src/Matcher/AnnotationScanMatcher.php index 32802f4..f9347a0 100644 --- a/src/Matcher/AnnotationScanMatcher.php +++ b/src/Matcher/AnnotationScanMatcher.php @@ -42,9 +42,19 @@ public function __construct() * {@inheritdoc} */ public function matchesClass(\ReflectionClass $class, array $arguments) + { + return $this->has($class, $arguments[0]); + } + + /** + * @param \ReflectionClass $class + * @param $annotation + * + * @return bool + */ + private function has(\ReflectionClass $class, $annotation) { $count = 0; - $annotation = $arguments[0]; foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { $match = $this->reader->getMethodAnnotation($reflectionMethod, $annotation); if ($match) { @@ -63,19 +73,8 @@ public function matchesClass(\ReflectionClass $class, array $arguments) */ public function matchesMethod(\ReflectionMethod $method, array $arguments) { - $count = 0; - $annotation = $arguments[0]; - $reflectionClass = new \ReflectionClass($method->class); - foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { - $match = $this->reader->getMethodAnnotation($reflectionMethod, $annotation); - if ($match) { - $count++; - } - } - if ($count > 1) { - return false; - } + $class = new \ReflectionClass($method->class); - return true; + return $this->has($class, $arguments[0]); } } diff --git a/src/RayAspectKernel.php b/src/RayAspectKernel.php index 29ef433..f0a0057 100644 --- a/src/RayAspectKernel.php +++ b/src/RayAspectKernel.php @@ -176,7 +176,7 @@ protected function registerAspectModule() } /** - * @return string[] + * @return array * @codeCoverageIgnore */ protected function aspectConfiguration() diff --git a/tests/CacheableTest.php b/tests/CacheableTest.php index cb9c106..ff13c79 100644 --- a/tests/CacheableTest.php +++ b/tests/CacheableTest.php @@ -65,6 +65,14 @@ public function testCacheableCacheObject() $this->assertSame(1000, $result); } + public function testShouldBeNullForNegativeCache() + { + /** @var \__Test\AspectCacheable $cache */ + $cache = $this->app->make(\__Test\AspectCacheable::class); + $this->assertNull($cache->negativeCache()); + $this->assertNull($this->app['cache']->get('negative')); + } + /** * */ diff --git a/tests/src/AspectCacheable.php b/tests/src/AspectCacheable.php index 3d5ef71..fdb7484 100644 --- a/tests/src/AspectCacheable.php +++ b/tests/src/AspectCacheable.php @@ -17,6 +17,7 @@ class AspectCacheable /** * @Cacheable(key="#id",driver="null") * @param null $id + * * @return null */ public function singleKey($id = null) @@ -28,6 +29,7 @@ public function singleKey($id = null) * @Cacheable(key={"#id","#value"},driver="array") * @param $id * @param $value + * * @return mixed */ public function multipleKey($id, $value) @@ -39,6 +41,7 @@ public function multipleKey($id, $value) * @Cacheable(cacheName="testing1",key={"#id","#value"}) * @param $id * @param $value + * * @return mixed */ public function namedMultipleKey($id, $value) @@ -50,6 +53,7 @@ public function namedMultipleKey($id, $value) * @Cacheable(tags={"testing1","testing2"},key={"#id","#value"}) * @param $id * @param $value + * * @return mixed */ public function namedMultipleNameAndKey($id, $value) @@ -61,10 +65,20 @@ public function namedMultipleNameAndKey($id, $value) * @Cacheable(tags={"testing1","testing2"},key={"#id","#class"}) * @param $id * @param \stdClass $class + * * @return mixed */ public function cachingKeyObject($id, \stdClass $class) { return $id; } + + /** + * @Cacheable(negative=true,cacheName="negative") + * @return null + */ + public function negativeCache() + { + return null; + } }