2017-02-06 9 views
12

Я занимаюсь программированием в Silex с компонентами symfony, и я думаю, что нашел ошибку с symfony/serializer и symfony/validator компонентами.Переписываются ли эти данные другим компонентом?

Прежде всего позвольте мне объяснить, к чему я стремлюсь достичь, а затем перейдем к коду. Моя цель - аннотировать класс с информацией, такой как директивы сериализации, а также директивы валидации. Поскольку чтение этих аннотаций может стоить litle cpu, мне нравится кэшировать их в памяти. Для этой цели я использую оболочку memcache в пакете Doctrine/Common/Cache.

Проблема, с которой я сталкиваюсь, заключается в том, что как symfony/serializer, так и symfony/validator записывают метаданные в кеш с использованием имени класса в качестве ключа. Когда они пытаются получить метаданные позже, они генерируют исключение, потому что кэш имеет недопустимые метаданные, либо экземпляр Symfony\Component\Validator\Mapping\ClassMetadata, либо Symfony\Component\Serializer\Mapping\ClassMetadataInterface.

Ниже приведен пример воспроизводимый (извините, если его большое, я пытался сделать как можно меньше):

use Symfony\Component\Serializer\Annotation\Groups; 
use Symfony\Component\Validator\Constraints as Assert; 

class Foo 
{ 
    /** 
    * @var int 
    * @Assert\NotBlank(message="This field cannot be empty") 
    */ 
    private $someProperty; 

    /** 
    * @return int 
    * @Groups({"some_group"}) 
    */ 
    public function getSomeProperty() { 
     return $this->someProperty; 
    } 
} 


use Doctrine\Common\Annotations\AnnotationReader; 
use \Memcache as MemcachePHP; 
use Doctrine\Common\Cache\MemcacheCache as MemcacheWrapper; 

$loader = require_once __DIR__ . '/../vendor/autoload.php'; 

\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$loader, 'loadClass']); 

$memcache = new MemcachePHP(); 

if (! $memcache->connect('localhost', '11211')) { 
    throw new \Exception('Unable to connect to memcache server'); 
} 

$cacheDriver = new MemcacheWrapper(); 
$cacheDriver->setMemcache($memcache); 

$app = new \Silex\Application(); 

$app->register(new Silex\Provider\SerializerServiceProvider()); 

$app['serializer.normalizers'] = function() use ($app, $cacheDriver) { 
    $classMetadataFactory = new Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory(
     new Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader(new AnnotationReader()), $cacheDriver); 

    return [new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer($classMetadataFactory) ]; 
}; 

$app->register(new Silex\Provider\ValidatorServiceProvider(), [ 
    'validator.mapping.class_metadata_factory' => 
     new \Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory(
      new \Symfony\Component\Validator\Mapping\Loader\AnnotationLoader(new AnnotationReader()), 
      new \Symfony\Component\Validator\Mapping\Cache\DoctrineCache($cacheDriver) 
     ) 
]); 

$app->get('/', function(\Silex\Application $app) { 
    $foo = new Foo(); 

    $app['validator']->validate($foo); 
    $json = $app['serializer']->serialize($foo, 'json'); 

    return new \Symfony\Component\HttpFoundation\JsonResponse($json, \Symfony\Component\HttpFoundation\Response::HTTP_OK, [], true); 
}); 

$app->error(function (\Exception $e, \Symfony\Component\HttpFoundation\Request $request, $code) { 
    return new \Symfony\Component\HttpFoundation\Response('We are sorry, but something went terribly wrong.' . $e->getMessage()); 
}); 

$app->run(); 

После запуска этого примера вы получите фатальные ошибки. Может ли кто-нибудь подтвердить, что я не совершаю здесь трудную ошибку?

В настоящее время мое обходное решение для этого переписывает класс DoctrineCache, используя пространство имен для ключей кеша. Его работа, но я считаю его уродливым.

ответ

2

Я думаю, что вам нужно сделать, это два отдельных CacheDrivers. См. https://github.com/doctrine/cache/blob/master/lib/Doctrine/Common/Cache/CacheProvider.php о том, как там используются пространства имён.

Вы могли:

$validatorCacheDriver = new MemcacheWrapper(); 
$validatorCacheDriver->setMemcache($memcache); 
$validatorCacheDriver->setNamespace('symfony_validator'); 

$serializerCacheDriver = new MemcacheWrapper(); 
$serializerCacheDriver->setMemcache($memcache); 
$serializerCacheDriver->setNamespace('symfony_serializer'); 

// note that the two drivers are using the same memcache instance, 
// so only one connection will be used. 

$app['serializer.normalizers'] = function() use ($app, $serializerCacheDriver) { 
    $classMetadataFactory = new Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory(
     new Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader(new AnnotationReader()), $serializerCacheDriver); 

    return [new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer($classMetadataFactory) ]; 
}; 

$app->register(new Silex\Provider\ValidatorServiceProvider(), [ 
    'validator.mapping.class_metadata_factory' => 
     new \Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory(
      new \Symfony\Component\Validator\Mapping\Loader\AnnotationLoader(new AnnotationReader()), 
      new \Symfony\Component\Validator\Mapping\Cache\DoctrineCache($validatorCacheDriver) 
     ) 
]); 

Я обрезается код, чтобы показывать только те части, которые играют определенную роль в решении. Надеюсь, это поможет!

Смежные вопросы