ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Memcache.php
Go to the documentation of this file.
1<?php
2
3
21{
22
28 private static $serverGroups = null;
29
30
36 private static $extension = '';
37
38
46 public static function get($key)
47 {
48 SimpleSAML\Logger::debug("loading key $key from memcache");
49
50 $latestInfo = null;
51 $latestTime = 0.0;
52 $latestData = null;
53 $mustUpdate = false;
54 $allDown = true;
55
56 // search all the servers for the given id
57 foreach (self::getMemcacheServers() as $server) {
58 $serializedInfo = $server->get($key);
59 if ($serializedInfo === false) {
60 // either the server is down, or we don't have the value stored on that server
61 $mustUpdate = true;
62 $up = $server->getstats();
63 if ($up !== false) {
64 $allDown = false;
65 }
66 continue;
67 }
68 $allDown = false;
69
70 // unserialize the object
71 $info = unserialize($serializedInfo);
72
73 /*
74 * Make sure that this is an array with two keys:
75 * - 'timestamp': The time the data was saved.
76 * - 'data': The data.
77 */
78 if (!is_array($info)) {
80 'Retrieved invalid data from a memcache server. Data was not an array.'
81 );
82 continue;
83 }
84 if (!array_key_exists('timestamp', $info)) {
86 'Retrieved invalid data from a memcache server. Missing timestamp.'
87 );
88 continue;
89 }
90 if (!array_key_exists('data', $info)) {
92 'Retrieved invalid data from a memcache server. Missing data.'
93 );
94 continue;
95 }
96
97 if ($latestInfo === null) {
98 // first info found
99 $latestInfo = $serializedInfo;
100 $latestTime = $info['timestamp'];
101 $latestData = $info['data'];
102 continue;
103 }
104
105 if ($info['timestamp'] === $latestTime && $serializedInfo === $latestInfo) {
106 // this data matches the data from the other server(s)
107 continue;
108 }
109
110 // different data from different servers. We need to update at least one of them to maintain sync
111 $mustUpdate = true;
112
113 // update if data in $info is newer than $latestData
114 if ($latestTime < $info['timestamp']) {
115 $latestInfo = $serializedInfo;
116 $latestTime = $info['timestamp'];
117 $latestData = $info['data'];
118 }
119 }
120
121 if ($latestData === null) {
122 if ($allDown) {
123 // all servers are down, panic!
124 $e = new SimpleSAML_Error_Error('MEMCACHEDOWN', null, 503);
125 throw new SimpleSAML_Error_Exception('All memcache servers are down', 503, $e);
126 }
127 // we didn't find any data matching the key
128 SimpleSAML\Logger::debug("key $key not found in memcache");
129 return null;
130 }
131
132 if ($mustUpdate) {
133 // we found data matching the key, but some of the servers need updating
134 SimpleSAML\Logger::debug("Memcache servers out of sync for $key, forcing sync");
135 self::set($key, $latestData);
136 }
137
138 return $latestData;
139 }
140
141
149 public static function set($key, $value, $expire = null)
150 {
151 SimpleSAML\Logger::debug("saving key $key to memcache");
152 $savedInfo = array(
153 'timestamp' => microtime(true),
154 'data' => $value
155 );
156
157 if ($expire === null) {
159 }
160
161 $savedInfoSerialized = serialize($savedInfo);
162
163 // store this object to all groups of memcache servers
164 foreach (self::getMemcacheServers() as $server) {
165 if (self::$extension === 'memcached') {
166 $server->set($key, $savedInfoSerialized, $expire);
167 } else {
168 $server->set($key, $savedInfoSerialized, 0, $expire);
169 }
170 }
171 }
172
173
179 public static function delete($key)
180 {
181 assert(is_string($key));
182 SimpleSAML\Logger::debug("deleting key $key from memcache");
183
184 // store this object to all groups of memcache servers
185 foreach (self::getMemcacheServers() as $server) {
186 $server->delete($key);
187 }
188 }
189
190
214 private static function addMemcacheServer($memcache, $server)
215 {
216 // the hostname option is required
217 if (!array_key_exists('hostname', $server)) {
218 throw new Exception(
219 "hostname setting missing from server in the 'memcache_store.servers' configuration option."
220 );
221 }
222
223 $hostname = $server['hostname'];
224
225 // the hostname must be a valid string
226 if (!is_string($hostname)) {
227 throw new Exception(
228 "Invalid hostname for server in the 'memcache_store.servers' configuration option. The hostname is".
229 ' supposed to be a string.'
230 );
231 }
232
233 // check if we are told to use a socket
234 $socket = false;
235 if (strpos($hostname, 'unix:///') === 0) {
236 $socket = true;
237 }
238
239 // check if the user has specified a port number
240 if ($socket) {
241 // force port to be 0 for sockets
242 $port = 0;
243 } elseif (array_key_exists('port', $server)) {
244 // get the port number from the array, and validate it
245 $port = (int) $server['port'];
246 if (($port <= 0) || ($port > 65535)) {
247 throw new Exception(
248 "Invalid port for server in the 'memcache_store.servers' configuration option. The port number".
249 ' is supposed to be an integer between 0 and 65535.'
250 );
251 }
252 } else {
253 // use the default port number from the ini-file
254 $port = (int) ini_get('memcache.default_port');
255 if ($port <= 0 || $port > 65535) {
256 // invalid port number from the ini-file. fall back to the default
257 $port = 11211;
258 }
259 }
260
261 // check if the user has specified a weight for this server
262 if (array_key_exists('weight', $server)) {
263 // get the weight and validate it
264 $weight = (int) $server['weight'];
265 if ($weight <= 0) {
266 throw new Exception(
267 "Invalid weight for server in the 'memcache_store.servers' configuration option. The weight is".
268 ' supposed to be a positive integer.'
269 );
270 }
271 } else {
272 // use a default weight of 1
273 $weight = 1;
274 }
275
276 // check if the user has specified a timeout for this server
277 if (array_key_exists('timeout', $server)) {
278 // get the timeout and validate it
279 $timeout = (int) $server['timeout'];
280 if ($timeout <= 0) {
281 throw new Exception(
282 "Invalid timeout for server in the 'memcache_store.servers' configuration option. The timeout is".
283 ' supposed to be a positive integer.'
284 );
285 }
286 } else {
287 // use a default timeout of 3 seconds
288 $timeout = 3;
289 }
290
291 // add this server to the Memcache object
292 if (self::$extension === 'memcached') {
293 $memcache->addServer($hostname, $port);
294 } else {
295 $memcache->addServer($hostname, $port, true, $weight, $timeout, $timeout, true);
296 }
297 }
298
299
310 private static function loadMemcacheServerGroup(array $group)
311 {
312 $class = class_exists('Memcache') ? 'Memcache' : (class_exists('Memcached') ? 'Memcached' : false);
313 if (!$class) {
314 throw new Exception('Missing Memcached implementation. You must install either the Memcache or Memcached extension.');
315 }
316 self::$extension = strtolower($class);
317
318 // create the Memcache object
319 $memcache = new $class();
320
321 // iterate over all the servers in the group and add them to the Memcache object
322 foreach ($group as $index => $server) {
323 // make sure that we don't have an index. An index would be a sign of invalid configuration
324 if (!is_int($index)) {
325 throw new Exception(
326 "Invalid index on element in the 'memcache_store.servers' configuration option. Perhaps you".
327 ' have forgotten to add an array(...) around one of the server groups? The invalid index was: '.
328 $index
329 );
330 }
331
332 // make sure that the server object is an array. Each server is an array with name-value pairs
333 if (!is_array($server)) {
334 throw new Exception(
335 'Invalid value for the server with index '.$index.
336 '. Remeber that the \'memcache_store.servers\' configuration option'.
337 ' contains an array of arrays of arrays.'
338 );
339 }
340
342 }
343
344 return $memcache;
345 }
346
347
356 private static function getMemcacheServers()
357 {
358 // check if we have loaded the servers already
359 if (self::$serverGroups != null) {
360 return self::$serverGroups;
361 }
362
363 // initialize the servers-array
364 self::$serverGroups = array();
365
366 // load the configuration
368
369
370 $groups = $config->getArray('memcache_store.servers');
371
372 // iterate over all the groups in the 'memcache_store.servers' configuration option
373 foreach ($groups as $index => $group) {
374 // make sure that the group doesn't have an index. An index would be a sign of invalid configuration
375 if (!is_int($index)) {
376 throw new Exception(
377 "Invalid index on element in the 'memcache_store.servers'".
378 ' configuration option. Perhaps you have forgotten to add an array(...)'.
379 ' around one of the server groups? The invalid index was: '.$index
380 );
381 }
382
383 /*
384 * Make sure that the group is an array. Each group is an array of servers. Each server is
385 * an array of name => value pairs for that server.
386 */
387 if (!is_array($group)) {
388 throw new Exception(
389 "Invalid value for the server with index ".$index.
390 ". Remeber that the 'memcache_store.servers' configuration option".
391 ' contains an array of arrays of arrays.'
392 );
393 }
394
395 // parse and add this group to the server group list
396 self::$serverGroups[] = self::loadMemcacheServerGroup($group);
397 }
398
399 return self::$serverGroups;
400 }
401
402
415 private static function getExpireTime()
416 {
417 // get the configuration instance
419 assert($config instanceof SimpleSAML_Configuration);
420
421 // get the expire-value from the configuration
422 $expire = $config->getInteger('memcache_store.expires', 0);
423
424 // it must be a positive integer
425 if ($expire < 0) {
426 throw new Exception(
427 "The value of 'memcache_store.expires' in the configuration can't be a negative integer."
428 );
429 }
430
431 /* If the configuration option is 0, then we should return 0. This allows the user to specify that the data
432 * shouldn't expire.
433 */
434 if ($expire == 0) {
435 return 0;
436 }
437
438 /* The expire option is given as the number of seconds into the future an item should expire. We convert this
439 * to an actual timestamp.
440 */
441 $expireTime = time() + $expire;
442
443 return $expireTime;
444 }
445
446
454 public static function getStats()
455 {
456 $ret = array();
457
458 foreach (self::getMemcacheServers() as $sg) {
459 $stats = method_exists($sg, 'getExtendedStats') ? $sg->getExtendedStats() : $sg->getStats();
460 foreach ($stats as $server => $data) {
461 if ($data === false) {
462 throw new Exception('Failed to get memcache server status.');
463 }
464 }
465
467
468 $ret = array_merge_recursive($ret, $stats);
469 }
470
471 return $ret;
472 }
473
474
481 public static function getRawStats()
482 {
483 $ret = array();
484
485 foreach (self::getMemcacheServers() as $sg) {
486 $stats = method_exists($sg, 'getExtendedStats') ? $sg->getExtendedStats() : $sg->getStats();
487 $ret[] = $stats;
488 }
489
490 return $ret;
491 }
492}
An exception for terminatinating execution or to throw for unit testing.
static warning($string)
Definition: Logger.php:177
static debug($string)
Definition: Logger.php:211
static transpose($array)
This function transposes a two-dimensional array, so that $a['k1']['k2'] becomes $a['k2']['k1'].
Definition: Arrays.php:39
static getInstance($instancename='simplesaml')
Get a configuration file by its instance name.
static getMemcacheServers()
This function gets a list of all configured memcache servers.
Definition: Memcache.php:356
static loadMemcacheServerGroup(array $group)
This function takes in a list of servers belonging to a group and creates a Memcache object from the ...
Definition: Memcache.php:310
static set($key, $value, $expire=null)
Save a key-value pair to the memcache servers.
Definition: Memcache.php:149
static getRawStats()
Retrieve statistics directly in the form returned by getExtendedStats, for all server groups.
Definition: Memcache.php:481
static $serverGroups
Definition: Memcache.php:28
static getExpireTime()
This is a helper-function which returns the expire value of data we should store to the memcache serv...
Definition: Memcache.php:415
static addMemcacheServer($memcache, $server)
This function adds a server from the 'memcache_store.servers' configuration option to a Memcache obje...
Definition: Memcache.php:214
static getStats()
This function retrieves statistics about all memcache server groups.
Definition: Memcache.php:454
$key
Definition: croninfo.php:18
$config
Definition: bootstrap.php:15
$index
Definition: metadata.php:60
$expire
Definition: saml2-acs.php:140
$info
Definition: index.php:5
$ret
Definition: parser.php:6
$server
Definition: sabredav.php:48
$data
Definition: bench.php:6
foreach($authData as $name=> $values) $memcache