From 0c2b17c80f54027aaebbc43b8efb21fdaffe8d45 Mon Sep 17 00:00:00 2001
From: Lukas Reschke <lukas@statuscode.ch>
Date: Fri, 7 Oct 2016 21:15:52 +0200
Subject: [PATCH] Cache AppInfo in Memory Cache if configured

This saves around 20ms on a bare-bone instance, on bigger ones more (depending on the number of installed apps).

See https://blackfire.io/profiles/compare/fc326ad3-100d-49b8-8ea9-8343240f53f3/graph

Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
---
 lib/private/App/InfoParser.php   | 23 ++++++++++++++++++++
 lib/private/legacy/app.php       |  2 +-
 tests/lib/App/InfoParserTest.php | 36 ++++++++++++++++++++++----------
 3 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php
index 3c14fe7c0cf..fbeb932763e 100644
--- a/lib/private/App/InfoParser.php
+++ b/lib/private/App/InfoParser.php
@@ -1,6 +1,7 @@
 <?php
 /**
  * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
  *
  * @author Andreas Fischer <bantu@owncloud.com>
  * @author Christoph Wurst <christoph@owncloud.com>
@@ -26,7 +27,18 @@
 
 namespace OC\App;
 
+use OCP\ICache;
+
 class InfoParser {
+	/** @var \OCP\ICache|null */
+	private $cache;
+
+	/**
+	 * @param ICache|null $cache
+	 */
+	public function __construct(ICache $cache = null) {
+		$this->cache = $cache;
+	}
 
 	/**
 	 * @param string $file the xml file to be loaded
@@ -37,6 +49,13 @@ class InfoParser {
 			return null;
 		}
 
+		if(!is_null($this->cache)) {
+			$fileCacheKey = $file . filemtime($file);
+			if ($cachedValue = $this->cache->get($fileCacheKey)) {
+				return json_decode($cachedValue, true);
+			}
+		}
+
 		libxml_use_internal_errors(true);
 		$loadEntities = libxml_disable_entity_loader(false);
 		$xml = simplexml_load_file($file);
@@ -119,6 +138,10 @@ class InfoParser {
 		if (isset($array['background-jobs']['job']) && is_array($array['background-jobs']['job'])) {
 			$array['background-jobs'] = $array['background-jobs']['job'];
 		}
+
+		if(!is_null($this->cache)) {
+			$this->cache->set($fileCacheKey, json_encode($array));
+		}
 		return $array;
 	}
 
diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php
index 1093f2c8814..5e05884f5c0 100644
--- a/lib/private/legacy/app.php
+++ b/lib/private/legacy/app.php
@@ -682,7 +682,7 @@ class OC_App {
 			$file = $appPath . '/appinfo/info.xml';
 		}
 
-		$parser = new InfoParser();
+		$parser = new InfoParser(\OC::$server->getMemCacheFactory()->create('core.appinfo'));
 		$data = $parser->parse($file);
 
 		if (is_array($data)) {
diff --git a/tests/lib/App/InfoParserTest.php b/tests/lib/App/InfoParserTest.php
index af2dfd2ef1c..5a3847a71e8 100644
--- a/tests/lib/App/InfoParserTest.php
+++ b/tests/lib/App/InfoParserTest.php
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * @author Thomas Müller
  * @copyright 2014 Thomas Müller deepdiver@owncloud.com
@@ -14,31 +13,46 @@ use OC\App\InfoParser;
 use Test\TestCase;
 
 class InfoParserTest extends TestCase {
+	/** @var OC\Cache\CappedMemoryCache */
+	private static $cache;
 
-	/** @var InfoParser */
-	private $parser;
-
-	public function setUp() {
-		$this->parser = new InfoParser();
+	public static function setUpBeforeClass() {
+		self::$cache = new OC\Cache\CappedMemoryCache();
 	}
 
-	/**
-	 * @dataProvider providesInfoXml
-	 */
-	public function testParsingValidXml($expectedJson, $xmlFile) {
+
+	public function parserTest($expectedJson, $xmlFile, $cache = null) {
+		$parser = new InfoParser($cache);
+
 		$expectedData = null;
 		if (!is_null($expectedJson)) {
 			$expectedData = json_decode(file_get_contents(OC::$SERVERROOT . "/tests/data/app/$expectedJson"), true);
 		}
-		$data = $this->parser->parse(OC::$SERVERROOT. "/tests/data/app/$xmlFile");
+		$data = $parser->parse(OC::$SERVERROOT. "/tests/data/app/$xmlFile");
 
 		$this->assertEquals($expectedData, $data);
 	}
 
+	/**
+	 * @dataProvider providesInfoXml
+	 */
+	public function testParsingValidXmlWithoutCache($expectedJson, $xmlFile) {
+		$this->parserTest($expectedJson, $xmlFile);
+	}
+
+	/**
+	 * @dataProvider providesInfoXml
+	 */
+	public function testParsingValidXmlWithCache($expectedJson, $xmlFile) {
+		$this->parserTest($expectedJson, $xmlFile, self::$cache);
+	}
+
 	function providesInfoXml() {
 		return array(
 			array('expected-info.json', 'valid-info.xml'),
 			array(null, 'invalid-info.xml'),
+			array('expected-info.json', 'valid-info.xml'),
+			array(null, 'invalid-info.xml'),
 		);
 	}
 }
-- 
GitLab