浏览代码

[Validator] Added support for metadata caching

Bernhard Schussek 15 年之前
父节点
当前提交
8c9f9de086

+ 36 - 0
src/Symfony/Components/Validator/Mapping/Cache/CacheInterface.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Symfony\Components\Validator\Mapping\Cache;
+
+use Symfony\Components\Validator\Mapping\ClassMetadata;
+
+/**
+ * Persists ClassMetadata instances in a cache
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
+ */
+interface CacheInterface
+{
+    /**
+     * Returns whether metadata for the given class exists in the cache
+     *
+     * @param string $class
+     */
+    public function has($class);
+
+    /**
+     * Returns the metadata for the given class from the cache
+     *
+     * @param string $class
+     * @return ClassMetadata
+     */
+    public function read($class);
+
+    /**
+     * Stores a class metadata in the cache
+     *
+     * @param $class
+     * @param $metadata
+     */
+    public function write(ClassMetadata $metadata);
+}

+ 37 - 12
src/Symfony/Components/Validator/Mapping/ClassMetadataFactory.php

@@ -3,16 +3,33 @@
 namespace Symfony\Components\Validator\Mapping;
 
 use Symfony\Components\Validator\Mapping\Loader\LoaderInterface;
+use Symfony\Components\Validator\Mapping\Cache\CacheInterface;
 
+/**
+ * Implementation of ClassMetadataFactoryInterface
+ *
+ * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
+ */
 class ClassMetadataFactory implements ClassMetadataFactoryInterface
 {
+    /**
+     * The loader for loading the class metadata
+     * @var LoaderInterface
+     */
     protected $loader;
 
+    /**
+     * The cache for caching class metadata
+     * @var CacheInterface
+     */
+    protected $cache;
+
     protected $loadedClasses = array();
 
-    public function __construct(LoaderInterface $loader)
+    public function __construct(LoaderInterface $loader, CacheInterface $cache = null)
     {
         $this->loader = $loader;
+        $this->cache = $cache;
     }
 
     public function getClassMetadata($class)
@@ -20,21 +37,29 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
         $class = ltrim($class, '\\');
 
         if (!isset($this->loadedClasses[$class])) {
-            $metadata = new ClassMetadata($class);
+            if ($this->cache !== null && $this->cache->has($class)) {
+                $this->loadedClasses[$class] = $this->cache->read($class);
+            } else {
+                $metadata = new ClassMetadata($class);
 
-            // Include constraints from the parent class
-            if ($parent = $metadata->getReflectionClass()->getParentClass()) {
-                $metadata->mergeConstraints($this->getClassMetadata($parent->getName()));
-            }
+                // Include constraints from the parent class
+                if ($parent = $metadata->getReflectionClass()->getParentClass()) {
+                    $metadata->mergeConstraints($this->getClassMetadata($parent->getName()));
+                }
 
-            // Include constraints from all implemented interfaces
-            foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) {
-                $metadata->mergeConstraints($this->getClassMetadata($interface->getName()));
-            }
+                // Include constraints from all implemented interfaces
+                foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) {
+                    $metadata->mergeConstraints($this->getClassMetadata($interface->getName()));
+                }
 
-            $this->loader->loadClassMetadata($metadata);
+                $this->loader->loadClassMetadata($metadata);
 
-            $this->loadedClasses[$class] = $metadata;
+                $this->loadedClasses[$class] = $metadata;
+
+                if ($this->cache !== null) {
+                    $this->cache->write($metadata);
+                }
+            }
         }
 
         return $this->loadedClasses[$class];

+ 50 - 0
tests/Symfony/Tests/Components/Validator/Mapping/ClassMetadataFactoryTest.php

@@ -55,6 +55,56 @@ class ClassMetadataFactoryTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals($constraints, $metadata->getConstraints());
     }
+
+    public function testWriteMetadataToCache()
+    {
+        $cache = $this->getMock('Symfony\Components\Validator\Mapping\Cache\CacheInterface');
+        $factory = new ClassMetadataFactory(new TestLoader(), $cache);
+
+        $tester = $this;
+        $constraints = array(
+            new ConstraintA(array('groups' => array('Default', 'EntityParent'))),
+        );
+
+        $cache->expects($this->once())
+              ->method('has')
+              ->with($this->equalTo(self::PARENTCLASS))
+              ->will($this->returnValue(false));
+        $cache->expects($this->once())
+              ->method('write')
+              ->will($this->returnCallback(function($metadata) use ($tester, $constraints) {
+                  $tester->assertEquals($constraints, $metadata->getConstraints());
+              }));
+
+        $metadata = $factory->getClassMetadata(self::PARENTCLASS);
+
+        $this->assertEquals(self::PARENTCLASS, $metadata->getClassName());
+        $this->assertEquals($constraints, $metadata->getConstraints());
+    }
+
+    public function testReadMetadataFromCache()
+    {
+        $loader = $this->getMock('Symfony\Components\Validator\Mapping\Loader\LoaderInterface');
+        $cache = $this->getMock('Symfony\Components\Validator\Mapping\Cache\CacheInterface');
+        $factory = new ClassMetadataFactory($loader, $cache);
+
+        $tester = $this;
+        $metadata = new ClassMetadata(self::PARENTCLASS);
+        $metadata->addConstraint(new ConstraintA());
+
+        $loader->expects($this->never())
+               ->method('loadClassMetadata');
+
+        $cache->expects($this->once())
+              ->method('has')
+              ->with($this->equalTo(self::PARENTCLASS))
+              ->will($this->returnValue(true));
+        $cache->expects($this->once())
+              ->method('read')
+              ->will($this->returnValue($metadata));
+
+        $this->assertEquals($metadata,$factory->getClassMetadata(self::PARENTCLASS));
+    }
 }
 
 class TestLoader implements LoaderInterface