diff options
author | robocoder <anthon.pang@gmail.com> | 2011-03-05 21:28:08 +0300 |
---|---|---|
committer | robocoder <anthon.pang@gmail.com> | 2011-03-05 21:28:08 +0300 |
commit | 6f7dccc9c68e18c834246ce472e0f70b3844e000 (patch) | |
tree | cbb7496f777f40e4e4fb8f5a7dff5f28f9db53a2 | |
parent | 410da5dae2ca94a97faeb924414360e381bf277d (diff) |
fixes #2150
git-svn-id: http://dev.piwik.org/svn/trunk@4030 59fd770c-687e-43c8-a1e3-f5a4ff64c105
-rw-r--r-- | core/Option.php | 81 | ||||
-rw-r--r-- | tests/core/Option.test.php | 165 |
2 files changed, 240 insertions, 6 deletions
diff --git a/core/Option.php b/core/Option.php index c8147a23ce..39e12c98d3 100644 --- a/core/Option.php +++ b/core/Option.php @@ -21,9 +21,13 @@ class Piwik_Option { private $all = array(); + private $loaded = false; static private $instance = null; + /** + * Singleton + * * @return Piwik_Option */ static public function getInstance() @@ -37,6 +41,12 @@ class Piwik_Option private function __construct() {} + /** + * Returns the option value for the requested option $name, fetching from database, if not in cache. + * + * @param string $name Key + * @return string|false Value or false, if not found + */ public function get($name) { $this->autoload(); @@ -55,6 +65,13 @@ class Piwik_Option return $value; } + /** + * Sets the option value in the database and cache + * + * @param string $name + * @param string $value + * @param int $autoload if set to 1, this option value will be automatically loaded; should be set to 1 for options that will always be used in the Piwik request. + */ public function set($name, $value, $autoload = 0) { $autoload = (int)$autoload; @@ -64,14 +81,64 @@ class Piwik_Option array($name, $value, $autoload, $value)); $this->all[$name] = $value; } - + + /** + * Delete key-value pair from database and reload cache. + * + * @param string $name Key to match exactly + * @param string $value Optional value + */ + public function delete($name, $value = null) + { + $sql = 'DELETE FROM '. Piwik_Common::prefixTable('option') . ' WHERE option_name = ?'; + $bind[] = $name; + + if(isset($value)) + { + $sql .= ' AND option_value = ?'; + $bind[] = $value; + } + + Piwik_Query($sql, $bind); + + $this->clearCache(); + } + + /** + * Delete key-value pair(s) from database and reload cache. + * The supplied pattern should use '%' as wildcards, and literal '_' should be escaped. + * + * @param string $name Pattern of key to match. + * @param string $value Optional value + */ + public function deleteLike($name, $value = null) + { + $sql = 'DELETE FROM `'. Piwik_Common::prefixTable('option') . '` WHERE option_name LIKE ?'; + $bind[] = $name; + + if(isset($value)) + { + $sql .= ' AND option_value = ?'; + $bind[] = $value; + } + + Piwik_Query($sql, $bind); + + $this->clearCache(); + } + + /** + * Initialize cache with autoload settings. + * + * @param bool $forceReload Forces a reload if true; default is false + */ private function autoload() { - static $loaded = false; - if($loaded) + if($this->loaded) { return; } + $all = Piwik_FetchAll('SELECT option_value, option_name FROM `'. Piwik_Common::prefixTable('option') . '` WHERE autoload = 1'); @@ -79,7 +146,8 @@ class Piwik_Option { $this->all[$option['option_name']] = $option['option_value']; } - $loaded = true; + + $this->loaded = true; } /** @@ -90,6 +158,7 @@ class Piwik_Option */ public function clearCache() { + $this->loaded = false; $this->all = array(); } } @@ -97,8 +166,8 @@ class Piwik_Option /** * Returns the option value for the requested option $name * - * @param string $name - * @return string|false if not found + * @param string $name Key + * @return string|false Value or false, if not found */ function Piwik_GetOption($name) { diff --git a/tests/core/Option.test.php b/tests/core/Option.test.php new file mode 100644 index 0000000000..8a9cf45e4c --- /dev/null +++ b/tests/core/Option.test.php @@ -0,0 +1,165 @@ +<?php +if(!defined('PIWIK_CONFIG_TEST_INCLUDED')) +{ + require_once dirname(__FILE__)."/../../tests/config_test.php"; +} + +require_once 'Database.test.php'; +require_once "Option.php"; + +class Test_Piwik_Option extends Test_Database +{ + public function test_get() + { + // empty table, expect false (i.e., not found) + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === false ); + + // populate table, expect '1' (i.e., found) + Piwik_Query("INSERT INTO ".Piwik_Common::prefixTable('option')." VALUES ('anonymous_defaultReport', '1', false)"); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + + // delete row (bypassing API), expect '1' (i.e., from cache) + Piwik_Query("DELETE FROM ".Piwik_Common::prefixTable('option')." WHERE option_name = ?", array('anonymous_defaultReport')); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + + // force cache reload, expect false (i.e., not found) + Piwik_Option::getInstance()->clearCache(); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === false ); + } + + public function test_getOption() + { + // empty table, expect false (i.e., not found) + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === false ); + + // populate table, expect '1' (i.e., found) + Piwik_Query("INSERT INTO ".Piwik_Common::prefixTable('option')." VALUES ('anonymous_defaultReport', '1',true)"); + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === '1' ); + + // delete row (bypassing API), expect '1' (i.e., from cache) + Piwik_Query("DELETE FROM ".Piwik_Common::prefixTable('option')." WHERE option_name = ?", array('anonymous_defaultReport')); + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === '1' ); + + // force cache reload, expect false (i.e., not found) + Piwik_Option::getInstance()->clearCache(); + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === false ); + } + + public function test_set() + { + // empty table, expect false (i.e., not found) + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === false ); + + // populate table, expect '1' + Piwik_Option::getInstance()->set('anonymous_defaultReport', '1', true); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + } + + public function test_setOption() + { + // empty table, expect false (i.e., not found) + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === false ); + + // populate table, expect '1' + Piwik_SetOption('anonymous_defaultReport', '1', false); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + } + + public function test_delete() + { + // empty table, expect false (i.e., not found) + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === false ); + $this->assertTrue( Piwik_GetOption('admin_defaultReport') === false ); + + // populate table, expect '1' + Piwik_SetOption('anonymous_defaultReport', '1', true); + Piwik_Option::getInstance()->delete('_defaultReport'); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + + // populate table, expect '2' + Piwik_SetOption('admin_defaultReport', '2', false); + Piwik_Option::getInstance()->delete('_defaultReport'); + $this->assertTrue( Piwik_Option::getInstance()->get('admin_defaultReport') === '2' ); + + // delete with non-matching value, expect '1' + Piwik_Option::getInstance()->delete('anonymous_defaultReport', '2'); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + + // delete with matching value, expect false + Piwik_Option::getInstance()->delete('anonymous_defaultReport', '1'); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === false ); + + // this shouldn't have been deleted, expect '2' + $this->assertTrue( Piwik_Option::getInstance()->get('admin_defaultReport') === '2' ); + + // deleted, expect false + Piwik_Option::getInstance()->delete('admin_defaultReport'); + $this->assertTrue( Piwik_Option::getInstance()->get('admin_defaultReport') === false ); + } + + public function test_deleteLike() + { + // empty table, expect false (i.e., not found) + $this->assertTrue( Piwik_GetOption('anonymous_defaultReport') === false ); + $this->assertTrue( Piwik_GetOption('admin_defaultReport') === false ); + $this->assertTrue( Piwik_GetOption('visitor_defaultReport') === false ); + + // insert guard - to test unescaped underscore + Piwik_SetOption('adefaultReport', '0', true); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // populate table, expect '1' + Piwik_SetOption('anonymous_defaultReport', '1', true); + Piwik_Option::getInstance()->deleteLike('\_defaultReport'); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // populate table, expect '2' + Piwik_SetOption('admin_defaultReport', '2', false); + Piwik_Option::getInstance()->deleteLike('\_defaultReport'); + $this->assertTrue( Piwik_Option::getInstance()->get('admin_defaultReport') === '2' ); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // populate table, expect '3' + Piwik_SetOption('visitor_defaultReport', '3', false); + Piwik_Option::getInstance()->deleteLike('\_defaultReport'); + $this->assertTrue( Piwik_Option::getInstance()->get('visitor_defaultReport') === '3' ); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // delete with non-matching value, expect '1' + Piwik_Option::getInstance()->deleteLike('%\_defaultReport', '4'); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === '1' ); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // delete with matching pattern, expect false + Piwik_Option::getInstance()->deleteLike('%\_defaultReport', '1'); + $this->assertTrue( Piwik_Option::getInstance()->get('anonymous_defaultReport') === false ); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // this shouldn't have been deleted, expect '2' and '3' + $this->assertTrue( Piwik_Option::getInstance()->get('admin_defaultReport') === '2' ); + $this->assertTrue( Piwik_Option::getInstance()->get('visitor_defaultReport') === '3' ); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // deleted, expect false (except for the guard) + Piwik_Option::getInstance()->deleteLike('%\_defaultReport'); + $this->assertTrue( Piwik_Option::getInstance()->get('admin_defaultReport') === false ); + $this->assertTrue( Piwik_Option::getInstance()->get('visitor_defaultReport') === false ); + + // unescaped backslash (single quotes) + Piwik_Option::getInstance()->deleteLike('%\_defaultReport'); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // escaped backslash (single quotes) + Piwik_Option::getInstance()->deleteLike('%\\_defaultReport'); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // unescaped backslash (double quotes) + Piwik_Option::getInstance()->deleteLike("%\_defaultReport"); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + + // escaped backslash (double quotes) + Piwik_Option::getInstance()->deleteLike("%\\_defaultReport"); + $this->assertTrue( Piwik_GetOption('adefaultReport') === '0' ); + } +} |