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

/library/Doctrine/Doctrine/Cache.php

https://github.com/ostric/e-learning
PHP | 416 lines | 198 code | 63 blank | 155 comment | 29 complexity | b120e6cf712a27c1280d11c5c788ba0f MD5 | raw file
  1. <?php
  2. /*
  3. * $Id: Cache.php 4520 2008-06-13 22:29:22Z jwage $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information, see
  19. * <http://www.phpdoctrine.org>.
  20. */
  21. /**
  22. * Doctrine_Cache
  23. *
  24. * @package Doctrine
  25. * @subpackage Cache
  26. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
  27. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  28. * @link www.phpdoctrine.org
  29. * @since 1.0
  30. * @version $Revision: 4520 $
  31. */
  32. class Doctrine_Cache extends Doctrine_EventListener implements Countable, IteratorAggregate
  33. {
  34. /**
  35. * @var array $_options an array of general caching options
  36. */
  37. protected $_options = array('size' => 1000,
  38. 'lifeTime' => 3600,
  39. 'addStatsPropability' => 0.25,
  40. 'savePropability' => 0.10,
  41. 'cleanPropability' => 0.01,
  42. 'statsFile' => '../data/stats.cache',
  43. );
  44. /**
  45. * @var array $_queries query stack
  46. */
  47. protected $_queries = array();
  48. /**
  49. * @var Doctrine_Cache_Interface $_driver the cache driver object
  50. */
  51. protected $_driver;
  52. /**
  53. * @var array $data current cache data array
  54. */
  55. protected $_data = array();
  56. /**
  57. * @var boolean $success the success of last operation
  58. */
  59. protected $_success = false;
  60. /**
  61. * constructor
  62. *
  63. * @param Doctrine_Cache_Interface|string $driver cache driver name or a driver object
  64. * @param array $options cache driver options
  65. */
  66. public function __construct($driver, $options = array())
  67. {
  68. if (is_object($driver)) {
  69. if ( ! ($driver instanceof Doctrine_Cache_Interface)) {
  70. throw new Doctrine_Cache_Exception('Driver should implement Doctrine_Cache_Interface.');
  71. }
  72. $this->_driver = $driver;
  73. $this->_driver->setOptions($options);
  74. } else {
  75. $class = 'Doctrine_Cache_' . ucwords(strtolower($driver));
  76. if ( ! class_exists($class)) {
  77. throw new Doctrine_Cache_Exception('Cache driver ' . $driver . ' could not be found.');
  78. }
  79. $this->_driver = new $class($options);
  80. }
  81. }
  82. /**
  83. * Get the current cache driver instance
  84. *
  85. * @return Doctrine_Cache_Driver $driver
  86. */
  87. public function getDriver()
  88. {
  89. return $this->_driver;
  90. }
  91. /**
  92. * Set option name and value
  93. *
  94. * @param mixed $option the option name
  95. * @param mixed $value option value
  96. * @return boolean TRUE on success, FALSE on failure
  97. */
  98. public function setOption($option, $value)
  99. {
  100. // sanity check (we need this since we are using isset() instead of array_key_exists())
  101. if ($value === null) {
  102. throw new Doctrine_Cache_Exception('Null values not accepted for options.');
  103. }
  104. if (isset($this->_options[$option])) {
  105. $this->_options[$option] = $value;
  106. return true;
  107. }
  108. return false;
  109. }
  110. /**
  111. * Get value of option name
  112. *
  113. * @param mixed $option the option name
  114. * @return mixed option value
  115. */
  116. public function getOption($option)
  117. {
  118. if ( ! isset($this->_options[$option])) {
  119. throw new Doctrine_Cache_Exception('Unknown option ' . $option);
  120. }
  121. return $this->_options[$option];
  122. }
  123. /**
  124. * Adds a query to internal query stack
  125. *
  126. * @param string|array $query sql query string
  127. * @param string $namespace connection namespace
  128. * @return void
  129. */
  130. public function add($query, $namespace = null)
  131. {
  132. if (isset($namespace)) {
  133. $this->_queries[$namespace][] = $query;
  134. } else {
  135. $this->_queries[] = $query;
  136. }
  137. }
  138. /**
  139. * Get array of all executed queries
  140. *
  141. * @param string $namespace optional query namespace
  142. * @return array an array of sql query strings
  143. */
  144. public function getAll($namespace = null)
  145. {
  146. if (isset($namespace)) {
  147. if ( ! isset($this->_queries[$namespace])) {
  148. return array();
  149. }
  150. return $this->_queries[$namespace];
  151. }
  152. return $this->_queries;
  153. }
  154. /**
  155. * Pops a query from the stack
  156. *
  157. * @return string $query
  158. */
  159. public function pop()
  160. {
  161. return array_pop($this->_queries);
  162. }
  163. /**
  164. * Removes all queries from the query stack
  165. *
  166. * @return void
  167. */
  168. public function reset()
  169. {
  170. $this->_queries = array();
  171. }
  172. /**
  173. * Count the number of queries on the stack
  174. *
  175. * @return integer the number of queries in the stack
  176. */
  177. public function count()
  178. {
  179. return count($this->_queries);
  180. }
  181. /**
  182. * Get queries iterator
  183. *
  184. * @return ArrayIterator an iterator that iterates through the query stack
  185. */
  186. public function getIterator()
  187. {
  188. return new ArrayIterator($this->_queries);
  189. }
  190. /**
  191. * Check whether or not the last cache opration was successful or not
  192. *
  193. * @return boolean whether or not the last cache operation was successful
  194. */
  195. public function isSuccessful()
  196. {
  197. return $this->_success;
  198. }
  199. /**
  200. * Delete all cache
  201. *
  202. * @return void
  203. */
  204. public function clean()
  205. {
  206. $rand = (mt_rand() / mt_getrandmax());
  207. if ($rand <= $this->_options['cleanPropability']) {
  208. $queries = $this->readStats();
  209. $stats = array();
  210. foreach ($queries as $query) {
  211. if (isset($stats[$query])) {
  212. $stats[$query]++;
  213. } else {
  214. $stats[$query] = 1;
  215. }
  216. }
  217. sort($stats);
  218. $i = $this->_options['size'];
  219. while ($i--) {
  220. $element = next($stats);
  221. $query = key($stats);
  222. $hash = md5($query);
  223. $this->_driver->delete($hash);
  224. }
  225. }
  226. }
  227. /**
  228. * Read stats file from disk
  229. *
  230. * @return array $stats
  231. */
  232. public function readStats()
  233. {
  234. if ($this->_options['statsFile'] !== false) {
  235. $content = file_get_contents($this->_options['statsFile']);
  236. $e = explode("\n", $content);
  237. return array_map('unserialize', $e);
  238. }
  239. return array();
  240. }
  241. /**
  242. * Append all queries to stats file
  243. * @return void
  244. */
  245. public function appendStats()
  246. {
  247. if ($this->_options['statsFile'] !== false) {
  248. if ( ! file_exists($this->_options['statsFile'])) {
  249. throw new Doctrine_Cache_Exception("Couldn't save cache statistics. Cache statistics file doesn't exists!");
  250. }
  251. $rand = (mt_rand() / mt_getrandmax());
  252. if ($rand <= $this->_options['addStatsPropability']) {
  253. file_put_contents($this->_options['statsFile'], implode("\n", array_map('serialize', $this->_queries)));
  254. }
  255. }
  256. }
  257. /**
  258. * Listens on the Doctrine_Event preQuery event
  259. *
  260. * adds the issued query to internal query stack
  261. * and checks if cached element exists
  262. *
  263. * @return boolean
  264. */
  265. public function preQuery(Doctrine_Event $event)
  266. {
  267. $query = $event->getQuery();
  268. $data = false;
  269. // only process SELECT statements
  270. if (strtoupper(substr(ltrim($query), 0, 6)) != 'SELECT') {
  271. return false;
  272. }
  273. $this->add($query, $event->getInvoker()->getName());
  274. $data = $this->_driver->fetch(md5(serialize($query)));
  275. $this->success = ($data) ? true : false;
  276. if ( ! $data) {
  277. $rand = (mt_rand() / mt_getrandmax());
  278. if ($rand < $this->_options['savePropability']) {
  279. $stmt = $event->getInvoker()->getAdapter()->query($query);
  280. $data = $stmt->fetchAll(Doctrine::FETCH_ASSOC);
  281. $this->success = true;
  282. $this->_driver->save(md5(serialize($query)), $data);
  283. }
  284. }
  285. if ($this->success)
  286. {
  287. $this->_data = $data;
  288. return true;
  289. }
  290. return false;
  291. }
  292. /**
  293. * Listens the preFetch event of Doctrine_Connection_Statement
  294. *
  295. * advances the internal pointer of cached data and returns
  296. * the current element
  297. *
  298. * @return array
  299. */
  300. public function preFetch(Doctrine_Event $event)
  301. {
  302. $ret = current($this->_data);
  303. next($this->_data);
  304. return $ret;
  305. }
  306. /**
  307. * Listens the preFetchAll event of Doctrine_Connection_Statement
  308. *
  309. * returns the current cache data array
  310. *
  311. * @return array
  312. */
  313. public function preFetchAll(Doctrine_Event $event)
  314. {
  315. return $this->_data;
  316. }
  317. /**
  318. * Listens the preExecute event of Doctrine_Connection_Statement
  319. *
  320. * adds the issued query to internal query stack
  321. * and checks if cached element exists
  322. *
  323. * @return boolean
  324. */
  325. public function preExecute(Doctrine_Event $event)
  326. {
  327. $query = $event->getQuery();
  328. $data = false;
  329. // only process SELECT statements
  330. if (strtoupper(substr(ltrim($query), 0, 6)) != 'SELECT') {
  331. return false;
  332. }
  333. $this->add($query, $event->getInvoker()->getDbh()->getName());
  334. $data = $this->_driver->fetch(md5(serialize(array($query, $event->getParams()))));
  335. $this->success = ($data) ? true : false;
  336. if ( ! $data) {
  337. $rand = (mt_rand() / mt_getrandmax());
  338. if ($rand <= $this->_options['savePropability']) {
  339. $stmt = $event->getInvoker()->getStatement();
  340. $stmt->execute($event->getParams());
  341. $data = $stmt->fetchAll(Doctrine::FETCH_ASSOC);
  342. $this->success = true;
  343. $this->_driver->save(md5(serialize(array($query, $event->getParams()))), $data);
  344. }
  345. }
  346. if ($this->success)
  347. {
  348. $this->_data = $data;
  349. return true;
  350. }
  351. return false;
  352. }
  353. }