PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/framework/Prefs/lib/Horde/Prefs.php

https://github.com/imr/horde
PHP | 499 lines | 235 code | 55 blank | 209 comment | 18 complexity | 6c11017f20720c2829fd5df13884f3df MD5 | raw file
  1. <?php
  2. /**
  3. * The Horde_Prefs:: class provides a common abstracted interface into the
  4. * various preferences storage mediums. It also includes all of the
  5. * functions for retrieving, storing, and checking preference values.
  6. *
  7. * Copyright 1999-2014 Horde LLC (http://www.horde.org/)
  8. *
  9. * See the enclosed file COPYING for license information (LGPL). If you
  10. * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  11. *
  12. * @author Jon Parise <jon@horde.org>
  13. * @category Horde
  14. * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
  15. * @package Prefs
  16. */
  17. class Horde_Prefs implements ArrayAccess
  18. {
  19. /* The default scope name. */
  20. const DEFAULT_SCOPE = 'horde';
  21. /**
  22. * Caching object.
  23. *
  24. * @var Horde_Prefs_Cache
  25. */
  26. protected $_cache;
  27. /**
  28. * General library options.
  29. *
  30. * @var array
  31. */
  32. protected $_opts = array(
  33. 'cache' => null,
  34. 'logger' => null,
  35. 'password' => '',
  36. 'sizecallback' => null,
  37. 'storage' => null,
  38. 'user' => ''
  39. );
  40. /**
  41. * String containing the name of the current scope. This is used
  42. * to differentiate between sets of preferences. By default, preferences
  43. * belong to this scope.
  44. *
  45. * @var string
  46. */
  47. protected $_scope = self::DEFAULT_SCOPE;
  48. /**
  49. * Scope list. Keys are scope names, values are Horde_Prefs_Scope
  50. * objects.
  51. *
  52. * @var array
  53. */
  54. protected $_scopes = array();
  55. /**
  56. * The storage driver(s).
  57. *
  58. * @var array
  59. */
  60. protected $_storage;
  61. /**
  62. * Constructor.
  63. *
  64. * @param string $scope The scope for this set of preferences.
  65. * @param mixed $storage The storage object(s) to use. Either a single
  66. * Horde_Prefs_Storage object, or an array of
  67. * objects.
  68. * @param array $opts Additional confguration options:
  69. * <pre>
  70. * cache - (Horde_Prefs_Cache) The cache driver to use.
  71. * DEFAULT: No caching.
  72. * logger - (Horde_Log_Logger) Logging object.
  73. * DEFAULT: NONE
  74. * password - (string) The password associated with 'user'.
  75. * DEFAULT: NONE
  76. * sizecallback - (callback) If set, called when setting a value in the
  77. * backend.
  78. * DEFAULT: NONE
  79. * user - (string) The name of the user who owns this set of preferences.
  80. * DEFAULT: NONE
  81. * </pre>
  82. */
  83. public function __construct($scope, $storage = null, array $opts = array())
  84. {
  85. $this->_opts = array_merge($this->_opts, $opts);
  86. $this->_cache = isset($this->_opts['cache'])
  87. ? $this->_opts['cache']
  88. : new Horde_Prefs_Cache_Null($this->getUser());
  89. $this->_scope = $scope;
  90. if (is_null($storage)) {
  91. $storage = array(new Horde_Prefs_Storage_Null($this->getUser()));
  92. } elseif (!is_array($storage)) {
  93. $storage = array($storage);
  94. }
  95. $this->_storage = $storage;
  96. register_shutdown_function(array($this, 'store'), false);
  97. }
  98. /**
  99. * Return the cache object.
  100. *
  101. * @since 2.6.0
  102. *
  103. * @return Horde_Prefs_Cache_Base Cache object.
  104. */
  105. public function getCache()
  106. {
  107. return $this->_cache;
  108. }
  109. /**
  110. * Return the user who owns these preferences.
  111. *
  112. * @return string The user these preferences are for.
  113. */
  114. public function getUser()
  115. {
  116. return $this->_opts['user'];
  117. }
  118. /**
  119. * Get the current scope.
  120. *
  121. * @return string The current scope (application).
  122. */
  123. public function getScope()
  124. {
  125. return $this->_scope;
  126. }
  127. /**
  128. * Returns the storage drivers.
  129. *
  130. * @return array The storage drivers.
  131. */
  132. public function getStorage()
  133. {
  134. return $this->_storage;
  135. }
  136. /**
  137. * Removes a preference entry from the $prefs hash.
  138. *
  139. * @param string $pref The name of the preference to remove. If null,
  140. * removes all prefs.
  141. */
  142. public function remove($pref = null)
  143. {
  144. $to_remove = array();
  145. if (is_null($pref)) {
  146. foreach ($this->_scopes as $key => $val) {
  147. $to_remove[$key] = array_keys(iterator_to_array($val));
  148. }
  149. } elseif ($scope = $this->_getScope($pref)) {
  150. $to_remove[$scope] = array($pref);
  151. }
  152. foreach ($to_remove as $key => $val) {
  153. $scope = $this->_scopes[$key];
  154. foreach ($val as $prefname) {
  155. $scope->remove($prefname);
  156. foreach ($this->_storage as $storage) {
  157. try {
  158. $storage->remove($scope->scope, $prefname);
  159. } catch (Exception $e) {}
  160. }
  161. }
  162. }
  163. }
  164. /**
  165. * Sets the given preference to the specified value if the preference is
  166. * modifiable.
  167. *
  168. * @param string $pref The preference name to modify.
  169. * @param string $val The preference value (UTF-8).
  170. * @param array $opts Additional options:
  171. * <pre>
  172. * - force: (boolean) If true, will set the value disregarding the
  173. * current locked status of the pref. (@since 2.5.0)
  174. * DEFAULT: false
  175. * - nosave: (boolean) If true, the preference will not be saved to the
  176. * storage backend(s).
  177. * DEFAULT: false
  178. * </pre>
  179. *
  180. * @return boolean True if the value was successfully set, false on a
  181. * failure.
  182. * @throws Horde_Prefs_Exception
  183. */
  184. public function setValue($pref, $val, array $opts = array())
  185. {
  186. /* Exit early if preference doesn't exist or is locked. */
  187. if (!($scope = $this->_getScope($pref)) ||
  188. (empty($opts['force']) &&
  189. $this->_scopes[$scope]->isLocked($pref))) {
  190. return false;
  191. }
  192. // Check to see if the value exceeds the allowable storage limit.
  193. if ($this->_opts['sizecallback'] &&
  194. call_user_func($this->_opts['sizecallback'], $pref, strlen($val))) {
  195. return false;
  196. }
  197. $this->_scopes[$scope]->set($pref, $val);
  198. if (!empty($opts['nosave'])) {
  199. $this->_scopes[$scope]->setDirty($pref, false);
  200. }
  201. foreach ($this->_storage as $storage) {
  202. $storage->onChange($scope, $pref);
  203. }
  204. if ($this->_opts['logger']) {
  205. $this->_opts['logger']->log(__CLASS__ . ': Storing preference value (' . $pref . ')', 'DEBUG');
  206. }
  207. return true;
  208. }
  209. /**
  210. * Shortcut to setValue().
  211. */
  212. public function __set($name, $value)
  213. {
  214. return $this->setValue($name, $value);
  215. }
  216. /**
  217. * Returns the value of the requested preference.
  218. *
  219. * @param string $pref The preference name.
  220. *
  221. * @return string The value of the preference (UTF-8), null if it doesn't
  222. * exist.
  223. */
  224. public function getValue($pref)
  225. {
  226. return ($scope = $this->_getScope($pref))
  227. ? $this->_scopes[$scope]->get($pref)
  228. : null;
  229. }
  230. /**
  231. * Shortcut to getValue().
  232. */
  233. public function __get($name)
  234. {
  235. return $this->getValue($name);
  236. }
  237. /**
  238. * Mark a preference as locked.
  239. *
  240. * @param string $pref The preference name.
  241. * @param boolean $locked Is the preference locked?
  242. */
  243. public function setLocked($pref, $bool)
  244. {
  245. if ($scope = $this->_getScope($pref)) {
  246. $this->_scopes[$scope]->setLocked($pref, $bool);
  247. }
  248. }
  249. /**
  250. * Is a preference locked?
  251. *
  252. * @param string $pref The preference name.
  253. *
  254. * @return boolean Whether the preference is locked.
  255. */
  256. public function isLocked($pref)
  257. {
  258. return ($scope = $this->_getScope($pref))
  259. ? $this->_scopes[$scope]->isLocked($pref)
  260. : false;
  261. }
  262. /**
  263. * Is a preference marked dirty?
  264. *
  265. * @param string $pref The preference name.
  266. *
  267. * @return boolean True if the preference is marked dirty.
  268. */
  269. public function isDirty($pref)
  270. {
  271. return ($scope = $this->_getScope($pref))
  272. ? $this->_scopes[$scope]->isDirty($pref)
  273. : false;
  274. }
  275. /**
  276. * Returns the default value of the given preference.
  277. *
  278. * @param string $pref The name of the preference to get the default for.
  279. *
  280. * @return string The preference's default value.
  281. */
  282. public function getDefault($pref)
  283. {
  284. return ($scope = $this->_getScope($pref))
  285. ? $this->_scopes[$scope]->getDefault($pref)
  286. : null;
  287. }
  288. /**
  289. * Determines if the current preference value is the default value.
  290. *
  291. * @param string $pref The name of the preference to check.
  292. *
  293. * @return boolean True if the preference is the application default
  294. * value.
  295. */
  296. public function isDefault($pref)
  297. {
  298. return ($scope = $this->_getScope($pref))
  299. ? $this->_scopes[$scope]->isDefault($pref)
  300. : false;
  301. }
  302. /**
  303. * Returns the scope of a preference.
  304. *
  305. * @param string $pref The preference name.
  306. *
  307. * @return mixed The scope of the preference, or null if it doesn't
  308. * exist.
  309. */
  310. protected function _getScope($pref)
  311. {
  312. $this->_loadScope($this->_scope);
  313. if ($this->_scopes[$this->_scope]->exists($pref)) {
  314. return $this->_scope;
  315. } elseif ($this->_scope != self::DEFAULT_SCOPE) {
  316. $this->_loadScope(self::DEFAULT_SCOPE);
  317. if ($this->_scopes[self::DEFAULT_SCOPE]->exists($pref)) {
  318. return self::DEFAULT_SCOPE;
  319. }
  320. }
  321. return null;
  322. }
  323. /**
  324. * Retrieves preferences for the current scope.
  325. *
  326. * @param string $scope Optional scope specifier - if not present the
  327. * current scope will be used.
  328. */
  329. public function retrieve($scope = null)
  330. {
  331. if (!is_null($scope)) {
  332. $this->changeScope($scope);
  333. }
  334. $this->_loadScope(self::DEFAULT_SCOPE);
  335. $this->_loadScope($this->getScope());
  336. }
  337. /**
  338. * Changes the current preference scope.
  339. *
  340. * @since 2.6.0
  341. *
  342. * @param string $scope Scope specifier.
  343. */
  344. public function changeScope($scope)
  345. {
  346. $this->_scope = $scope;
  347. }
  348. /**
  349. * Load a specific preference scope.
  350. *
  351. * @param string $scope The scope to load.
  352. */
  353. protected function _loadScope($scope)
  354. {
  355. // Return if we've already loaded these prefs.
  356. if (!empty($this->_scopes[$scope])) {
  357. return;
  358. }
  359. // Now check the prefs cache for existing values.
  360. try {
  361. if ((($cached = $this->_cache->get($scope)) !== false) &&
  362. ($cached instanceof Horde_Prefs_Scope)) {
  363. $this->_scopes[$scope] = $cached;
  364. return;
  365. }
  366. } catch (Horde_Prefs_Exception $e) {}
  367. $scope_ob = new Horde_Prefs_Scope($scope);
  368. $scope_ob->init = true;
  369. // Need to set object in scopes array now, since the storage object
  370. // might recursively call the prefs object.
  371. $this->_scopes[$scope] = $scope_ob;
  372. foreach ($this->_storage as $storage) {
  373. $scope_ob = $storage->get($scope_ob);
  374. }
  375. $scope_ob->init = false;
  376. $this->_cache->store($scope_ob);
  377. }
  378. /**
  379. * Save all dirty prefs to the storage backend.
  380. *
  381. * @param boolean $throw Throw exception on error? If false, ignores
  382. * errors. (Since 2.1.0)
  383. */
  384. public function store($throw = true)
  385. {
  386. foreach ($this->_scopes as $scope) {
  387. if ($scope->isDirty()) {
  388. foreach ($this->_storage as $storage) {
  389. try {
  390. $storage->store($scope);
  391. } catch (Exception $e) {
  392. if ($throw) {
  393. throw $e;
  394. }
  395. }
  396. }
  397. try {
  398. $this->_cache->store($scope);
  399. } catch (Exception $e) {
  400. if ($throw) {
  401. throw $e;
  402. }
  403. }
  404. }
  405. }
  406. }
  407. /**
  408. * Cleanup (e.g. remove) scope(s).
  409. *
  410. * @param boolean $all Cleanup all scopes. If false, clean present scope
  411. * only.
  412. */
  413. public function cleanup($all = false)
  414. {
  415. if ($all) {
  416. /* Destroy all scopes. */
  417. $this->_scopes = array();
  418. $scope = null;
  419. } else {
  420. unset($this->_scopes[$this->_scope]);
  421. $scope = $this->_scope;
  422. }
  423. try {
  424. $this->_cache->remove($scope);
  425. } catch (Horde_Prefs_Exception $e) {}
  426. }
  427. /* ArrayAccess methods. */
  428. public function offsetExists($offset)
  429. {
  430. return !is_null($this->getValue($offset));
  431. }
  432. public function offsetGet($offset)
  433. {
  434. return $this->getValue($offset);
  435. }
  436. public function offsetSet($offset, $value)
  437. {
  438. $this->setValue($offset, $value);
  439. }
  440. public function offsetUnset($offset)
  441. {
  442. $this->remove($offset);
  443. }
  444. }