PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/jelix-plugins/kvdb/file/file.kvdriver.php

https://github.com/foxmask/Booster
PHP | 472 lines | 261 code | 61 blank | 150 comment | 71 complexity | 6aa5d914fc9e1b78fd90dffa10af019e MD5 | raw file
  1. <?php
  2. /**
  3. * @package jelix
  4. * @subpackage kvdb
  5. * @author Zend Technologies
  6. * @contributor Tahina Ramaroson, Sylvain de Vathaire, Laurent Jouanneau
  7. * @copyright 2005-2008 Zend Technologies USA Inc (http://www.zend.com), 2008 Neov, 2010-2011 Laurent Jouanneau
  8. * The implementation of this class is based on Zend Cache Backend File class
  9. * Few lines of code was adapted for Jelix
  10. * @licence see LICENCE file
  11. */
  12. /**
  13. * driver for jKVDb which store key values in files
  14. */
  15. class fileKVDriver extends jKVDriver implements jIKVPersistent, jIKVttl {
  16. /**
  17. * directory where to put the files
  18. * @var string
  19. * @access protected
  20. */
  21. protected $_storage_dir;
  22. /**
  23. * enable / disable locking file
  24. * @var boolean
  25. * @access protected
  26. */
  27. protected $_file_locking = true;
  28. /**
  29. * directory level
  30. * @var integer
  31. * @access protected
  32. */
  33. protected $_directory_level = 2;
  34. /**
  35. * umask for directory structure
  36. * @var string
  37. * @access protected
  38. */
  39. protected $_directory_umask = 0700;
  40. /**
  41. * umask for cache files
  42. * @var string
  43. * @access protected
  44. */
  45. protected $file_umask = 0600;
  46. /**
  47. * profil name used in the ini file
  48. * @var string
  49. * @access public
  50. */
  51. public $profil_name;
  52. /**
  53. * automatic cleaning process
  54. * 0 means disabled, 1 means systematic cache cleaning of expired data (at each set or add call), greater values mean less frequent cleaning
  55. * @var integer
  56. * @access public
  57. */
  58. public $automatic_cleaning_factor = 0;
  59. public function _connect() {
  60. if (isset($this->_profile['storage_dir']) && $this->_profile['storage_dir']!='') {
  61. $this->_storage_dir = str_replace(array('var:', 'temp:'), array(jApp::varPath(), jApp::tempPath()), $this->_profile['storage_dir']);
  62. $this->_storage_dir = rtrim($this->_storage_dir, '\\/') . DIRECTORY_SEPARATOR;
  63. }
  64. else
  65. $this->_storage_dir = jApp::varPath('kvfiles/');
  66. jFile::createDir($this->_storage_dir);
  67. if (isset($this->_profile['file_locking'])) {
  68. $this->_file_locking = ($this->_profile['file_locking']?true:false);
  69. }
  70. if (isset($this->_profile['automatic_cleaning_factor'])) {
  71. $this->automatic_cleaning_factor = $this->_profile['automatic_cleaning_factor'];
  72. }
  73. if (isset($this->_profile['directory_level']) && $this->_profile['directory_level'] > 0) {
  74. $this->_directory_level = $this->_profile['directory_level'];
  75. if ($this->_directory_level > 16)
  76. $this->_directory_level = 16;
  77. }
  78. if (isset($this->_profile['directory_umask']) && is_string($this->_profile['directory_umask']) && $this->_profile['directory_umask']!='') {
  79. $this->_directory_umask = octdec($this->_profile['directory_umask']);
  80. }
  81. if (isset($this->_profile['file_umask']) && is_string($this->_profile['file_umask']) && $this->_profile['file_umask']!='') {
  82. $this->file_umask = octdec($this->_profile['file_umask']);
  83. }
  84. }
  85. protected function _disconnect() {
  86. }
  87. /**
  88. * reads a specific data
  89. * @param mixed $key key or array of keys used for storing data
  90. * @return mixed $data data or null if failure
  91. */
  92. public function get ($key) {
  93. $data = null;
  94. if (is_array($key)) {
  95. $data = array();
  96. foreach ($key as $k) {
  97. if ($this->_isStored($k)) {
  98. $data[$k] = $this->_getFileContent($this->_getFilePath($k));
  99. }
  100. }
  101. }
  102. else {
  103. if ($this->_isStored($key)) {
  104. $data = $this->_getFileContent($this->_getFilePath($key));
  105. }
  106. }
  107. return $data;
  108. }
  109. /**
  110. * set a specific data
  111. * @param string $key key used for storing data
  112. * @param mixed $value data to store
  113. * @return boolean false if failure
  114. */
  115. public function set ($key, $value) {
  116. $filePath = $this->_getFilePath($key);
  117. $this->_createDir(dirname($filePath));
  118. return $this->_setFileContent ($filePath, $value, time() + 3650*24*3600);
  119. }
  120. /**
  121. * insert new data
  122. * @param string $key key used for storing data
  123. * @param mixed $value data to store
  124. * @return boolean false if failure
  125. */
  126. public function insert ($key, $value) {
  127. if ($this->_isStored($key))
  128. return false;
  129. else
  130. return $this->set($key, $value);
  131. }
  132. /**
  133. * replace a specific data
  134. * @param string $key key used for storing data
  135. * @param mixed $value data to store
  136. * @return boolean false if failure
  137. */
  138. public function replace ($key, $value){
  139. if (!$this->_isStored($key))
  140. return false;
  141. else
  142. return $this->set($key, $value);
  143. }
  144. /**
  145. * delete a specific data
  146. * @param string $key key used for storing data in the cache
  147. * @return boolean false if failure
  148. */
  149. public function delete ($key) {
  150. $filePath = $this->_getFilePath($key);
  151. if (file_exists($filePath)) {
  152. if (!(@unlink($filePath))) {
  153. touch($filePath,strtotime("-1 day"));
  154. return false;
  155. }
  156. return true;
  157. }
  158. return false;
  159. }
  160. /**
  161. * clear all data in the cache
  162. * @return boolean false if failure
  163. */
  164. public function flush (){
  165. $this->_removeDir($this->_storage_dir, true, false);
  166. return true;
  167. }
  168. /**
  169. * append a string to an existing key value
  170. * @param string $key the key of the value to modify
  171. * @param string $value the value to append to the current key value
  172. * @return boolean false if failure
  173. */
  174. public function append ($key, $value) {
  175. $oldData = $this->get($key);
  176. if ($oldData === null)
  177. return false;
  178. if ($this->setWithTtl($key, $oldData.$value, filemtime($this->_getFilePath($key))))
  179. return $oldData.$value;
  180. else
  181. return false;
  182. }
  183. /**
  184. * prepend a string to an existing key value
  185. * @param string $key the key of the value to modify
  186. * @param string $value the value to prepend to the current key value
  187. * @return boolean false if failure
  188. */
  189. public function prepend ($key, $value) {
  190. $oldData = $this->get($key);
  191. if ($oldData === null)
  192. return false;
  193. if ($this->setWithTtl($key, $value.$oldData, filemtime($this->_getFilePath($key))))
  194. return $value.$oldData;
  195. else
  196. return false;
  197. }
  198. /**
  199. * increment a specific data value by $var
  200. * @param string $key key used for storing data in the cache
  201. * @param mixed $var value used
  202. * @return integer the result, or false if failure
  203. */
  204. public function increment ($key, $var=1) {
  205. $oldData = $this->get($key);
  206. if ($oldData === null)
  207. return false;
  208. if (!is_numeric($oldData)) {
  209. return false;
  210. }
  211. $data = $oldData + $var;
  212. if ($data < 0 || $oldData == $data) {
  213. return false;
  214. }
  215. return ( $this->setWithTtl($key, (int)$data, filemtime($this->_getFilePath($key))) ) ? (int)$data : false;
  216. }
  217. /**
  218. * decrement a specific data value by $var
  219. * @param string $key key used for storing data in the cache
  220. * @param mixed $var value used
  221. * @return integer the result, or false if failure
  222. */
  223. public function decrement ($key, $var=1) {
  224. $oldData = $this->get($key);
  225. if ($oldData === null)
  226. return false;
  227. if (!is_numeric($oldData)) {
  228. return false;
  229. }
  230. $data = $oldData - (int)$var;
  231. if ($data < 0 || $oldData == $data) {
  232. return false;
  233. }
  234. return ( $this->setWithTtl($key, (int)$data, filemtime($this->_getFilePath($key))) ) ? (int)$data : false;
  235. }
  236. // ----------------------------------- jIKVPersistent
  237. public function sync() { }
  238. // ----------------------------------- jIKVttl
  239. /**
  240. * set a specific data with a ttl
  241. * @param string $key key used for storing data
  242. * @param mixed $var data to store
  243. * @param int $ttl time to live, in seconds
  244. * @return boolean false if failure
  245. */
  246. public function setWithTtl ($key, $var, $ttl) {
  247. $filePath = $this->_getFilePath($key);
  248. $this->_createDir(dirname($filePath));
  249. if ($ttl > 0) {
  250. if ($ttl <= 2592000) {
  251. $ttl += time();
  252. }
  253. }
  254. else
  255. $ttl = time() + 3650*24*3600;
  256. return $this->_setFileContent ($filePath, $var, $ttl);
  257. }
  258. /**
  259. * remove from the cache data of which TTL was expired
  260. */
  261. public function garbage () {
  262. $this->_removeDir($this->_storage_dir, false, false);
  263. return true;
  264. }
  265. /**
  266. * Check if exist a non expired stored file for the key $key
  267. * @param string $key key used for the specific data
  268. * @return boolean
  269. */
  270. protected function _isStored ($key){
  271. $filePath = $this->_getFilePath($key);
  272. if (!file_exists($filePath))
  273. return false;
  274. if (version_compare(PHP_VERSION, '5.3.0') >= 0)
  275. clearstatcache(false, $filePath);
  276. else
  277. clearstatcache();
  278. $mt = filemtime($filePath);
  279. return ( $mt >= time() || $mt == 0) && is_readable ($filePath);
  280. }
  281. /**
  282. * Reading in a file.
  283. * @param string $File file name
  284. * @return mixed return file content or null if failure.
  285. */
  286. protected function _getFileContent ($filePath){
  287. if (!is_file($filePath)) {
  288. return null;
  289. }
  290. $f = @fopen($filePath, 'rb');
  291. if (!$f) {
  292. return null;
  293. }
  294. if ($this->_file_locking) {
  295. @flock($f, LOCK_SH);
  296. }
  297. $content = stream_get_contents($f);
  298. if ($this->_file_locking) {
  299. @flock($f, LOCK_UN);
  300. }
  301. @fclose($f);
  302. try {
  303. $content = unserialize($content);
  304. }
  305. catch(Exception $e) {
  306. throw new jException('jelix~kvstore.error.unserialize.data',array($this->profil_name,$e->getMessage()));
  307. }
  308. return $content;
  309. }
  310. /**
  311. * Writing in a file.
  312. * @param string $filePath file name
  313. * @param string $DataToWrite data to write in the file
  314. * @param integer $mtime modification time
  315. * @return boolean true if success of writing operation
  316. */
  317. protected function _setFileContent ($filePath, $dataToWrite, $mtime) {
  318. if (is_resource($dataToWrite))
  319. return false;
  320. try {
  321. $dataToWrite = serialize($dataToWrite);
  322. }
  323. catch(Exception $e) {
  324. throw new jException('jelix~kvstore.error.serialize.data', array($this->profil_name, $e->getMessage()));
  325. }
  326. $f = @fopen($filePath, 'wb+');
  327. if (!$f) {
  328. return false;
  329. }
  330. if ($this->_file_locking) {
  331. @flock($f, LOCK_EX);
  332. }
  333. @fwrite($f, $dataToWrite);
  334. if ($this->_file_locking) {
  335. @flock($f, LOCK_UN);
  336. }
  337. @fclose($f);
  338. @chmod($filePath, $this->file_umask);
  339. touch($filePath, $mtime);
  340. return true;
  341. }
  342. /**
  343. * create a directory
  344. * It creates also all necessary parent directory
  345. * @param string $dir the path of the directory
  346. */
  347. protected function _createDir($dir) {
  348. if (!file_exists($dir)) {
  349. $this->_createDir(dirname($dir));
  350. @mkdir($dir, $this->_directory_umask);
  351. @chmod($dir, $this->_directory_umask); //this line is required in some configurations
  352. }
  353. }
  354. protected $keyPath = array();
  355. /**
  356. * make and return a file name (with path)
  357. *
  358. * @param string $key the key
  359. * @return string File name (with path)
  360. */
  361. protected function _getFilePath($key) {
  362. if (isset($this->keyPath[$key]))
  363. return $this->keyPath[$key];
  364. $hash = md5($key);
  365. $path = $this->_storage_dir;
  366. if ($this->_directory_level > 0) {
  367. for ($i = 0; $i < $this->_directory_level; $i++) {
  368. $path .= substr($hash, $i*2, 2 ). DIRECTORY_SEPARATOR;
  369. }
  370. }
  371. if (preg_match("/^([a-zA-Z0-9\._\-]+)/", $key, $m))
  372. $fileName = $path.$hash."_".$m[1];
  373. else
  374. $fileName = $path.$hash;
  375. $this->keyPath[$key] = $fileName;
  376. return $fileName;
  377. }
  378. /**
  379. * recursive function deleting a directory for the class
  380. * @param string $dir the path of the directory to remove recursively
  381. * @param boolean $all directory deleting mode. If true delete all else delete files expired
  382. */
  383. protected function _removeDir($dir, $all = true, $deleteParent = true) {
  384. if (file_exists($dir) && $handle = opendir($dir)) {
  385. while (false !== ($file = readdir($handle))) {
  386. if ($file != '.' && $file != '..') {
  387. $f = $dir.'/'.$file;
  388. if (is_file ($f)) {
  389. if ($all) {
  390. @unlink ($f);
  391. } else {
  392. if (version_compare(PHP_VERSION, '5.3.0') >= 0)
  393. clearstatcache(false, $f);
  394. else
  395. clearstatcache();
  396. if (time() > filemtime($f) && filemtime($f) != 0) {
  397. @unlink ($f);
  398. }
  399. }
  400. }
  401. if (is_dir ($f)) {
  402. self::_removeDir($f, $all);
  403. }
  404. }
  405. }
  406. closedir($handle);
  407. if ($deleteParent)
  408. @rmdir($dir);
  409. }
  410. }
  411. }