GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

MemcacheHandler::close()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
c 1
b 0
f 0
nc 7
nop 0
dl 0
loc 17
rs 9.6111
1
<?php
2
/**
3
 * This file is part of the O2System Framework package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author         Steeve Andrian Salim
9
 * @copyright      Copyright (c) Steeve Andrian Salim
10
 */
11
12
// ------------------------------------------------------------------------
13
14
namespace O2System\Session\Handlers;
15
16
// ------------------------------------------------------------------------
17
18
use Psr\Log\LoggerInterface;
19
use O2System\Session\Abstracts\AbstractHandler;
20
use O2System\Session\DataStructures\Config;
21
22
/**
23
 * Class MemcachedHandler
24
 *
25
 * @package O2System\Session\Handlers
26
 */
27
class MemcacheHandler extends AbstractHandler
28
{
29
    /**
30
     * Platform Name
31
     *
32
     * @access  protected
33
     * @var string
34
     */
35
    protected $platform = 'memcache';
36
37
    /**
38
     * Memcached Object
39
     *
40
     * @var \Memcache
41
     */
42
    protected $memcache;
43
44
    // ------------------------------------------------------------------------
45
46
    /**
47
     * MemcachedHandler::__construct
48
     *
49
     * @param Config $config
50
     */
51
    public function __construct(Config $config)
52
    {
53
        $config->merge(
54
            [
55
                'host'   => '127.0.0.1',
56
                'port'   => 11211,
57
                'weight' => 1,
58
            ]
59
        );
60
61
        parent::__construct($config);
62
    }
63
64
    /**
65
     * MemcachedHandler::open
66
     *
67
     * Initialize session
68
     *
69
     * @link  http://php.net/manual/en/sessionhandlerinterface.open.php
70
     *
71
     * @param string $save_path The path where to store/retrieve the session.
72
     * @param string $name      The session name.
73
     *
74
     * @return bool <p>
75
     * The return value (usually TRUE on success, FALSE on failure).
76
     * Note this value is returned internally to PHP for processing.
77
     * </p>
78
     * @since 5.4.0
79
     */
80
    public function open($save_path, $name)
81
    {
82
        if (class_exists('Memcache', false)) {
83
            $this->memcache = new \Memcache();
84
        } else {
85
            if ($this->logger instanceof LoggerInterface) {
0 ignored issues
show
introduced by
$this->logger is always a sub-type of Psr\Log\LoggerInterface.
Loading history...
86
                $this->logger->error('SESSION_E_PLATFORM_UNSUPPORTED', ['Memcache']);
87
            }
88
89
            return false;
90
        }
91
92
        if (isset($this->config[ 'servers' ])) {
93
            foreach ($this->config[ 'servers' ] as $server => $setup) {
94
                isset($setup[ 'port' ]) OR $setup[ 'port' ] = 11211;
95
                isset($setup[ 'weight' ]) OR $setup[ 'weight' ] = 1;
96
97
                // Third parameter is persistance and defaults to TRUE.
98
                $this->memcache->addServer(
99
                    $setup[ 'host' ],
100
                    $setup[ 'port' ],
101
                    true,
102
                    $setup[ 'weight' ]
103
                );
104
            }
105
        } else {
106
            // Third parameter is persistance and defaults to TRUE.
107
            $this->memcache->addServer(
108
                $this->config[ 'host' ],
109
                $this->config[ 'port' ],
110
                true,
111
                $this->config[ 'weight' ]
112
            );
113
        }
114
115
        if ($this->memcache->getVersion() === false) {
116
            if ($this->logger instanceof LoggerInterface) {
0 ignored issues
show
introduced by
$this->logger is always a sub-type of Psr\Log\LoggerInterface.
Loading history...
117
                $this->logger->error('SESSION_E_MEMCACHED_CONNECTION_REFUSED');
118
            }
119
120
            return false;
121
        }
122
123
        return true;
124
    }
125
126
    // ------------------------------------------------------------------------
127
128
    /**
129
     * MemcachedHandler::close
130
     *
131
     * Close the session
132
     *
133
     * @link  http://php.net/manual/en/sessionhandlerinterface.close.php
134
     * @return bool <p>
135
     *        The return value (usually TRUE on success, FALSE on failure).
136
     *        Note this value is returned internally to PHP for processing.
137
     *        </p>
138
     * @since 5.4.0
139
     */
140
    public function close()
141
    {
142
        if (isset($this->memcache)) {
143
            isset($this->lockKey) AND $this->memcache->delete($this->lockKey);
144
145
            if ($this->memcache instanceof \Memcached) {
0 ignored issues
show
introduced by
$this->memcache is never a sub-type of Memcached.
Loading history...
146
                $this->memcache->quit();
147
            } elseif ($this->memcache instanceof \MemcachePool) {
0 ignored issues
show
introduced by
$this->memcache is always a sub-type of MemcachePool.
Loading history...
148
                $this->memcache->close();
149
            }
150
151
            $this->memcache = null;
152
153
            return true;
154
        }
155
156
        return false;
157
    }
158
159
    // ------------------------------------------------------------------------
160
161
    /**
162
     * MemcachedHandler::destroy
163
     *
164
     * Destroy a session
165
     *
166
     * @link  http://php.net/manual/en/sessionhandlerinterface.destroy.php
167
     *
168
     * @param string $session_id The session ID being destroyed.
169
     *
170
     * @return bool <p>
171
     * The return value (usually TRUE on success, FALSE on failure).
172
     * Note this value is returned internally to PHP for processing.
173
     * </p>
174
     * @since 5.4.0
175
     */
176
    public function destroy($session_id)
177
    {
178
        if (isset($this->memcache, $this->lockKey)) {
179
            $this->memcache->delete($this->prefixKey . $session_id);
180
181
            return $this->destroyCookie();
182
        }
183
184
        return false;
185
    }
186
187
    // ------------------------------------------------------------------------
188
189
    /**
190
     * MemcachedHandler::gc
191
     *
192
     * Cleanup old sessions
193
     *
194
     * @link  http://php.net/manual/en/sessionhandlerinterface.gc.php
195
     *
196
     * @param int $maxlifetime <p>
197
     *                         Sessions that have not updated for
198
     *                         the last maxlifetime seconds will be removed.
199
     *                         </p>
200
     *
201
     * @return bool <p>
202
     * The return value (usually TRUE on success, FALSE on failure).
203
     * Note this value is returned internally to PHP for processing.
204
     * </p>
205
     * @since 5.4.0
206
     */
207
    public function gc($maxlifetime)
208
    {
209
        // Not necessary, Memcached takes care of that.
210
        return true;
211
    }
212
213
    // ------------------------------------------------------------------------
214
215
    /**
216
     * MemcachedHandler::read
217
     *
218
     * Read session data
219
     *
220
     * @link  http://php.net/manual/en/sessionhandlerinterface.read.php
221
     *
222
     * @param string $session_id The session id to read data for.
223
     *
224
     * @return string <p>
225
     * Returns an encoded string of the read data.
226
     * If nothing was read, it must return an empty string.
227
     * Note this value is returned internally to PHP for processing.
228
     * </p>
229
     * @since 5.4.0
230
     */
231
    public function read($session_id)
232
    {
233
        if (isset($this->memcache) && $this->lockSession($session_id)) {
234
            // Needed by write() to detect session_regenerate_id() calls
235
            $this->sessionId = $session_id;
236
237
            $sessionData = (string)$this->memcache->get($this->prefixKey . $session_id);
238
            $this->fingerprint = md5($sessionData);
0 ignored issues
show
Documentation Bug introduced by
The property $fingerprint was declared of type boolean, but md5($sessionData) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
239
240
            return $sessionData;
241
        }
242
243
        return '';
244
    }
245
246
    // ------------------------------------------------------------------------
247
248
    /**
249
     * MemcachedHandler::_lockSession
250
     *
251
     * Acquires an (emulated) lock.
252
     *
253
     * @param   string $session_id Session ID
254
     *
255
     * @return  bool
256
     */
257
    protected function lockSession($session_id)
258
    {
259
        if (isset($this->lockKey)) {
260
            return $this->memcache->replace($this->lockKey, time(), false, 300);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $flag of MemcachePool::replace(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

260
            return $this->memcache->replace($this->lockKey, time(), /** @scrutinizer ignore-type */ false, 300);
Loading history...
261
        }
262
263
        // 30 attempts to obtain a lock, in case another request already has it
264
        $lockKey = $this->prefixKey . $session_id . ':lock';
265
        $attempt = 0;
266
267
        do {
268
            if ($this->memcache->get($lockKey)) {
269
                sleep(1);
270
                continue;
271
            }
272
273
            if ( ! @$this->memcache->set($lockKey, time(), false, 300)) {
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $flag of MemcachePool::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

273
            if ( ! @$this->memcache->set($lockKey, time(), /** @scrutinizer ignore-type */ false, 300)) {
Loading history...
274
                if ($this->logger instanceof LoggerInterface) {
275
                    $this->logger->error('SESSION_E_OBTAIN_LOCK', [$this->prefixKey . $session_id]);
276
                }
277
278
                return false;
279
            }
280
281
            $this->lockKey = $lockKey;
282
            break;
283
        } while (++$attempt < 30);
284
285
        if ($attempt === 30) {
286
            if ($this->logger instanceof LoggerInterface) {
0 ignored issues
show
introduced by
$this->logger is always a sub-type of Psr\Log\LoggerInterface.
Loading history...
287
                $this->logger->error('SESSION_E_OBTAIN_LOCK_30', [$this->prefixKey . $session_id]);
288
            }
289
290
            return false;
291
        }
292
293
        $this->isLocked = true;
294
295
        return true;
296
    }
297
298
    // ------------------------------------------------------------------------
299
300
    /**
301
     * MemcachedHandler::write
302
     *
303
     * Write session data
304
     *
305
     * @link  http://php.net/manual/en/sessionhandlerinterface.write.php
306
     *
307
     * @param string $session_id   The session id.
308
     * @param string $session_data <p>
309
     *                             The encoded session data. This data is the
310
     *                             result of the PHP internally encoding
311
     *                             the $_SESSION superglobal to a serialized
312
     *                             string and passing it as this parameter.
313
     *                             Please note sessions use an alternative serialization method.
314
     *                             </p>
315
     *
316
     * @return bool <p>
317
     * The return value (usually TRUE on success, FALSE on failure).
318
     * Note this value is returned internally to PHP for processing.
319
     * </p>
320
     * @since 5.4.0
321
     */
322
    public function write($session_id, $session_data)
323
    {
324
        if ( ! isset($this->memcache)) {
325
            return false;
326
        } // Was the ID regenerated?
327
        elseif ($session_id !== $this->sessionId) {
328
            if ( ! $this->lockRelease() OR ! $this->lockSession($session_id)) {
329
                return false;
330
            }
331
332
            $this->fingerprint = md5('');
0 ignored issues
show
Documentation Bug introduced by
The property $fingerprint was declared of type boolean, but md5('') is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
333
            $this->sessionId = $session_id;
334
        }
335
336
        if (isset($this->lockKey)) {
337
            $this->memcache->replace($this->lockKey, time(), false, 300);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $flag of MemcachePool::replace(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

337
            $this->memcache->replace($this->lockKey, time(), /** @scrutinizer ignore-type */ false, 300);
Loading history...
338
339
            if ($this->fingerprint !== ($fingerprint = md5($session_data))) {
340
                if ($this->memcache->set(
341
                    $this->prefixKey . $session_id,
342
                    $session_data,
343
                    false,
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $flag of MemcachePool::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

343
                    /** @scrutinizer ignore-type */ false,
Loading history...
344
                    $this->config[ 'lifetime' ]
345
                )
346
                ) {
347
                    $this->fingerprint = $fingerprint;
348
349
                    return true;
350
                }
351
352
                return false;
353
            }
354
355
            return $this->memcache->replace($this->prefixKey . $session_id, $session_data, false,
356
                $this->config[ 'lifetime' ]);
357
        }
358
359
        return false;
360
    }
361
362
    //--------------------------------------------------------------------
363
364
    /**
365
     * MemcachedHandler::_lockRelease
366
     *
367
     * Releases a previously acquired lock
368
     *
369
     * @return    bool
370
     */
371
    protected function lockRelease()
372
    {
373
        if (isset($this->memcache, $this->lockKey) && $this->isLocked) {
374
            if ( ! $this->memcache->delete($this->lockKey)) {
375
                if ($this->logger instanceof LoggerInterface) {
0 ignored issues
show
introduced by
$this->logger is always a sub-type of Psr\Log\LoggerInterface.
Loading history...
376
                    $this->logger->error('SESSION_E_FREE_LOCK', [$this->lockKey]);
377
                }
378
379
                return false;
380
            }
381
382
            $this->lockKey = null;
383
            $this->isLocked = false;
384
        }
385
386
        return true;
387
    }
388
389
    //--------------------------------------------------------------------
390
391
    /**
392
     * MemcachedHandler::isSupported
393
     *
394
     * Checks if this platform is supported on this system.
395
     *
396
     * @return bool Returns FALSE if unsupported.
397
     */
398
    public function isSupported()
399
    {
400
        return (bool)extension_loaded('memcache');
401
    }
402
}