session_handler_memcache.class.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Definition of the SessionHandlerMemcache class
  5. * @package chamilo.library
  6. */
  7. /**
  8. * Class SessionHandlerMemcache deals with volatile Memcache storage
  9. * and a more persistent but less frequent database backup storage
  10. * @todo This class might require a review to MySQL calls, depending on
  11. * when the session variables start to be saved
  12. */
  13. class SessionHandlerMemcache
  14. {
  15. public $connection;
  16. public $connection_handler;
  17. public $lifetime;
  18. public $session_name;
  19. public $memcache;
  20. public $initSessionData;
  21. public function __construct()
  22. {
  23. global $_configuration;
  24. $this->memcache = new Memcache;
  25. if (!empty($_configuration['memcache_server'])) {
  26. foreach ($_configuration['memcache_server'] as $serverData) {
  27. $isServerAvailable = @fsockopen($serverData['host'], $serverData['port']);
  28. if (!$isServerAvailable){
  29. continue;
  30. }
  31. $this->memcache->addServer($serverData['host'], $serverData['port']);
  32. }
  33. }
  34. $this->lifetime = 3600; // 60 minutes
  35. $this->connection = array (
  36. 'server' => $_configuration['db_host'],
  37. 'login' => $_configuration['db_user'],
  38. 'password' => $_configuration['db_password'],
  39. 'base' => $_configuration['main_database']
  40. );
  41. $this->connection_handler = false;
  42. }
  43. public function sqlConnect()
  44. {
  45. if (!$this->connection_handler) {
  46. $this->connection_handler = @mysql_connect($this->connection['server'], $this->connection['login'], $this->connection['password'], true);
  47. // The system has not been designed to use special SQL modes that were introduced since MySQL 5
  48. @mysql_query("set session sql_mode='';", $this->connection_handler);
  49. @mysql_select_db($this->connection['base'], $this->connection_handler);
  50. // Initialization of the database connection encoding to be used.
  51. // The internationalization library should be already initialized.
  52. @mysql_query("SET SESSION character_set_server='utf8';", $this->connection_handler);
  53. @mysql_query("SET SESSION collation_server='utf8_general_ci';", $this->connection_handler);
  54. $system_encoding = api_get_system_encoding();
  55. if (api_is_utf8($system_encoding)) {
  56. // See Bug #1802: For UTF-8 systems we prefer to use "SET NAMES 'utf8'" statement in order to avoid a bizarre problem with Chinese language.
  57. @mysql_query("SET NAMES 'utf8';", $this->connection_handler);
  58. } else {
  59. @mysql_query("SET CHARACTER SET '" . Database::to_db_encoding($system_encoding) . "';", $this->connection_handler);
  60. }
  61. }
  62. return ($this->connection_handler) ? true : false;
  63. }
  64. public function sqlClose()
  65. {
  66. if ($this->connection_handler) {
  67. mysql_close($this->connection_handler);
  68. $this->connection_handler = false;
  69. return true;
  70. }
  71. return false;
  72. }
  73. public function sqlQuery($query, $dieOnError = true)
  74. {
  75. $result = mysql_query($query, $this->connection_handler);
  76. if ($dieOnError && !$result) {
  77. $this->sqlClose();
  78. return;
  79. }
  80. return $result;
  81. }
  82. public function open($savePath, $sessionName)
  83. {
  84. $sessionID = session_id();
  85. if ($sessionID !== "") {
  86. $this->initSessionData = $this->read($sessionID);
  87. $this->session_name = $sessionName;
  88. }
  89. return true;
  90. }
  91. public function close()
  92. {
  93. $this->lifeTime = null;
  94. $this->memcache = null;
  95. $this->initSessionData = null;
  96. return $this->gc(0) ? true : false;
  97. }
  98. public function read($sessionID)
  99. {
  100. $data = $this->memcache->get($sessionID);
  101. if (($data === false || empty($data)) && $this->sqlConnect()) {
  102. $result = $this->sqlQuery("SELECT session_value FROM ".$this->connection['base'].".php_session WHERE session_id='$sessionID'");
  103. if (!empty($result) && $result !== false && $row = Database::fetch_row($result)) {
  104. $data = stripslashes($row[0]);
  105. $this->memcache->set($sessionID, $data);
  106. } else {
  107. $data = false;
  108. }
  109. } else {
  110. $data = stripslashes($data);
  111. }
  112. return $data;
  113. }
  114. public function write($sessionID, $data)
  115. {
  116. global $_configuration;
  117. $this->memcache->set($sessionID, $data);
  118. if ($this->memcache->get('interactions-' . $sessionID) !== false) {
  119. $interactions = $this->memcache->get('interactions-' . $sessionID);
  120. ++$interactions;
  121. if ($_configuration['session_stored_after_n_times'] < $interactions) {
  122. $interactions = 1;
  123. }
  124. $this->memcache->set('interactions-' . $sessionID, $interactions);
  125. } else {
  126. $this->memcache->set('interactions-' . $sessionID, 1);
  127. }
  128. $interactions = $this->memcache->get('interactions-' . $sessionID);
  129. //$this->initSessionData !== $data #avoid this validation for performance improvements
  130. if ($_configuration['session_stored_after_n_times'] === $interactions) {
  131. $sessionID = mysql_real_escape_string($sessionID);
  132. $sessionExpirationTS = ($this->lifetime + time());
  133. $sessionData = mysql_real_escape_string($data);
  134. if ($this->sqlConnect()) {
  135. $result = $this->sqlQuery("INSERT INTO ".$this->connection['base'].".php_session(
  136. session_id,session_name,session_time,session_start,session_value)
  137. VALUES('$sessionID','".$this->session_name."','$sessionExpirationTS','$sessionExpirationTS','".addslashes($sessionData)."')", false);
  138. if (!$result) {
  139. $this->sqlQuery("UPDATE ".$this->connection['base'].".php_session
  140. SET session_name='".$this->session_name."',session_time='$sessionExpirationTS',session_value='".addslashes($sessionData)."'
  141. WHERE session_id='$sessionID'");
  142. }
  143. return true;
  144. }
  145. }
  146. return false;
  147. }
  148. public function destroy($sessionID)
  149. {
  150. $this->memcache->delete($sessionID);
  151. if ($this->sqlConnect()) {
  152. $this->sqlQuery("DELETE FROM ".$this->connection['base'].".php_session WHERE session_id='$sessionID'");
  153. return true;
  154. }
  155. return false;
  156. }
  157. public function gc($maxlifetime)
  158. {
  159. if ($this->sqlConnect()) {
  160. $result = $this->sqlQuery("SELECT COUNT(session_id) FROM ".$this->connection['base'].".php_session");
  161. list($nbr_results) = Database::fetch_row($result);
  162. if ($nbr_results > 5000) {
  163. $this->sqlQuery("DELETE FROM ".$this->connection['base'].".php_session WHERE session_time<'".strtotime('-'.$this->lifetime.' minutes')."'");
  164. }
  165. $this->sqlClose();
  166. return true;
  167. }
  168. return false;
  169. }
  170. }