2017-02-06 2 views
12

Ich mache etwas Programmierung in Silex mit den Symfony-Komponenten und ich denke, ich habe einen Fehler mit den symfony/serializer und symfony/validator Komponenten gefunden.Werden diese Daten von einer anderen Komponente überschrieben?

Zuerst lassen Sie mich erklären, was ich zu erreichen trage, dann gehen wir zum Code. Mein Ziel ist es, eine Klasse mit Informationen wie Serialisierungsdirektiven und Validierungsdirektiven zu kommentieren. Da das Lesen dieser Annotationen eine kleine CPU kosten kann, speichere ich sie gerne im Speicher. Zu diesem Zweck verwende ich Memcache-Wrapper im Doctrine/Common/Cache-Paket.

Das Problem, dem ich gegenüberstehe, ist, dass sowohl die symfony/serializer als auch die symfony/validator Metadaten mit dem Klassennamen als Schlüssel in den Cache schreiben. Wenn sie später versuchen, die Metadaten abzurufen, lösen sie eine Ausnahme aus, weil der Cache ungültige Metadaten hat, entweder eine Instanz von Symfony\Component\Validator\Mapping\ClassMetadata oder Symfony\Component\Serializer\Mapping\ClassMetadataInterface.

folgt ein reproduzierbares Beispiel (sorry, wenn seine große, habe ich versucht, so klein wie möglich zu machen):

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

Nach dem Ausführen dieses Beispiels Sie fatale Fehler bekommen. Kann mir jemand bestätigen, dass ich hier keinen schweren Fehler mache?

Momentan ist meine Problemumgehung dafür die DoctrineCache Klasse neu schreiben, die Verwendung eines Namespace für die Cache-Schlüssel. Es funktioniert, aber ich denke, es ist hässlich.

Antwort

2

Ich denke, was Sie tun müssen, ist zwei separate CacheDrivers. Siehe https://github.com/doctrine/cache/blob/master/lib/Doctrine/Common/Cache/CacheProvider.php für wie Namespaces dort verwendet werden.

Sie könnten:

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

ich den Code nur getrimmt haben, um die Teile zu zeigen, dass ein Teil der Lösung spielen. Ich hoffe das hilft!

Verwandte Themen