Completed
Push — 3.0 ( c29f0e...fdb42e )
by Vermeulen
01:43
created

Memcached   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 281
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
wmc 24
lcom 2
cbo 2
dl 0
loc 281
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 2
A getConfig() 0 4 1
B connectToServers() 0 47 5
A generateServerList() 0 11 2
A completeServerInfos() 0 23 4
A testConnect() 0 22 4
A ifExists() 0 23 3
A updateExpire() 0 27 3
1
<?php
2
3
namespace BFW;
4
5
use \Exception;
6
7
/**
8
 * Class to manage connection to memcache(d) server with memcached lib
9
 */
10
class Memcached extends \Memcached
11
{
12
    /**
13
     * @const ERR_SERVER_INFOS_FORMAT Exception code if server informations is
14
     * not in a correct format.
15
     */
16
    const ERR_SERVER_INFOS_FORMAT = 1309001;
17
    
18
    /**
19
     * @const ERR_NO_SERVER_CONNECTED Exception code if no server is connected.
20
     */
21
    const ERR_NO_SERVER_CONNECTED = 1309002;
22
    
23
    /**
24
     * @const ERR_A_SERVER_IS_NOT_CONNECTED Exception code if a server is not
25
     * connected.
26
     */
27
    const ERR_A_SERVER_IS_NOT_CONNECTED = 1309003;
28
    
29
    /**
30
     * @const ERR_IFEXISTS_PARAM_TYPE Exception code if a parameter type is not
31
     * correct into the method ifExists().
32
     */
33
    const ERR_IFEXISTS_PARAM_TYPE = 1309004;
34
    
35
    /**
36
     * @const ERR_UPDATEEXPIRE_PARAM_TYPE Exception code if a parameter type
37
     * is not correct into the method updateExpire().
38
     */
39
    const ERR_UPDATEEXPIRE_PARAM_TYPE = 1309005;
40
    
41
    /**
42
     * @const ERR_KEY_NOT_EXIST Exception code if the asked key not exist.
43
     * Actually only used into the method updateExpire().
44
     */
45
    const ERR_KEY_NOT_EXIST = 1309006;
46
47
    /**
48
     * @var array $config Config define into bfw config file for memcache(d)
49
     */
50
    protected $config;
51
52
    /**
53
     * Constructor.
54
     * Call parent constructor with the persistentId if declared in config
55
     * Connect to servers.
56
     */
57
    public function __construct()
58
    {
59
        $app          = \BFW\Application::getInstance();
60
        $this->config = $app->getConfig()->getValue(
0 ignored issues
show
Documentation Bug introduced by
The method getConfig does not exist on object<BFW\Application>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
61
            'memcached',
62
            'memcached.php'
63
        );
64
        
65
        if (!empty($this->config['persistentId'])) {
66
            parent::__construct($this->config['persistentId']);
67
        } else {
68
            parent::__construct();
69
        }
70
    }
71
    
72
    /**
73
     * Get accessor to the property config
74
     * 
75
     * @return array
76
     */
77
    public function getConfig()
78
    {
79
        return $this->config;
80
    }
81
82
    /**
83
     * Get the list of server already connected (persistent)
84
     * Loop on server declared into the config file.
85
     * Connect to server if not already connected
86
     * 
87
     * @return void
88
     */
89
    public function connectToServers()
90
    {
91
        //Array for the list of server(s) to connect
92
        $addServers  = [];
93
        
94
        //Get all server already connected (persistent)
95
        $serversList = $this->generateServerList();
96
        
97
        //Loop on server declared into config
98
        foreach ($this->config['servers'] as $server) {
99
            $this->completeServerInfos($server);
100
            
101
            $host   = $server['host'];
102
            $port   = $server['port'];
103
            $weight = $server['weight'];
104
            
105
            //not check if port = (int) 0; Doc said to define to 0 for socket.
106
            if (empty($host) || $port === null) {
107
                continue;
108
            }
109
            
110
            //search if the reading server is not already connected
111
            if (in_array($host.':'.$port, $serversList)) {
112
                continue;
113
            }
114
            
115
            \BFW\Application::getInstance()
0 ignored issues
show
Documentation Bug introduced by
The method getMonolog does not exist on object<BFW\Application>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
116
                ->getMonolog()
117
                ->getLogger()
118
                ->debug(
119
                    'Try to connect to memcached server.',
120
                    [
121
                        'host' => $host,
122
                        'port' => $port
123
                    ]
124
                );
125
            
126
            //If not, add the server at the list to connect
127
            $addServers[] = [$host, $port, $weight];
128
        }
129
130
        //Connect to server(s)
131
        $this->addServers($addServers);
132
        
133
        //Check if connect is successfull
134
        $this->testConnect();
135
    }
136
    
137
    /**
138
     * Get the list of servers where we are already connected (persistent)
139
     * 
140
     * @return string[]
141
     */
142
    protected function generateServerList()
143
    {
144
        $serversList = $this->getServerList();
145
        $servers     = [];
146
        
147
        foreach ($serversList as $serverInfos) {
148
            $servers[] = $serverInfos['host'].':'.$serverInfos['port'];
149
        }
150
        
151
        return $servers;
152
    }
153
    /**
154
     * Read the server information and add not existing keys
155
     * 
156
     * @param array &$infos Server informations
157
     * 
158
     * @return void
159
     * 
160
     * @throw \Exception If informations datas is not an array
161
     */
162
    protected function completeServerInfos(&$infos)
163
    {
164
        if (!is_array($infos)) {
165
            throw new Exception(
166
                'Memcache(d) server information is not an array.',
167
                self::ERR_SERVER_INFOS_FORMAT
168
            );
169
        }
170
        
171
        $infosKeyDefaultValues = [
172
            'host'       => null,
173
            'port'       => null,
174
            'weight'     => 0,
175
            'timeout'    => null,
176
            'persistent' => false
177
        ];
178
        
179
        foreach ($infosKeyDefaultValues as $infosKey => $defaultValue) {
180
            if (!isset($infos[$infosKey])) {
181
                $infos[$infosKey] = $defaultValue;
182
            }
183
        }
184
    }
185
    
186
    /**
187
     * addServer not created the connection. It's created at the first call
188
     * to the memcached servers.
189
     * 
190
     * So, we run the connect to all server declared
191
     * 
192
     * @throws \Exception If a server is not connected
193
     * 
194
     * @return boolean
195
     */
196
    protected function testConnect()
197
    {
198
        $stats = $this->getStats();
199
        
200
        if (!is_array($stats)) {
201
            throw new Exception(
202
                'No memcached server connected.',
203
                self::ERR_NO_SERVER_CONNECTED
204
            );
205
        }
206
        
207
        foreach ($stats as $serverName => $serverStat) {
208
            if ($serverStat['uptime'] < 1) {
209
                throw new Exception(
210
                    'Memcached server '.$serverName.' not connected',
211
                    self::ERR_A_SERVER_IS_NOT_CONNECTED
212
                );
213
            }
214
        }
215
        
216
        return true;
217
    }
218
    
219
    /**
220
     * Check if a key exists into memcache(d)
221
     * /!\ Not work if the correct value is the boolean false /!\
222
     * 
223
     * @param string $key The memcache(d) key to check
224
     * 
225
     * @return boolean
226
     * 
227
     * @throws Exception If the key is not a string
228
     */
229
    public function ifExists($key)
230
    {
231
        $verifParams = \BFW\Helpers\Datas::checkType([
232
            [
233
                'type' => 'string',
234
                'data' => $key
235
            ]
236
        ]);
237
        
238
        if (!$verifParams) {
239
            throw new Exception(
240
                'The $key parameters must be a string.'
241
                .' Currently the value is a/an '.gettype($key),
242
                self::ERR_IFEXISTS_PARAM_TYPE
243
            );
244
        }
245
246
        if ($this->get($key) === false) {
247
            return false;
248
        }
249
250
        return true;
251
    }
252
253
    /**
254
     * Update the expire time for a memcache(d) key.
255
     * 
256
     * @param string $key The memcache(d) key to update
257
     * @param int $expire The new expire time
258
     * 
259
     * @return boolean
260
     * 
261
     * @throws Exception
262
     */
263
    public function updateExpire($key, $expire)
264
    {
265
        $verifParams = \BFW\Helpers\Datas::checkType([
266
            ['type' => 'string', 'data' => $key],
267
            ['type' => 'int', 'data' => $expire]
268
        ]);
269
270
        if (!$verifParams) {
271
            throw new Exception(
272
                'Once of parameters $key or $expire not have a correct type.',
273
                self::ERR_UPDATEEXPIRE_PARAM_TYPE
274
            );
275
        }
276
        
277
        if (!$this->ifExists($key)) {
278
            throw new Exception(
279
                'The key '.$key.' not exist on memcache(d) server',
280
                self::ERR_KEY_NOT_EXIST
281
            );
282
        }
283
284
        //To change expire time, we need to re-set the value.
285
        $value = $this->get($key); //Get the value
286
        
287
        //Re-set the value with new expire time.
288
        return $this->replace($key, $value, $expire); //We can use touch()
289
    }
290
}
291