[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/classes/session/ -> memcached.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Memcached based session handler.
  19   *
  20   * @package    core
  21   * @copyright  2013 Petr Skoda {@link http://skodak.org}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core\session;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  /**
  30   * Memcached based session handler.
  31   *
  32   * @package    core
  33   * @copyright  2013 Petr Skoda {@link http://skodak.org}
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class memcached extends handler {
  37      /** @var string $savepath save_path string  */
  38      protected $savepath;
  39      /** @var array $servers list of servers parsed from save_path */
  40      protected $servers;
  41      /** @var string $prefix session key prefix  */
  42      protected $prefix;
  43      /** @var int $acquiretimeout how long to wait for session lock */
  44      protected $acquiretimeout = 120;
  45      /**
  46       * @var int $lockexpire how long to wait before expiring the lock so that other requests
  47       * may continue execution, ignored if PECL memcached is below version 2.2.0.
  48       */
  49      protected $lockexpire = 7200;
  50  
  51      /**
  52       * Create new instance of handler.
  53       */
  54      public function __construct() {
  55          global $CFG;
  56  
  57          if (empty($CFG->session_memcached_save_path)) {
  58              $this->savepath = '';
  59          } else {
  60              $this->savepath =  $CFG->session_memcached_save_path;
  61          }
  62  
  63          if (empty($this->savepath)) {
  64              $this->servers = array();
  65          } else {
  66              $this->servers = util::connection_string_to_memcache_servers($this->savepath);
  67          }
  68  
  69          if (empty($CFG->session_memcached_prefix)) {
  70              $this->prefix = ini_get('memcached.sess_prefix');
  71          } else {
  72              $this->prefix = $CFG->session_memcached_prefix;
  73          }
  74  
  75          if (!empty($CFG->session_memcached_acquire_lock_timeout)) {
  76              $this->acquiretimeout = (int)$CFG->session_memcached_acquire_lock_timeout;
  77          }
  78  
  79          if (!empty($CFG->session_memcached_lock_expire)) {
  80              $this->lockexpire = (int)$CFG->session_memcached_lock_expire;
  81          }
  82      }
  83  
  84      /**
  85       * Start the session.
  86       * @return bool success
  87       */
  88      public function start() {
  89          // NOTE: memcached before 2.2.0 expires session locks automatically after max_execution_time,
  90          //       this leads to major difference compared to other session drivers that timeout
  91          //       and stop the second request execution instead.
  92  
  93          $default = ini_get('max_execution_time');
  94          set_time_limit($this->acquiretimeout);
  95  
  96          $isnewsession = empty($_COOKIE[session_name()]);
  97          $starttimer = microtime(true);
  98  
  99          $result = parent::start();
 100  
 101          // If session_start returned TRUE, but it took as long
 102          // as the timeout value, and the $_SESSION returned is
 103          // empty when should not have been (isnewsession false)
 104          // then assume it did timeout and is invalid.
 105          // Add 1 second to elapsed time to account for inexact
 106          // timings in php_memcached_session.c.
 107          // @TODO Remove this check when php-memcached is fixed
 108          // to return false after key lock acquisition timeout.
 109          if (!$isnewsession && $result && count($_SESSION) == 0
 110              && (microtime(true) - $starttimer + 1) >= floatval($this->acquiretimeout)) {
 111              $result = false;
 112          }
 113  
 114          set_time_limit($default);
 115          return $result;
 116      }
 117  
 118      /**
 119       * Init session handler.
 120       */
 121      public function init() {
 122          if (!extension_loaded('memcached')) {
 123              throw new exception('sessionhandlerproblem', 'error', '', null, 'memcached extension is not loaded');
 124          }
 125          $version = phpversion('memcached');
 126          if (!$version or version_compare($version, '2.0') < 0) {
 127              throw new exception('sessionhandlerproblem', 'error', '', null, 'memcached extension version must be at least 2.0');
 128          }
 129          if (empty($this->savepath)) {
 130              throw new exception('sessionhandlerproblem', 'error', '', null, '$CFG->session_memcached_save_path must be specified in config.php');
 131          }
 132  
 133          ini_set('session.save_handler', 'memcached');
 134          ini_set('session.save_path', $this->savepath);
 135          ini_set('memcached.sess_prefix', $this->prefix);
 136          ini_set('memcached.sess_locking', '1'); // Locking is required!
 137  
 138          // Try to configure lock and expire timeouts - ignored if memcached is before version 2.2.0.
 139          if (version_compare($version, '3.0.0-dev') >= 0) {
 140              ini_set('memcached.sess_lock_wait_max', $this->acquiretimeout * 1000);
 141          } else {
 142              ini_set('memcached.sess_lock_max_wait', $this->acquiretimeout);
 143          }
 144  
 145          ini_set('memcached.sess_lock_expire', $this->lockexpire);
 146      }
 147  
 148      /**
 149       * Check the backend contains data for this session id.
 150       *
 151       * Note: this is intended to be called from manager::session_exists() only.
 152       *
 153       * @param string $sid
 154       * @return bool true if session found.
 155       */
 156      public function session_exists($sid) {
 157          if (!$this->servers) {
 158              return false;
 159          }
 160  
 161          // Go through the list of all servers because
 162          // we do not know where the session handler put the
 163          // data.
 164  
 165          foreach ($this->servers as $server) {
 166              list($host, $port) = $server;
 167              $memcached = new \Memcached();
 168              $memcached->addServer($host, $port);
 169              $value = $memcached->get($this->prefix . $sid);
 170              $memcached->quit();
 171              if ($value !== false) {
 172                  return true;
 173              }
 174          }
 175  
 176          return false;
 177      }
 178  
 179      /**
 180       * Kill all active sessions, the core sessions table is
 181       * purged afterwards.
 182       */
 183      public function kill_all_sessions() {
 184          global $DB;
 185          if (!$this->servers) {
 186              return;
 187          }
 188  
 189          // Go through the list of all servers because
 190          // we do not know where the session handler put the
 191          // data.
 192  
 193          $memcacheds = array();
 194          foreach ($this->servers as $server) {
 195              list($host, $port) = $server;
 196              $memcached = new \Memcached();
 197              $memcached->addServer($host, $port);
 198              $memcacheds[] = $memcached;
 199          }
 200  
 201          // Note: this can be significantly improved by fetching keys from memcached,
 202          //       but we need to make sure we are not deleting somebody else's sessions.
 203  
 204          $rs = $DB->get_recordset('sessions', array(), 'id DESC', 'id, sid');
 205          foreach ($rs as $record) {
 206              foreach ($memcacheds as $memcached) {
 207                  $memcached->delete($this->prefix . $record->sid);
 208              }
 209          }
 210          $rs->close();
 211  
 212          foreach ($memcacheds as $memcached) {
 213              $memcached->quit();
 214          }
 215      }
 216  
 217      /**
 218       * Kill one session, the session record is removed afterwards.
 219       * @param string $sid
 220       */
 221      public function kill_session($sid) {
 222          if (!$this->servers) {
 223              return;
 224          }
 225  
 226          // Go through the list of all servers because
 227          // we do not know where the session handler put the
 228          // data.
 229  
 230          foreach ($this->servers as $server) {
 231              list($host, $port) = $server;
 232              $memcached = new \Memcached();
 233              $memcached->addServer($host, $port);
 234              $memcached->delete($this->prefix . $sid);
 235              $memcached->quit();
 236          }
 237      }
 238  
 239  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1