Tiqr_StateStorage_Memcache::unsetValue()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 13
ccs 0
cts 10
cp 0
rs 9.9666
cc 3
nc 3
nop 1
crap 12
1
<?php 
2
/**
3
 * This file is part of the tiqr project.
4
 * 
5
 * The tiqr project aims to provide an open implementation for 
6
 * authentication using mobile devices. It was initiated by 
7
 * SURFnet and developed by Egeniq.
8
 *
9
 * More information: http://www.tiqr.org
10
 *
11
 * @author Ivo Jansch <[email protected]>
12
 * 
13
 * @package tiqr
14
 *
15
 * @license New BSD License - See LICENSE file for details.
16
 *
17
 * @copyright (C) 2010-2011 SURFnet BV
18
 */
19
20
21
/**
22
 * A StateStorage implementation using memcache to store state information.
23
 * 
24
 * When passing $options to the StateStorage::getStorage method, you can use
25
 * the following settings:
26
 * - "servers": An array of servers with "host" and "port" keys. If "port" is 
27
 *              ommited the default port is used.
28
 *              This option is optional; if servers is not specified, we will
29
 *              assume a memcache on localhost on the default memcache port.
30
 * - "prefix": If the memcache server is shared with other applications, a prefix
31
 *             can be used so that there are no key collisions with other 
32
 *             applications.
33
 * 
34
 * @author ivo
35
 */
36
class Tiqr_StateStorage_Memcache extends Tiqr_StateStorage_Abstract
37
{    
38
    /**
39
     * The memcache instance.
40
     * @var Memcache
41
     */
42
    protected $_memcache = NULL;
43
44
    /**
45
     * The flavor of memcache PHP extension we are using.
46
     * @var string
47
     */
48
    private static $extension = '';
49
50
    /**
51
     * The default configuration
52
     */
53
    const DEFAULT_HOST = '127.0.0.1';
54
    const DEFAULT_PORT =  11211;
55
    
56
    /**
57
     * Get the prefix to use for all keys in memcache.
58
     * @return String the prefix
59
     */
60
    protected function _getKeyPrefix()
61
    {
62
        if (isset($this->_options["prefix"])) {
63
            return $this->_options["prefix"];
64
        }
65
66
        $this->logger->info('No key prefix was configured, using "" as default memcache prefix.');
67
        return "";
68
    }
69
    
70
    /**
71
     * Initialize the statestorage by setting up the memcache instance.
72
     * It's not necessary to call this function, the Tiqr_StateStorage factory
73
     * will take care of that.
74
     *
75
     * @see Tiqr_StateStorage_StateStorageInterface::init()
76
     */
77
    public function init(): void
78
    {
79
        parent::init();
80
81
        $class = class_exists('\Memcache') ? '\Memcache' : (class_exists('\Memcached') ? '\Memcached' : false);
82
        if (!$class) {
83
            throw new RuntimeException('Memcache or Memcached class not found');
84
        }
85
        self::$extension = strtolower($class);
86
        
87
        $this->_memcache = new $class();
88
89
        if (!isset($this->_options["servers"])) {
90
            $this->logger->info('No memcache server was configured for StateStorage, using preconfigured defaults.');
91
            $this->_memcache->addServer(self::DEFAULT_HOST, self::DEFAULT_PORT);
92
        } else {
93
            foreach ($this->_options['servers'] as $server) {
94
                if (!array_key_exists('port', $server)) {
95
                    $server['port'] = self::DEFAULT_PORT;
96
                } 
97
                if (!array_key_exists('host', $server)) {
98
                    $server['host'] = self::DEFAULT_HOST;
99
                }  
100
             
101
                $this->_memcache->addServer($server['host'], $server['port']);
102
            }
103
        }          
104
    }
105
    
106
    /**
107
     * @see Tiqr_StateStorage_StateStorageInterface::setValue()
108
     */
109
    public function setValue(string $key, $value, int $expire=0): void
110
    {
111
        if (empty($key)) {
112
            throw new InvalidArgumentException('Empty key not allowed');
113
        }
114
115
        $key = $this->_getKeyPrefix().$key;
116
117
        if (self::$extension === '\memcached') {
118
            $result = $this->_memcache->set($key, $value, $expire);
119
        } else {
120
            $result = $this->_memcache->set($key, $value, 0, $expire);
121
        }
122
        if (!$result) {
123
            throw new ReadWriteException(sprintf('Unable to store key "%s" to Memcache StateStorage', $key));
124
        }
125
    }
126
    
127
    /**
128
     * @see Tiqr_StateStorage_StateStorageInterface::unsetValue()
129
     */
130
    public function unsetValue(string $key): void
131
    {
132
        if (empty($key)) {
133
            throw new InvalidArgumentException('Empty key not allowed');
134
        }
135
136
        $key = $this->_getKeyPrefix().$key;
137
        $result = $this->_memcache->delete($key);
138
        if (!$result) {
139
            throw new ReadWriteException(
140
                sprintf(
141
                    'Unable to delete key "%s" from state storage, key not found in Memcache StateStorage',
142
                    $key
143
                )
144
            );
145
        }
146
    }
147
    
148
    /**
149
     * @see Tiqr_StateStorage_StateStorageInterface::getValue()
150
     */
151
    public function getValue(string $key)
152
    {
153
        if (empty($key)) {
154
            throw new InvalidArgumentException('Empty key not allowed');
155
        }
156
157
        $key = $this->_getKeyPrefix().$key;
158
159
        $result = $this->_memcache->get($key);
160
        if ($result === false) {
161
            // Memcache interface does not provide error information, either the key does not exist or
162
            // there was an error communicating with the memcache
163
            $this->logger->info( sprintf('Unable to get key "%s" from memcache StateStorage', $key) );
164
            return null;
165
        }
166
        return $result;
167
    }
168
169
    /**
170
     * @see Tiqr_HealthCheck_Interface::healthCheck()
171
     */
172
    public function healthCheck(string &$statusMessage = ''): bool
173
    {
174
        try {
175
            // Generate a random key and use it to store a value in the memcache
176
            $key = bin2hex(random_bytes(16));
177
            $this->setValue($key, 'healthcheck', 10);
178
        } catch (Exception $e) {
179
            $statusMessage = 'Unable to store key in memcache: ' . $e->getMessage();
180
            return false;
181
        }
182
183
        return true;
184
    }
185
186
}
187