mirror of
https://github.com/bdnugget/magento2.git
synced 2026-11-07 09:17:35 +00:00
223 lines
7.3 KiB
PHP
223 lines
7.3 KiB
PHP
<?php
|
|
/**
|
|
* Copyright © Magento, Inc. All rights reserved.
|
|
* See COPYING.txt for license details.
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace Magento\GraphQlResolverCache\Model\Plugin\Resolver;
|
|
|
|
use Magento\Framework\App\Cache\StateInterface as CacheState;
|
|
use Magento\Framework\GraphQl\Config\Element\Field;
|
|
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
|
|
use Magento\Framework\GraphQl\Query\Resolver\Value;
|
|
use Magento\Framework\GraphQl\Query\ResolverInterface;
|
|
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
|
|
use Magento\Framework\Serialize\SerializerInterface;
|
|
use Magento\GraphQlResolverCache\Model\Resolver\Result\CacheKey\CalculationException;
|
|
use Magento\GraphQlResolverCache\Model\Resolver\Result\CacheKey\Calculator\ProviderInterface;
|
|
use Magento\GraphQlResolverCache\Model\Resolver\Result\ResolverIdentityClassProvider;
|
|
use Magento\GraphQlResolverCache\Model\Resolver\Result\Type as GraphQlResolverCache;
|
|
use Magento\GraphQlResolverCache\Model\Resolver\Result\ValueProcessorInterface;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
/**
|
|
* Plugin to cache resolver result where applicable.
|
|
*
|
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
|
*/
|
|
class Cache
|
|
{
|
|
/**
|
|
* GraphQL Resolver cache type
|
|
*
|
|
* @var GraphQlResolverCache
|
|
*/
|
|
private $graphQlResolverCache;
|
|
|
|
/**
|
|
* @var SerializerInterface
|
|
*/
|
|
private $serializer;
|
|
|
|
/**
|
|
* @var CacheState
|
|
*/
|
|
private $cacheState;
|
|
|
|
/**
|
|
* @var ResolverIdentityClassProvider
|
|
*/
|
|
private $resolverIdentityClassProvider;
|
|
|
|
/**
|
|
* @var ValueProcessorInterface
|
|
*/
|
|
private ValueProcessorInterface $valueProcessor;
|
|
|
|
/**
|
|
* @var ProviderInterface
|
|
*/
|
|
private ProviderInterface $keyCalculatorProvider;
|
|
|
|
/**
|
|
* @var LoggerInterface
|
|
*/
|
|
private $logger;
|
|
|
|
/**
|
|
* @param GraphQlResolverCache $graphQlResolverCache
|
|
* @param SerializerInterface $serializer
|
|
* @param CacheState $cacheState
|
|
* @param ResolverIdentityClassProvider $resolverIdentityClassProvider
|
|
* @param ValueProcessorInterface $valueProcessor
|
|
* @param ProviderInterface $keyCalculatorProvider
|
|
* @param LoggerInterface $logger
|
|
*/
|
|
public function __construct(
|
|
GraphQlResolverCache $graphQlResolverCache,
|
|
SerializerInterface $serializer,
|
|
CacheState $cacheState,
|
|
ResolverIdentityClassProvider $resolverIdentityClassProvider,
|
|
ValueProcessorInterface $valueProcessor,
|
|
ProviderInterface $keyCalculatorProvider,
|
|
LoggerInterface $logger
|
|
) {
|
|
$this->graphQlResolverCache = $graphQlResolverCache;
|
|
$this->serializer = $serializer;
|
|
$this->cacheState = $cacheState;
|
|
$this->resolverIdentityClassProvider = $resolverIdentityClassProvider;
|
|
$this->valueProcessor = $valueProcessor;
|
|
$this->keyCalculatorProvider = $keyCalculatorProvider;
|
|
$this->logger = $logger;
|
|
}
|
|
|
|
/**
|
|
* Checks for cacheability of resolver's data, and, if cacheable, loads and persists cache entry for future use
|
|
*
|
|
* @param ResolverInterface $subject
|
|
* @param \Closure $proceed
|
|
* @param Field $field
|
|
* @param ContextInterface $context
|
|
* @param ResolveInfo $info
|
|
* @param array|null $value
|
|
* @param array|null $args
|
|
* @return mixed|Value
|
|
*/
|
|
public function aroundResolve(
|
|
ResolverInterface $subject,
|
|
\Closure $proceed,
|
|
Field $field,
|
|
$context,
|
|
ResolveInfo $info,
|
|
array $value = null,
|
|
array $args = null
|
|
) {
|
|
// even though a frontend access proxy is used to prevent saving/loading in $graphQlResolverCache when it is
|
|
// disabled, it's best to return as early as possible to avoid unnecessary processing
|
|
if (!$this->cacheState->isEnabled(GraphQlResolverCache::TYPE_IDENTIFIER)
|
|
|| $info->operation->operation !== 'query'
|
|
) {
|
|
return $proceed($field, $context, $info, $value, $args);
|
|
}
|
|
|
|
$identityProvider = $this->resolverIdentityClassProvider->getIdentityFromResolver($subject);
|
|
|
|
if (!$identityProvider) { // not cacheable; proceed
|
|
return $this->executeResolver($proceed, $field, $context, $info, $value, $args);
|
|
}
|
|
|
|
// Cache key provider may base cache key on the parent resolver value
|
|
// $value is processed on key calculation if needed
|
|
try {
|
|
$cacheKey = $this->prepareCacheIdentifier($subject, $args, $value);
|
|
} catch (CalculationException $e) {
|
|
$this->logger->warning(
|
|
sprintf(
|
|
"Unable to obtain cache key for %s resolver results, proceeding to invoke resolver."
|
|
. "Original exception message: %s ",
|
|
get_class($subject),
|
|
$e->getMessage()
|
|
)
|
|
);
|
|
return $this->executeResolver($proceed, $field, $context, $info, $value, $args);
|
|
}
|
|
|
|
$cachedResult = $this->graphQlResolverCache->load($cacheKey);
|
|
|
|
if ($cachedResult !== false) {
|
|
$returnValue = $this->serializer->unserialize($cachedResult);
|
|
$this->valueProcessor->processCachedValueAfterLoad($info, $subject, $cacheKey, $returnValue);
|
|
return $returnValue;
|
|
}
|
|
|
|
$returnValue = $this->executeResolver($proceed, $field, $context, $info, $value, $args);
|
|
|
|
// $value (parent value) is preprocessed (hydrated) on the previous step
|
|
$identities = $identityProvider->getIdentities($returnValue, $value);
|
|
|
|
if (count($identities)) {
|
|
$cachedValue = $returnValue;
|
|
$this->valueProcessor->preProcessValueBeforeCacheSave($subject, $cachedValue);
|
|
$this->graphQlResolverCache->save(
|
|
$this->serializer->serialize($cachedValue),
|
|
$cacheKey,
|
|
$identities,
|
|
false // use default lifetime directive
|
|
);
|
|
unset($cachedValue);
|
|
}
|
|
|
|
return $returnValue;
|
|
}
|
|
|
|
/**
|
|
* Call proceed method with context.
|
|
*
|
|
* @param \Closure $closure
|
|
* @param Field $field
|
|
* @param ContextInterface $context
|
|
* @param ResolveInfo $info
|
|
* @param array|null $value
|
|
* @param array|null $args
|
|
* @return mixed
|
|
*/
|
|
private function executeResolver(
|
|
\Closure $closure,
|
|
Field $field,
|
|
ContextInterface $context,
|
|
ResolveInfo $info,
|
|
array &$value = null,
|
|
array $args = null
|
|
) {
|
|
if (is_array($value)) {
|
|
$this->valueProcessor->preProcessParentValue($value);
|
|
}
|
|
return $closure($field, $context, $info, $value, $args);
|
|
}
|
|
|
|
/**
|
|
* Generate cache key incorporating factors from parameters.
|
|
*
|
|
* @param ResolverInterface $resolver
|
|
* @param array|null $args
|
|
* @param array|null $value
|
|
*
|
|
* @return string
|
|
* @throws CalculationException
|
|
*/
|
|
private function prepareCacheIdentifier(
|
|
ResolverInterface $resolver,
|
|
?array $args,
|
|
?array $value
|
|
): string {
|
|
$queryPayloadHash = sha1(get_class($resolver) . $this->serializer->serialize($args ?? []));
|
|
|
|
return GraphQlResolverCache::CACHE_TAG
|
|
. '_'
|
|
. $this->keyCalculatorProvider->getKeyCalculatorForResolver($resolver)->calculateCacheKey($value)
|
|
. '_'
|
|
. $queryPayloadHash;
|
|
}
|
|
}
|