2017-02-06 18 views
12

Robię programowanie w Silex z komponentami symfony i myślę, że znalazłem błąd z komponentami symfony/serializer i symfony/validator.Czy dane są nadpisywane przez inny komponent?

Najpierw pozwól mi wyjaśnić, co zamierzam osiągnąć, a następnie przejdźmy do kodu. Moim celem jest przypisywanie klasy do informacji, takich jak dyrektywy serializacji, a także dyrektywy dotyczące weryfikacji. Ponieważ odczyt tych adnotacji może kosztować małego procesora, lubię buforować je w pamięci. W tym celu używam opakowania memcache w pakiecie Doctrine/Common/Cache.

Problem polega na tym, że zarówno symfony/serializer jak i symfony/validator zapisują metadane do pamięci podręcznej, używając nazwy klasy jako klucza. Gdy próbują później odzyskać metadane, generują wyjątek, ponieważ pamięć podręczna zawiera nieprawidłowe metadane: instancję Symfony\Component\Validator\Mapping\ClassMetadata lub Symfony\Component\Serializer\Mapping\ClassMetadataInterface.

Poniżej znajduje się reproductible przykład (przepraszam, jeśli jego duże, starałem się zrobić tak małe, jak to możliwe):

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(); 

Po uruchomieniu tego przykładu można dostać błędy krytyczne. Czy ktoś może potwierdzić, że nie popełniam tutaj dużego błędu?

Obecnie moim rozwiązaniem jest przepisanie klasy DoctrineCache z wykorzystaniem przestrzeni nazw dla kluczy pamięci podręcznej. Działa, ale myślę, że to brzydkie.

Odpowiedz

2

Myślę, że musisz zrobić dwa oddzielne CacheDrivers. Zobacz https://github.com/doctrine/cache/blob/master/lib/Doctrine/Common/Cache/CacheProvider.php, w jaki sposób używane są przestrzenie nazw.

Mogłabyś:

$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) 
     ) 
]); 

mam przycięte kod, aby pokazać tylko te części, które odgrywają pewną rolę w roztworze. Mam nadzieję, że to pomoże!

Powiązane problemy