551 lines
14 KiB
PHP
551 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 1999-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Jon Parise <jon@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
|
|
* @package Prefs
|
|
*/
|
|
|
|
/**
|
|
* The Horde_Prefs class provides a common abstracted interface into the
|
|
* various preferences storage mediums.
|
|
*
|
|
* It also includes all of the functions for retrieving, storing, and checking
|
|
* preference values.
|
|
*
|
|
* @author Jon Parise <jon@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
|
|
* @package Prefs
|
|
*/
|
|
class Horde_Prefs implements ArrayAccess
|
|
{
|
|
/* The default scope name. */
|
|
const DEFAULT_SCOPE = 'horde';
|
|
|
|
/**
|
|
* Caching object.
|
|
*
|
|
* @var Horde_Prefs_Cache
|
|
*/
|
|
protected $_cache;
|
|
|
|
/**
|
|
* General library options.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_opts = array(
|
|
'cache' => null,
|
|
'logger' => null,
|
|
'sizecallback' => null,
|
|
'storage' => null,
|
|
'user' => ''
|
|
);
|
|
|
|
/**
|
|
* String containing the name of the current scope. This is used
|
|
* to differentiate between sets of preferences. By default, preferences
|
|
* belong to this scope.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_scope = self::DEFAULT_SCOPE;
|
|
|
|
/**
|
|
* Scope list. Keys are scope names, values are Horde_Prefs_Scope
|
|
* objects.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_scopes = array();
|
|
|
|
/**
|
|
* The storage driver(s).
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_storage;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $scope The scope for this set of preferences.
|
|
* @param mixed $storage The storage object(s) to use. Either a single
|
|
* Horde_Prefs_Storage object, or an array of
|
|
* objects.
|
|
* @param array $opts Additional confguration options:
|
|
* <pre>
|
|
* cache - (Horde_Prefs_Cache) The cache driver to use.
|
|
* DEFAULT: No caching.
|
|
* logger - (Horde_Log_Logger) Logging object.
|
|
* DEFAULT: NONE
|
|
* sizecallback - (callback) If set, called when setting a value in the
|
|
* backend.
|
|
* DEFAULT: NONE
|
|
* user - (string) The name of the user who owns this set of preferences.
|
|
* DEFAULT: NONE
|
|
* </pre>
|
|
*/
|
|
public function __construct($scope, $storage = null, array $opts = array())
|
|
{
|
|
$this->_opts = array_merge($this->_opts, $opts);
|
|
|
|
$this->_cache = isset($this->_opts['cache'])
|
|
? $this->_opts['cache']
|
|
: new Horde_Prefs_Cache_Null($this->getUser());
|
|
|
|
$this->_scope = $scope;
|
|
|
|
if (is_null($storage)) {
|
|
$storage = array(new Horde_Prefs_Storage_Null($this->getUser()));
|
|
} elseif (!is_array($storage)) {
|
|
$storage = array($storage);
|
|
}
|
|
$this->_storage = $storage;
|
|
|
|
register_shutdown_function(array($this, 'store'), false);
|
|
}
|
|
|
|
/**
|
|
* Return the cache object.
|
|
*
|
|
* @since 2.6.0
|
|
*
|
|
* @return Horde_Prefs_Cache_Base Cache object.
|
|
*/
|
|
public function getCache()
|
|
{
|
|
return $this->_cache;
|
|
}
|
|
|
|
/**
|
|
* Return the user who owns these preferences.
|
|
*
|
|
* @return string The user these preferences are for.
|
|
*/
|
|
public function getUser()
|
|
{
|
|
return $this->_opts['user'];
|
|
}
|
|
|
|
/**
|
|
* Get the current scope.
|
|
*
|
|
* @return string The current scope (application).
|
|
*/
|
|
public function getScope()
|
|
{
|
|
return $this->_scope;
|
|
}
|
|
|
|
/**
|
|
* Returns the current scope object.
|
|
*
|
|
* @since 2.9.0
|
|
*
|
|
* @return Horde_Prefs_Scope The current scope object.
|
|
*/
|
|
public function getScopeObject($scope = null)
|
|
{
|
|
if (!is_null($scope)) {
|
|
$this->changeScope($scope);
|
|
}
|
|
return $this->_scopes[$this->_scope];
|
|
}
|
|
|
|
/**
|
|
* Returns the storage drivers.
|
|
*
|
|
* @return array The storage drivers.
|
|
*/
|
|
public function getStorage()
|
|
{
|
|
return $this->_storage;
|
|
}
|
|
|
|
/**
|
|
* Removes a preference entry from the $prefs hash.
|
|
*
|
|
* @param string $pref The name of the preference to remove. If null,
|
|
* removes all preferences from the current scope.
|
|
*/
|
|
public function remove($pref = null)
|
|
{
|
|
$to_remove = array();
|
|
|
|
if (is_null($pref)) {
|
|
$to_remove[$this->_scope] = array_keys(iterator_to_array($this->_scopes[$this->_scope]));
|
|
} elseif ($scope = $this->_getScope($pref)) {
|
|
$to_remove[$scope] = array($pref);
|
|
}
|
|
|
|
foreach ($to_remove as $key => $val) {
|
|
$scope = $this->_scopes[$key];
|
|
|
|
foreach ($val as $prefname) {
|
|
$scope->remove($prefname);
|
|
|
|
// We remove all prefs at once in the backends below.
|
|
if (is_null($pref)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($this->_storage as $storage) {
|
|
try {
|
|
$storage->remove($key, $prefname);
|
|
} catch (Exception $e) {}
|
|
}
|
|
}
|
|
|
|
if (is_null($pref)) {
|
|
foreach ($this->_storage as $storage) {
|
|
try {
|
|
$storage->remove($key);
|
|
} catch (Exception $e) {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes all preference entries for the current user from the $prefs hash
|
|
* and the backends.
|
|
*
|
|
* @since Horde_Prefs 2.8.0
|
|
* @throws Horde_Prefs_Exception
|
|
*/
|
|
public function removeAll()
|
|
{
|
|
foreach ($this->_scopes as $key => $val) {
|
|
foreach (array_keys(iterator_to_array($val)) as $prefname) {
|
|
$this->_scopes[$key]->remove($prefname);
|
|
}
|
|
}
|
|
foreach ($this->_storage as $storage) {
|
|
$storage->remove();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the given preference to the specified value if the preference is
|
|
* modifiable.
|
|
*
|
|
* @param string $pref The preference name to modify.
|
|
* @param string $val The preference value (UTF-8).
|
|
* @param array $opts Additional options:
|
|
* <pre>
|
|
* - force: (boolean) If true, will set the value disregarding the
|
|
* current locked status of the pref. (@since 2.5.0)
|
|
* DEFAULT: false
|
|
* - nosave: (boolean) If true, the preference will not be saved to the
|
|
* storage backend(s).
|
|
* DEFAULT: false
|
|
* </pre>
|
|
*
|
|
* @return boolean True if the value was successfully set, false on a
|
|
* failure.
|
|
* @throws Horde_Prefs_Exception
|
|
*/
|
|
public function setValue($pref, $val, array $opts = array())
|
|
{
|
|
/* Exit early if preference doesn't exist or is locked. */
|
|
if (!($scope = $this->_getScope($pref)) ||
|
|
(empty($opts['force']) &&
|
|
$this->_scopes[$scope]->isLocked($pref))) {
|
|
return false;
|
|
}
|
|
|
|
// Check to see if the value exceeds the allowable storage limit.
|
|
if ($this->_opts['sizecallback'] &&
|
|
call_user_func($this->_opts['sizecallback'], $pref, strlen($val))) {
|
|
return false;
|
|
}
|
|
|
|
$this->_scopes[$scope]->set($pref, $val);
|
|
if (!empty($opts['nosave'])) {
|
|
$this->_scopes[$scope]->setDirty($pref, false);
|
|
}
|
|
|
|
foreach ($this->_storage as $storage) {
|
|
$storage->onChange($scope, $pref);
|
|
}
|
|
|
|
if ($this->_opts['logger']) {
|
|
$this->_opts['logger']->log(__CLASS__ . ': Storing preference value (' . $pref . ')', 'DEBUG');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Shortcut to setValue().
|
|
*/
|
|
public function __set($name, $value)
|
|
{
|
|
return $this->setValue($name, $value);
|
|
}
|
|
|
|
/**
|
|
* Returns the value of the requested preference.
|
|
*
|
|
* @param string $pref The preference name.
|
|
*
|
|
* @return string The value of the preference (UTF-8), null if it doesn't
|
|
* exist.
|
|
*/
|
|
public function getValue($pref)
|
|
{
|
|
return ($scope = $this->_getScope($pref))
|
|
? $this->_scopes[$scope]->get($pref)
|
|
: null;
|
|
}
|
|
|
|
/**
|
|
* Shortcut to getValue().
|
|
*/
|
|
public function __get($name)
|
|
{
|
|
return $this->getValue($name);
|
|
}
|
|
|
|
/**
|
|
* Mark a preference as locked.
|
|
*
|
|
* @param string $pref The preference name.
|
|
* @param boolean $locked Is the preference locked?
|
|
*/
|
|
public function setLocked($pref, $bool)
|
|
{
|
|
if ($scope = $this->_getScope($pref)) {
|
|
$this->_scopes[$scope]->setLocked($pref, $bool);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Is a preference locked?
|
|
*
|
|
* @param string $pref The preference name.
|
|
*
|
|
* @return boolean Whether the preference is locked.
|
|
*/
|
|
public function isLocked($pref)
|
|
{
|
|
return ($scope = $this->_getScope($pref))
|
|
? $this->_scopes[$scope]->isLocked($pref)
|
|
: false;
|
|
}
|
|
|
|
/**
|
|
* Is a preference marked dirty?
|
|
*
|
|
* @param string $pref The preference name.
|
|
*
|
|
* @return boolean True if the preference is marked dirty.
|
|
*/
|
|
public function isDirty($pref)
|
|
{
|
|
return ($scope = $this->_getScope($pref))
|
|
? $this->_scopes[$scope]->isDirty($pref)
|
|
: false;
|
|
}
|
|
|
|
/**
|
|
* Returns the default value of the given preference.
|
|
*
|
|
* @param string $pref The name of the preference to get the default for.
|
|
*
|
|
* @return string The preference's default value.
|
|
*/
|
|
public function getDefault($pref)
|
|
{
|
|
return ($scope = $this->_getScope($pref))
|
|
? $this->_scopes[$scope]->getDefault($pref)
|
|
: null;
|
|
}
|
|
|
|
/**
|
|
* Determines if the current preference value is the default value.
|
|
*
|
|
* @param string $pref The name of the preference to check.
|
|
*
|
|
* @return boolean True if the preference is the application default
|
|
* value.
|
|
*/
|
|
public function isDefault($pref)
|
|
{
|
|
return ($scope = $this->_getScope($pref))
|
|
? $this->_scopes[$scope]->isDefault($pref)
|
|
: true;
|
|
}
|
|
|
|
/**
|
|
* Returns the scope of a preference.
|
|
*
|
|
* @param string $pref The preference name.
|
|
*
|
|
* @return mixed The scope of the preference, or null if it doesn't
|
|
* exist.
|
|
*/
|
|
protected function _getScope($pref)
|
|
{
|
|
$this->_loadScope($this->_scope);
|
|
|
|
if ($this->_scopes[$this->_scope]->exists($pref)) {
|
|
return $this->_scope;
|
|
} elseif ($this->_scope != self::DEFAULT_SCOPE) {
|
|
$this->_loadScope(self::DEFAULT_SCOPE);
|
|
if ($this->_scopes[self::DEFAULT_SCOPE]->exists($pref)) {
|
|
return self::DEFAULT_SCOPE;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Retrieves preferences for the current scope.
|
|
*
|
|
* @param string $scope Optional scope specifier - if not present the
|
|
* current scope will be used.
|
|
*/
|
|
public function retrieve($scope = null)
|
|
{
|
|
if (!is_null($scope)) {
|
|
$this->changeScope($scope);
|
|
}
|
|
$this->_loadScope(self::DEFAULT_SCOPE);
|
|
$this->_loadScope($this->getScope());
|
|
}
|
|
|
|
/**
|
|
* Changes the current preference scope.
|
|
*
|
|
* @since 2.6.0
|
|
*
|
|
* @param string $scope Scope specifier.
|
|
*/
|
|
public function changeScope($scope)
|
|
{
|
|
$this->_scope = $scope;
|
|
}
|
|
|
|
/**
|
|
* Load a specific preference scope.
|
|
*
|
|
* @param string $scope The scope to load.
|
|
*/
|
|
protected function _loadScope($scope)
|
|
{
|
|
// Return if we've already loaded these prefs.
|
|
if (!empty($this->_scopes[$scope])) {
|
|
return;
|
|
}
|
|
|
|
// Now check the prefs cache for existing values.
|
|
try {
|
|
if ((($cached = $this->_cache->get($scope)) !== false) &&
|
|
($cached instanceof Horde_Prefs_Scope)) {
|
|
$this->_scopes[$scope] = $cached;
|
|
return;
|
|
}
|
|
} catch (Horde_Prefs_Exception $e) {}
|
|
|
|
$scope_ob = new Horde_Prefs_Scope($scope);
|
|
$scope_ob->init = true;
|
|
|
|
// Need to set object in scopes array now, since the storage object
|
|
// might recursively call the prefs object.
|
|
$this->_scopes[$scope] = $scope_ob;
|
|
|
|
foreach ($this->_storage as $storage) {
|
|
$scope_ob = $storage->get($scope_ob);
|
|
}
|
|
|
|
$scope_ob->init = false;
|
|
|
|
$this->_cache->store($scope_ob);
|
|
}
|
|
|
|
/**
|
|
* Save all dirty prefs to the storage backend.
|
|
*
|
|
* @param boolean $throw Throw exception on error? If false, ignores
|
|
* errors. (Since 2.1.0)
|
|
*/
|
|
public function store($throw = true)
|
|
{
|
|
foreach ($this->_scopes as $scope) {
|
|
if ($scope->isDirty()) {
|
|
foreach ($this->_storage as $storage) {
|
|
try {
|
|
$storage->store($scope);
|
|
} catch (Exception $e) {
|
|
if ($throw) {
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
$this->_cache->store($scope);
|
|
} catch (Exception $e) {
|
|
if ($throw) {
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleanup (e.g. remove) scope(s).
|
|
*
|
|
* @param boolean $all Cleanup all scopes. If false, clean present scope
|
|
* only.
|
|
*/
|
|
public function cleanup($all = false)
|
|
{
|
|
if ($all) {
|
|
/* Destroy all scopes. */
|
|
$this->_scopes = array();
|
|
$scope = null;
|
|
} else {
|
|
unset($this->_scopes[$this->_scope]);
|
|
$scope = $this->_scope;
|
|
}
|
|
|
|
try {
|
|
$this->_cache->remove($scope);
|
|
} catch (Horde_Prefs_Exception $e) {}
|
|
}
|
|
|
|
/* ArrayAccess methods. */
|
|
|
|
public function offsetExists($offset)
|
|
{
|
|
return !is_null($this->getValue($offset));
|
|
}
|
|
|
|
public function offsetGet($offset)
|
|
{
|
|
return $this->getValue($offset);
|
|
}
|
|
|
|
public function offsetSet($offset, $value)
|
|
{
|
|
$this->setValue($offset, $value);
|
|
}
|
|
|
|
public function offsetUnset($offset)
|
|
{
|
|
$this->remove($offset);
|
|
}
|
|
|
|
}
|