~ubuntu-branches/ubuntu/utopic/moodle/utopic

« back to all changes in this revision

Viewing changes to lib/classes/session/memcached.php

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2014-05-12 16:10:38 UTC
  • mfrom: (36.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140512161038-puyqf65k4e0s8ytz
Tags: 2.6.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 memcached <= 2.1.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 = self::connection_string_to_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 <= 2.1.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
        $result = parent::start();
 
97
 
 
98
        set_time_limit($default);
 
99
        return $result;
 
100
    }
 
101
 
 
102
    /**
 
103
     * Init session handler.
 
104
     */
 
105
    public function init() {
 
106
        if (!extension_loaded('memcached')) {
 
107
            throw new exception('sessionhandlerproblem', 'error', '', null, 'memcached extension is not loaded');
 
108
        }
 
109
        $version = phpversion('memcached');
 
110
        if (!$version or version_compare($version, '2.0') < 0) {
 
111
            throw new exception('sessionhandlerproblem', 'error', '', null, 'memcached extension version must be at least 2.0');
 
112
        }
 
113
        if (empty($this->savepath)) {
 
114
            throw new exception('sessionhandlerproblem', 'error', '', null, '$CFG->session_memcached_save_path must be specified in config.php');
 
115
        }
 
116
 
 
117
        ini_set('session.save_handler', 'memcached');
 
118
        ini_set('session.save_path', $this->savepath);
 
119
        ini_set('memcached.sess_prefix', $this->prefix);
 
120
        ini_set('memcached.sess_locking', '1'); // Locking is required!
 
121
 
 
122
        // Try to configure lock and expire timeouts - ignored if memcached <=2.1.0.
 
123
        ini_set('memcached.sess_lock_max_wait', $this->acquiretimeout);
 
124
        ini_set('memcached.sess_lock_expire', $this->lockexpire);
 
125
    }
 
126
 
 
127
    /**
 
128
     * Check for existing session with id $sid.
 
129
     *
 
130
     * Note: this verifies the storage backend only, not the actual session records.
 
131
     *
 
132
     * @param string $sid
 
133
     * @return bool true if session found.
 
134
     */
 
135
    public function session_exists($sid) {
 
136
        if (!$this->servers) {
 
137
            return false;
 
138
        }
 
139
 
 
140
        $memcached = new \Memcached();
 
141
        $memcached->addServers($this->servers);
 
142
        $value = $memcached->get($this->prefix.$sid);
 
143
        $memcached->quit();
 
144
 
 
145
        return ($value !== false);
 
146
    }
 
147
 
 
148
    /**
 
149
     * Kill all active sessions, the core sessions table is
 
150
     * purged afterwards.
 
151
     */
 
152
    public function kill_all_sessions() {
 
153
        global $DB;
 
154
        if (!$this->servers) {
 
155
            return;
 
156
        }
 
157
 
 
158
        $memcached = new \Memcached();
 
159
        $memcached->addServers($this->servers);
 
160
 
 
161
        // Note: this can be significantly improved by fetching keys from memcached,
 
162
        //       but we need to make sure we are not deleting somebody else's sessions.
 
163
 
 
164
        $rs = $DB->get_recordset('sessions', array(), 'id DESC', 'id, sid');
 
165
        foreach ($rs as $record) {
 
166
            $memcached->delete($this->prefix.$record->sid);
 
167
        }
 
168
        $rs->close();
 
169
 
 
170
        $memcached->quit();
 
171
    }
 
172
 
 
173
    /**
 
174
     * Kill one session, the session record is removed afterwards.
 
175
     * @param string $sid
 
176
     */
 
177
    public function kill_session($sid) {
 
178
        if (!$this->servers) {
 
179
            return;
 
180
        }
 
181
 
 
182
        $memcached = new \Memcached();
 
183
        $memcached->addServers($this->servers);
 
184
        $memcached->delete($this->prefix.$sid);
 
185
 
 
186
        $memcached->quit();
 
187
    }
 
188
 
 
189
    /**
 
190
     * Convert a connection string to an array of servers
 
191
     *
 
192
     * EG: Converts: "abc:123, xyz:789" to
 
193
     *
 
194
     *  array(
 
195
     *      array('abc', '123'),
 
196
     *      array('xyz', '789'),
 
197
     *  )
 
198
     *
 
199
     * @copyright  2013 Moodlerooms Inc. (http://www.moodlerooms.com)
 
200
     * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
201
     * @author     Mark Nielsen
 
202
     *
 
203
     * @param string $str save_path value containing memcached connection string
 
204
     * @return array
 
205
     */
 
206
    protected static function connection_string_to_servers($str) {
 
207
        $servers = array();
 
208
        $parts   = explode(',', $str);
 
209
        foreach ($parts as $part) {
 
210
            $part = trim($part);
 
211
            $pos  = strrpos($part, ':');
 
212
            if ($pos !== false) {
 
213
                $host = substr($part, 0, $pos);
 
214
                $port = substr($part, ($pos + 1));
 
215
            } else {
 
216
                $host = $part;
 
217
                $port = 11211;
 
218
            }
 
219
            $servers[] = array($host, $port);
 
220
        }
 
221
        return $servers;
 
222
    }
 
223
}