From 1da4d15cd8e39cecbfa1fed90adb935fbb0a77cb Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Wed, 22 Jun 2016 17:32:20 +0200
Subject: [PATCH] Issue #2447803 by amateescu, Fabianx, Berdir: Use FileCache
 for caching discovered annotations

---
 .../Discovery/AnnotatedClassDiscovery.php     | 31 ++++++++++++++++++-
 .../Drupal/Component/Annotation/composer.json |  1 +
 .../src/Unit/ViewsHandlerManagerTest.php      |  1 +
 .../Field/BaseFieldDefinitionTestBase.php     |  1 +
 .../Core/Render/ElementInfoManagerTest.php    |  2 ++
 core/tests/Drupal/Tests/UnitTestCase.php      |  2 ++
 6 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
index 50d8856b3cf2..e564fd087a4d 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -3,6 +3,7 @@
 namespace Drupal\Component\Annotation\Plugin\Discovery;
 
 use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\FileCache\FileCacheFactory;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Doctrine\Common\Annotations\SimpleAnnotationReader;
@@ -48,6 +49,13 @@ class AnnotatedClassDiscovery implements DiscoveryInterface {
    */
   protected $annotationNamespaces = [];
 
+  /**
+   * The file cache object.
+   *
+   * @var \Drupal\Component\FileCache\FileCacheInterface
+   */
+  protected $fileCache;
+
   /**
    * Constructs a new instance.
    *
@@ -64,6 +72,10 @@ function __construct($plugin_namespaces = array(), $plugin_definition_annotation
     $this->pluginNamespaces = $plugin_namespaces;
     $this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
     $this->annotationNamespaces = $annotation_namespaces;
+
+    $file_cache_suffix = str_replace('\\', '_', $plugin_definition_annotation_name);
+    $file_cache_suffix .= ':' . hash('crc32b', serialize($annotation_namespaces));
+    $this->fileCache = FileCacheFactory::get('annotation_discovery:' . $file_cache_suffix);
   }
 
   /**
@@ -110,6 +122,14 @@ public function getDefinitions() {
           );
           foreach ($iterator as $fileinfo) {
             if ($fileinfo->getExtension() == 'php') {
+              if ($cached = $this->fileCache->get($fileinfo->getPathName())) {
+                if (isset($cached['id'])) {
+                  // Explicitly unserialize this to create a new object instance.
+                  $definitions[$cached['id']] = unserialize($cached['content']);
+                }
+                continue;
+              }
+
               $sub_path = $iterator->getSubIterator()->getSubPath();
               $sub_path = $sub_path ? str_replace(DIRECTORY_SEPARATOR, '\\', $sub_path) . '\\' : '';
               $class = $namespace . '\\' . $sub_path . $fileinfo->getBasename('.php');
@@ -123,7 +143,16 @@ public function getDefinitions() {
               /** @var $annotation \Drupal\Component\Annotation\AnnotationInterface */
               if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
                 $this->prepareAnnotationDefinition($annotation, $class);
-                $definitions[$annotation->getId()] = $annotation->get();
+
+                $id = $annotation->getId();
+                $content = $annotation->get();
+                $definitions[$id] = $content;
+                // Explicitly serialize this to create a new object instance.
+                $this->fileCache->set($fileinfo->getPathName(), ['id' => $id, 'content' => serialize($content)]);
+              }
+              else {
+                // Store a NULL object, so the file is not reparsed again.
+                $this->fileCache->set($fileinfo->getPathName(), [NULL]);
               }
             }
           }
diff --git a/core/lib/Drupal/Component/Annotation/composer.json b/core/lib/Drupal/Component/Annotation/composer.json
index ac59021da91a..76254ea7faa1 100644
--- a/core/lib/Drupal/Component/Annotation/composer.json
+++ b/core/lib/Drupal/Component/Annotation/composer.json
@@ -8,6 +8,7 @@
     "php": ">=5.5.9",
     "doctrine/common": "2.5.*",
     "doctrine/annotations": "1.2.*",
+    "drupal/core-fileCache": "~8.2",
     "drupal/core-plugin": "~8.2",
     "drupal/core-utility": "~8.2"
   },
diff --git a/core/modules/views/tests/src/Unit/ViewsHandlerManagerTest.php b/core/modules/views/tests/src/Unit/ViewsHandlerManagerTest.php
index d85fb43acecf..efbbe49e6f6a 100644
--- a/core/modules/views/tests/src/Unit/ViewsHandlerManagerTest.php
+++ b/core/modules/views/tests/src/Unit/ViewsHandlerManagerTest.php
@@ -42,6 +42,7 @@ class ViewsHandlerManagerTest extends UnitTestCase {
    * {@inheritdoc}
    */
   protected function setUp() {
+    parent::setUp();
     $this->viewsData = $this->getMockBuilder('Drupal\views\ViewsData')
       ->disableOriginalConstructor()
       ->getMock();
diff --git a/core/tests/Drupal/Tests/Core/Field/BaseFieldDefinitionTestBase.php b/core/tests/Drupal/Tests/Core/Field/BaseFieldDefinitionTestBase.php
index e6a5de81b68a..acb7346db0d3 100644
--- a/core/tests/Drupal/Tests/Core/Field/BaseFieldDefinitionTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Field/BaseFieldDefinitionTestBase.php
@@ -24,6 +24,7 @@ abstract class BaseFieldDefinitionTestBase extends UnitTestCase {
    * {@inheritdoc}
    */
   protected function setUp() {
+    parent::setUp();
 
     // getModuleAndPath() returns an array of the module name and directory.
     list($module_name, $module_dir) = $this->getModuleAndPath();
diff --git a/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php b/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php
index 647d82dbc6dc..0d5aec69c53d 100644
--- a/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/ElementInfoManagerTest.php
@@ -58,6 +58,8 @@ class ElementInfoManagerTest extends UnitTestCase {
    * @covers ::__construct
    */
   protected function setUp() {
+    parent::setUp();
+
     $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
     $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 77a3d2253faa..d8fbebcb2948 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -43,6 +43,8 @@ protected function setUp() {
     // Ensure that the NullFileCache implementation is used for the FileCache as
     // unit tests should not be relying on caches implicitly.
     FileCacheFactory::setConfiguration(['default' => ['class' => '\Drupal\Component\FileCache\NullFileCache']]);
+    // Ensure that FileCacheFactory has a prefix.
+    FileCacheFactory::setPrefix('prefix');
 
     $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
   }
-- 
GitLab