Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New PR for the cache function #7

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ class Configuration
**/
private $xpath;

/**
* Boolean indicating if configuration results should be cached
*
* @access private
* @var boolean
**/
private $useCaching;

/**
* The cache (used only when caching is enabled)
*
* @access private
* @var array
**/
private $cache = ['alwaysArray' => [], 'optionalArray' => []];

/**
* __construct
*
Expand All @@ -57,16 +73,19 @@ class Configuration
* @access public
* @param string|null $defaultConfigurationFile Location of the default XML configuration file
* @param string|null $xsdSchemaFile Location of the XSD file for configuration validation. Optional so that a default schema can also be provided by a subclass.
* @param boolean $useCaching Boolean indicating if caching should be used
* @return void
**/
public function __construct($defaultConfigurationFile = null, $xsdSchemaFile = null)
public function __construct($defaultConfigurationFile = null, $xsdSchemaFile = null, $useCaching = true)
{
if (is_file($defaultConfigurationFile)) {
$this->defaultConfigurationFile = $defaultConfigurationFile;
}
if (is_file($xsdSchemaFile)) {
$this->xsdSchemaFile = $xsdSchemaFile;
}

$this->useCaching = $useCaching;
}

/**
Expand Down Expand Up @@ -136,6 +155,35 @@ public function getDOMDocument()
* @return array|null
**/
public function get($xpathExpression, $alwaysReturnArray = false)
{
if ($this->useCaching === true && $alwaysReturnArray === true) {
if (!array_key_exists($xpathExpression, $this->cache['alwaysArray'])) {
$this->cache['alwaysArray'][$xpathExpression] = $this->getFromDOMDocument($xpathExpression, $alwaysReturnArray);
}

return $this->cache['alwaysArray'][$xpathExpression];
} elseif ($this->useCaching === true) {
if (!array_key_exists($xpathExpression, $this->cache['optionalArray'])) {
$this->cache['optionalArray'][$xpathExpression] = $this->getFromDOMDocument($xpathExpression, $alwaysReturnArray);
}

return $this->cache['optionalArray'][$xpathExpression];
} else {
return $this->getFromDOMDocument($xpathExpression, $alwaysReturnArray);
}
}

/**
* getFromDOMDocument
*
* Returns an array representation of the XML nodes from the requested $xpathExpression in the loaded dom
*
* @access private
* @param string $xpathExpression
* @param boolean $alwaysReturnArray
* @return array|null
**/
private function getFromDOMDocument($xpathExpression, $alwaysReturnArray = false)
{
if (($this->xpath instanceof DOMXPath) === false) {
return;
Expand Down Expand Up @@ -206,6 +254,7 @@ private function validateConfiguration(DOMDocument $dom)
$this->xpath = new DOMXPath($dom);

$this->cleanupDOMDocument();
$this->cache = ['alwaysArray' => [], 'optionalArray' => []]; // reset cache for the new dom
} else {
$this->triggerHumanReadableErrors();
if (realpath(parse_url($dom->documentURI, PHP_URL_PATH)) !== realpath($this->defaultConfigurationFile)) {
Expand Down
50 changes: 45 additions & 5 deletions tests/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ public function testLoadInvalidConfigurationTriggersErrors()
}

/**
* testGetWithDefaultConfiguration
* testGetWithDefaultConfigurationWithoutCache
*
* Tests if Configuration::get returns the expected result from the default configuration
* Tests if Configuration::get returns the expected result from the default configuration (without caching)
*
* @dataProvider provideTestGetWithDefaultConfiguration
*
Expand All @@ -83,7 +83,29 @@ public function testLoadInvalidConfigurationTriggersErrors()
* @param array|string $expectedResult
* @return void
**/
public function testGetWithDefaultConfiguration($xpathExpression, $alwaysReturnArray, $expectedResult)
public function testGetWithDefaultConfigurationWithoutCache($xpathExpression, $alwaysReturnArray, $expectedResult)
{
$configuration = new Configuration(__DIR__ . '/Resources/configuration/default.xml', __DIR__ . '/Resources/xsd/default.xsd', false);
$configuration->loadConfiguration(null);

$this->assertEquals($expectedResult, $configuration->get($xpathExpression, $alwaysReturnArray));
$this->assertSame($expectedResult, $configuration->get($xpathExpression, $alwaysReturnArray));
}

/**
* testGetWithDefaultConfigurationWithCache
*
* Tests if Configuration::get returns the expected result from the default configuration (with caching)
*
* @dataProvider provideTestGetWithDefaultConfiguration
*
* @access public
* @param string $xpathExpression
* @param boolean $alwaysReturnArray
* @param array|string $expectedResult
* @return void
**/
public function testGetWithDefaultConfigurationWithCache($xpathExpression, $alwaysReturnArray, $expectedResult)
{
$configuration = new Configuration(__DIR__ . '/Resources/configuration/default.xml', __DIR__ . '/Resources/xsd/default.xsd');
$configuration->loadConfiguration(null);
Expand All @@ -92,13 +114,31 @@ public function testGetWithDefaultConfiguration($xpathExpression, $alwaysReturnA
$this->assertSame($expectedResult, $configuration->get($xpathExpression, $alwaysReturnArray));
}

/**
* testCachedResultsAreValidWhenLoadingNewFileInRuntime
*
* Confirms that the cache reads from the currenlty loaded configuration
*
* @access public
* @return void
**/
public function testCachedResultsAreValidWhenLoadingNewFileInRuntime()
{
$configuration = new Configuration(__DIR__ . '/Resources/configuration/default.xml', __DIR__ . '/Resources/xsd/default.xsd');
$configuration->loadConfiguration(null);
$this->assertEquals('Text content', $configuration->get('/test/fuzzy'));

$configuration->loadConfiguration(__DIR__ . '/Resources/configuration/optional.xml');
$this->assertEquals('Fuzzy text content', $configuration->get('/test/fuzzy'));
}

/**
* testLoadInvalidConfigurationLoadsDefaultConfigurationAfterTriggeringErrors
*
* Tests if loading an invalid configuration loads the default configuration after triggering errors (warnings)
*
* @depends testLoadInvalidConfigurationTriggersErrors
* @depends testGetWithDefaultConfiguration
* @depends testGetWithDefaultConfigurationWithCache
*
* @access public
* @return void
Expand Down Expand Up @@ -149,7 +189,7 @@ public function testGetWithoutSchemaDoesNotTriggerAFatalError()
*
* Tests if Configuration::getDOMDocument returns the loaded DOMDocument instance
*
* @depends testGetWithDefaultConfiguration
* @depends testGetWithDefaultConfigurationWithCache
*
* @access public
* @return void
Expand Down
12 changes: 12 additions & 0 deletions tests/Resources/configuration/optional.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<test>
<!-- Configuration for unit testing -->
<foo bar='bar attribute'>
<foo bar='more bar attribute'/>
</foo>
<bar>
<fuzz id='fuzzy'/>
<fuzz id='very fuzzy'/>
</bar>
<fuzzy>Fuzzy text content</fuzzy>
</test>
35 changes: 35 additions & 0 deletions tests/test_performance_cached_configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

use Nijens\Utilities\Configuration;

/*
* POC script to demonstrate caching is usually faster
* As it's not always faster, it's not suitable to be tested in a unit test
*/
require '../vendor/autoload.php';

foreach ([2, 3] as $count) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This foreach can be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it can but it'd change that the scripts tests. You could add a 4 and a 5 (or others) to the array as well, just depends on what you want to script to test. I choose to test the performance benefits when calling the same get 2 or 3 times, obviously you'd benefit more from caching as you call a function that can return a cached variable more often.

$success = 0;
for ($i = 0; $i < 100; ++$i) {
$cached = new Configuration(__DIR__ . '/Resources/configuration/default.xml', __DIR__ . '/Resources/xsd/default.xsd');
$cached->loadConfiguration(null);
$notCached = new Configuration(__DIR__ . '/Resources/configuration/default.xml', __DIR__ . '/Resources/xsd/default.xsd', false);
$notCached->loadConfiguration(null);
$cachedStart = microtime(true);
for ($j = 0; $j < $count; ++$j) {
$cached->get('/test/foo');
}
$cachedTime = microtime(true) - $cachedStart;

$notCachedStart = microtime(true);
for ($j = 0; $j < $count; ++$j) {
$notCached->get('/test/foo');
}
$notCachedTime = microtime(true) - $notCachedStart;

if ($notCachedTime > $cachedTime) {
++$success;
}
}
echo "Cached was fastest {$success} times out of 100 on {$count} calls\n";
}