diff --git a/composer.json b/composer.json index 89f4d9f6..29d8e654 100644 --- a/composer.json +++ b/composer.json @@ -10,15 +10,15 @@ ], "require": { "php": "^7.2 || ^8.0", - "bear/resource": "^1.15", + "bear/resource": "^1.16.1", "bear/sunday": "^1.5", "doctrine/annotations": "^1.8", "doctrine/cache": "^2.0", "mobiledetect/mobiledetectlib": "^2.8", "psr/cache": "^1.0", "ray/aop": "^2.10", - "ray/di": "^2.12", - "ray/psr-cache-module": "^1.0", + "ray/di": "^2.13.1", + "ray/psr-cache-module": "1.1", "symfony/cache": "^v5.3", "symfony/cache-contracts": "^2.4" }, @@ -72,7 +72,7 @@ "cs": ["./vendor/bin/phpcs"], "cs-fix": ["./vendor/bin/phpcbf src tests"], "clean": ["./vendor/bin/phpstan clear-result-cache", "./vendor/bin/psalm --clear-cache", "rm -rf tests/tmp/*.php"], - "sa": ["./vendor/bin/phpstan analyse -c phpstan.neon", "psalm --show-info=true"], + "sa": ["psalm --show-info=true", "./vendor/bin/phpstan analyse -c phpstan.neon"], "metrics": ["./vendor/bin/phpmetrics --report-html=build/metrics --exclude=Exception --junit=build/junit.xml src"], "phpmd": ["./vendor/bin/phpmd --exclude src/Annotation src text ./phpmd.xml"], "build": ["@cs", "@sa", "@pcov", "@metrics"] diff --git a/phpcs.xml b/phpcs.xml index 7305bb57..dd74c95d 100755 --- a/phpcs.xml +++ b/phpcs.xml @@ -38,6 +38,7 @@ + diff --git a/src/Php73BcSerializableTrait.php b/src/Php73BcSerializableTrait.php new file mode 100644 index 00000000..b6d59f95 --- /dev/null +++ b/src/Php73BcSerializableTrait.php @@ -0,0 +1,30 @@ +__serialize()); + } + + /** + * @psalm-suppress all + * + * {@inheritDoc} + */ + final public function unserialize($serializedData) + { + $array = unserialize($serializedData); + $this->__unserialize($array); // @phpstan-ignore-line + } +} diff --git a/src/ResourceStorage.php b/src/ResourceStorage.php index 80438863..bdaed086 100644 --- a/src/ResourceStorage.php +++ b/src/ResourceStorage.php @@ -4,7 +4,6 @@ namespace BEAR\QueryRepository; -use BEAR\QueryRepository\SerializableTagAwareAdapter as TagAwareAdapter; use BEAR\RepositoryModule\Annotation\EtagPool; use BEAR\RepositoryModule\Annotation\KnownTagTtl; use BEAR\Resource\AbstractUri; @@ -13,8 +12,10 @@ use Doctrine\Common\Cache\CacheProvider; use Psr\Cache\CacheItemPoolInterface; use Ray\PsrCacheModule\Annotation\Shared; +use Serializable; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Contracts\Cache\ItemInterface; use function array_merge; @@ -27,8 +28,10 @@ use function sprintf; use function strtoupper; -final class ResourceStorage implements ResourceStorageInterface +final class ResourceStorage implements ResourceStorageInterface, Serializable { + use ResourceStorageCacheableTrait; + /** * Resource object cache prefix */ @@ -57,6 +60,9 @@ final class ResourceStorage implements ResourceStorageInterface /** @var ResourceStorageSaver */ private $saver; + /** @var float */ + private $knownTagTtl; + /** * @Shared("pool") * @EtagPool("etagPool") @@ -82,6 +88,7 @@ public function __construct( return; } + $this->knownTagTtl = $knownTagTtl; assert($pool instanceof AdapterInterface); $etagPool = $etagPool instanceof AdapterInterface ? $etagPool : $pool; $this->roPool = new TagAwareAdapter($pool, $etagPool, $knownTagTtl); diff --git a/src/ResourceStorageCacheableTrait.php b/src/ResourceStorageCacheableTrait.php new file mode 100644 index 00000000..d81ad933 --- /dev/null +++ b/src/ResourceStorageCacheableTrait.php @@ -0,0 +1,69 @@ +injector = $injector; + } + + /** + * @return array{logger: RepositoryLoggerInterface, purger: PurgerInterface, uriTag: UriTagInterface, saver: ResourceStorageSaver, knownTagTtl: float, injector: InjectorInterface} + */ + final public function __serialize(): array + { + assert($this->injector instanceof InjectorInterface); + + return [ + 'logger' => $this->logger, + 'purger' => $this->purger, + 'uriTag' => $this->uriTag, + 'saver' => $this->saver, + 'knownTagTtl' => $this->knownTagTtl, + 'injector' => $this->injector, + ]; + } + + /** + * @param array{logger: RepositoryLoggerInterface, purger: PurgerInterface, uriTag: UriTagInterface, saver: ResourceStorageSaver, knownTagTtl: float, injector: InjectorInterface} $data + */ + final public function __unserialize(array $data): void + { + $this->logger = $data['logger']; + $this->purger = $data['purger']; + $this->uriTag = $data['uriTag']; + $this->saver = $data['saver']; + $pool = $data['injector']->getInstance(CacheItemPoolInterface::class, Shared::class); + $etagPool = $data['injector']->getInstance(CacheItemPoolInterface::class, EtagPool::class); + assert($pool instanceof AdapterInterface); + assert($etagPool instanceof AdapterInterface); + $this->roPool = new TagAwareAdapter($pool, $etagPool, $data['knownTagTtl']); + $this->etagPool = new TagAwareAdapter($etagPool, $etagPool, $data['knownTagTtl']); + } +} diff --git a/src/SerializableTagAwareAdapter.php b/src/SerializableTagAwareAdapter.php deleted file mode 100644 index b7f92bb4..00000000 --- a/src/SerializableTagAwareAdapter.php +++ /dev/null @@ -1,47 +0,0 @@ - */ - private $args; - - public function __construct(AdapterInterface $itemsPool, ?AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15) - { - $this->args = func_get_args(); - parent::__construct($itemsPool, $tagsPool, $knownTagVersionsTtl); - } - - /** - * @inheritDoc - */ - public function serialize() - { - return serialize($this->args); - } - - /** - * @param string $data - * - * @inheritDoc - */ - public function unserialize($data) - { - call_user_func_array([$this, '__construct'], unserialize($data)); // @phpstan-ignore-line - } -} diff --git a/tests/Fake/fake-app/src/Resource/App/Value.php b/tests/Fake/fake-app/src/Resource/App/Value.php index e12970c7..b0184d47 100644 --- a/tests/Fake/fake-app/src/Resource/App/Value.php +++ b/tests/Fake/fake-app/src/Resource/App/Value.php @@ -17,7 +17,7 @@ class Value extends ResourceObject { public static $i = 1; - public function toString() + public function toString(): string { if ($this->view) { return $this->view; diff --git a/tests/Fake/fake-app/src/Resource/App/View.php b/tests/Fake/fake-app/src/Resource/App/View.php index 2bffff98..a2c3835e 100644 --- a/tests/Fake/fake-app/src/Resource/App/View.php +++ b/tests/Fake/fake-app/src/Resource/App/View.php @@ -17,7 +17,7 @@ class View extends ResourceObject { public static $i = 1; - public function toString() + public function toString(): string { if ($this->view) { return $this->view; diff --git a/tests/QueryRepositoryTest.php b/tests/QueryRepositoryTest.php index 323236d6..47189650 100644 --- a/tests/QueryRepositoryTest.php +++ b/tests/QueryRepositoryTest.php @@ -4,11 +4,15 @@ namespace BEAR\QueryRepository; +use BEAR\QueryRepository\QueryRepository as Repository; +use BEAR\RepositoryModule\Annotation\EtagPool; use BEAR\Resource\Module\ResourceModule; use BEAR\Resource\ResourceInterface; use BEAR\Resource\ResourceObject; use BEAR\Resource\Uri; use BEAR\Sunday\Extension\Transfer\HttpCacheInterface; +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\Reader; use FakeVendor\HelloWorld\Resource\App\NullView; use FakeVendor\HelloWorld\Resource\App\User\Profile; use FakeVendor\HelloWorld\Resource\Page\None; @@ -17,9 +21,12 @@ use Ray\Di\AbstractModule; use Ray\Di\Injector; use Ray\PsrCacheModule\Annotation\Shared; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; use function assert; use function is_array; +use function serialize; +use function unserialize; class QueryRepositoryTest extends TestCase { @@ -186,4 +193,21 @@ public function testRenderView(): void $this->repository->put($ro); $this->assertIsString($ro->view); } + + public function testSerializable(): void + { + $namespace = 'FakeVendor\HelloWorld'; + $module = new QueryRepositoryModule(); + $module->override(new class extends AbstractModule{ + protected function configure() + { + $this->bind(CacheItemPoolInterface::class)->annotatedWith(Shared::class)->to(FilesystemAdapter::class); + $this->bind(CacheItemPoolInterface::class)->annotatedWith(EtagPool::class)->to(FilesystemAdapter::class); + $this->bind(Reader::class)->to(AnnotationReader::class); + } + }); + $repository = (new Injector($module))->getInstance(QueryRepositoryInterface::class); + $unserilizedRepository = unserialize(serialize($repository)); + $this->assertInstanceOf(Repository::class, $unserilizedRepository); + } } diff --git a/tests/ResourceRepositoryTest.php b/tests/ResourceRepositoryTest.php index ed966131..a9d6c667 100644 --- a/tests/ResourceRepositoryTest.php +++ b/tests/ResourceRepositoryTest.php @@ -14,8 +14,6 @@ use function array_change_key_case; use function assert; -use function serialize; -use function unserialize; use const CASE_LOWER; @@ -121,10 +119,4 @@ protected function doGetStats() ); $this->assertInstanceOf(Repository::class, $repository); } - - public function testSerializable(): void - { - $repository = unserialize(serialize($this->repository)); - $this->assertInstanceOf(Repository::class, $repository); - } }