Completed
Push — 3.0 ( 5276e1...94e02b )
by Vermeulen
01:25
created

Memcached::obtainConnectedServerList()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
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_NO_SERVER_CONNECTED Exception code if no server is connected.
14
     */
15
    const ERR_NO_SERVER_CONNECTED = 1103001;
16
    
17
    /**
18
     * @const ERR_A_SERVER_IS_NOT_CONNECTED Exception code if a server is not
19
     * connected.
20
     */
21
    const ERR_A_SERVER_IS_NOT_CONNECTED = 1103002;
22
    
23
    /**
24
     * @const ERR_KEY_NOT_EXIST Exception code if the asked key not exist.
25
     * Actually only used into the method updateExpire().
26
     */
27
    const ERR_KEY_NOT_EXIST = 1103003;
28
29
    /**
30
     * @var array $config Config define into bfw config file for memcache(d)
31
     */
32
    protected $config;
33
34
    /**
35
     * Constructor.
36
     * Call parent constructor with the persistentId if declared in config
37
     * Connect to servers.
38
     */
39
    public function __construct()
40
    {
41
        $app          = \BFW\Application::getInstance();
42
        $this->config = $app->getConfig()->getValue(
43
            'memcached',
44
            'memcached.php'
45
        );
46
        
47
        if (!empty($this->config['persistentId'])) {
48
            parent::__construct($this->config['persistentId']);
49
        } else {
50
            parent::__construct();
51
        }
52
    }
53
    
54
    /**
55
     * Get accessor to the property config
56
     * 
57
     * @return array
58
     */
59
    public function getConfig(): array
60
    {
61
        return $this->config;
62
    }
63
64
    /**
65
     * Get the list of server already connected (persistent)
66
     * Loop on server declared into the config file.
67
     * Connect to server if not already connected
68
     * 
69
     * @return void
70
     */
71
    public function connectToServers()
72
    {
73
        //Array for the list of server(s) to connect
74
        $addServers  = [];
75
        
76
        //Get all server already connected (persistent)
77
        $serversList = $this->obtainConnectedServerList();
78
        
79
        //Loop on server declared into config
80
        foreach ($this->config['servers'] as $server) {
81
            $this->completeServerInfos($server);
82
            
83
            $host   = $server['host'];
84
            $port   = $server['port'];
85
            $weight = $server['weight'];
86
            
87
            //not check if port = (int) 0; Doc said to define to 0 for socket.
88
            if (empty($host) || $port === null) {
89
                continue;
90
            }
91
            
92
            //search if the reading server is not already connected
93
            if (in_array($host.':'.$port, $serversList)) {
94
                continue;
95
            }
96
            
97
            \BFW\Application::getInstance()
98
                ->getMonolog()
99
                ->getLogger()
100
                ->debug(
101
                    'Try to connect to memcached server.',
102
                    [
103
                        'host' => $host,
104
                        'port' => $port
105
                    ]
106
                );
107
            
108
            //If not, add the server at the list to connect
109
            $addServers[] = [$host, $port, $weight];
110
        }
111
112
        //Connect to server(s)
113
        $this->addServers($addServers);
114
        
115
        //Check if connect is successfull
116
        $this->testConnect();
117
    }
118
    
119
    /**
120
     * Get the list of servers where we are already connected (persistent)
121
     * 
122
     * @return string[]
123
     */
124
    protected function obtainConnectedServerList(): array
125
    {
126
        $serversList = $this->getServerList();
127
        $servers     = [];
128
        
129
        foreach ($serversList as $serverInfos) {
130
            $servers[] = $serverInfos['host'].':'.$serverInfos['port'];
131
        }
132
        
133
        return $servers;
134
    }
135
    /**
136
     * Read the server information and add not existing keys
137
     * 
138
     * @param array &$infos Server informations
139
     * 
140
     * @return void
141
     * 
142
     * @throw \Exception If informations datas is not an array
143
     */
144
    protected function completeServerInfos(array &$infos)
145
    {
146
        $infosKeyDefaultValues = [
147
            'host'   => null,
148
            'port'   => null,
149
            'weight' => 0
150
        ];
151
        
152
        foreach ($infosKeyDefaultValues as $infosKey => $defaultValue) {
153
            if (!isset($infos[$infosKey])) {
154
                $infos[$infosKey] = $defaultValue;
155
            }
156
        }
157
    }
158
    
159
    /**
160
     * addServer not created the connection. It's created at the first call
161
     * to the memcached servers.
162
     * 
163
     * So, we run the connect to all server declared
164
     * 
165
     * @throws \Exception If a server is not connected
166
     * 
167
     * @return boolean
168
     */
169
    protected function testConnect(): bool
170
    {
171
        $stats = $this->getStats();
172
        
173
        if (empty($stats)) {
174
            throw new Exception(
175
                'No memcached server connected.',
176
                self::ERR_NO_SERVER_CONNECTED
177
            );
178
        }
179
        
180
        foreach ($stats as $serverName => $serverStat) {
181
            if ($serverStat['uptime'] < 1) {
182
                throw new Exception(
183
                    'Memcached server '.$serverName.' not connected',
184
                    self::ERR_A_SERVER_IS_NOT_CONNECTED
185
                );
186
            }
187
        }
188
        
189
        return true;
190
    }
191
    
192
    /**
193
     * Check if a key exists into memcache(d)
194
     * /!\ Not work if the correct value is the boolean false /!\
195
     * 
196
     * @param string $key The memcache(d) key to check
197
     * 
198
     * @return boolean
199
     * 
200
     * @throws \Exception If the key is not a string
201
     */
202
    public function ifExists(string $key): bool
203
    {
204
        if ($this->get($key) === false) {
205
            return false;
206
        }
207
208
        return true;
209
    }
210
211
    /**
212
     * Update the expire time for a memcache(d) key.
213
     * 
214
     * @param string $key The memcache(d) key to update
215
     * @param int $expire The new expire time
216
     * 
217
     * @return boolean
218
     * 
219
     * @throws \Exception
220
     */
221
    public function updateExpire(string $key, int $expire): bool
222
    {
223
        if (!$this->ifExists($key)) {
224
            throw new Exception(
225
                'The key '.$key.' not exist on memcache(d) server',
226
                self::ERR_KEY_NOT_EXIST
227
            );
228
        }
229
        
230
        return $this->touch($key, $expire);
231
    }
232
}
233