ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 }
168 else {
169 $server->set($key, $savedInfoSerialized, 0, $expire);
170 }
171 }
172 }
173
174
180 public static function delete($key)
181 {
182 assert('is_string($key)');
183 SimpleSAML\Logger::debug("deleting key $key from memcache");
184
185 // store this object to all groups of memcache servers
186 foreach (self::getMemcacheServers() as $server) {
187 $server->delete($key);
188 }
189 }
190
191
215 private static function addMemcacheServer($memcache, $server)
216 {
217 // the hostname option is required
218 if (!array_key_exists('hostname', $server)) {
219 throw new Exception(
220 "hostname setting missing from server in the 'memcache_store.servers' configuration option."
221 );
222 }
223
224 $hostname = $server['hostname'];
225
226 // the hostname must be a valid string
227 if (!is_string($hostname)) {
228 throw new Exception(
229 "Invalid hostname for server in the 'memcache_store.servers' configuration option. The hostname is".
230 ' supposed to be a string.'
231 );
232 }
233
234 // check if we are told to use a socket
235 $socket = false;
236 if (strpos($hostname, 'unix:///') === 0) {
237 $socket = true;
238 }
239
240 // check if the user has specified a port number
241 if ($socket) {
242 // force port to be 0 for sockets
243 $port = 0;
244 } elseif (array_key_exists('port', $server)) {
245 // get the port number from the array, and validate it
246 $port = (int) $server['port'];
247 if (($port <= 0) || ($port > 65535)) {
248 throw new Exception(
249 "Invalid port for server in the 'memcache_store.servers' configuration option. The port number".
250 ' is supposed to be an integer between 0 and 65535.'
251 );
252 }
253 } else {
254 // use the default port number from the ini-file
255 $port = (int) ini_get('memcache.default_port');
256 if ($port <= 0 || $port > 65535) {
257 // invalid port number from the ini-file. fall back to the default
258 $port = 11211;
259 }
260 }
261
262 // check if the user has specified a weight for this server
263 if (array_key_exists('weight', $server)) {
264 // get the weight and validate it
265 $weight = (int) $server['weight'];
266 if ($weight <= 0) {
267 throw new Exception(
268 "Invalid weight for server in the 'memcache_store.servers' configuration option. The weight is".
269 ' supposed to be a positive integer.'
270 );
271 }
272 } else {
273 // use a default weight of 1
274 $weight = 1;
275 }
276
277 // check if the user has specified a timeout for this server
278 if (array_key_exists('timeout', $server)) {
279 // get the timeout and validate it
280 $timeout = (int) $server['timeout'];
281 if ($timeout <= 0) {
282 throw new Exception(
283 "Invalid timeout for server in the 'memcache_store.servers' configuration option. The timeout is".
284 ' supposed to be a positive integer.'
285 );
286 }
287 } else {
288 // use a default timeout of 3 seconds
289 $timeout = 3;
290 }
291
292 // add this server to the Memcache object
293 if (self::$extension === 'memcached') {
294 $memcache->addServer($hostname, $port);
295 } else {
296 $memcache->addServer($hostname, $port, true, $weight, $timeout, $timeout, true);
297 }
298 }
299
300
311 private static function loadMemcacheServerGroup(array $group)
312 {
313 $class = class_exists('Memcache') ? 'Memcache' : (class_exists('Memcached') ? 'Memcached' : FALSE);
314 if (!$class) {
315 throw new Exception('Missing Memcached implementation. You must install either the Memcache or Memcached extension.');
316 }
317 self::$extension = strtolower($class);
318
319 // create the Memcache object
320 $memcache = new $class();
321
322 // iterate over all the servers in the group and add them to the Memcache object
323 foreach ($group as $index => $server) {
324 // make sure that we don't have an index. An index would be a sign of invalid configuration
325 if (!is_int($index)) {
326 throw new Exception(
327 "Invalid index on element in the 'memcache_store.servers' configuration option. Perhaps you".
328 ' have forgotten to add an array(...) around one of the server groups? The invalid index was: '.
329 $index
330 );
331 }
332
333 // make sure that the server object is an array. Each server is an array with name-value pairs
334 if (!is_array($server)) {
335 throw new Exception(
336 'Invalid value for the server with index '.$index.
337 '. Remeber that the \'memcache_store.servers\' configuration option'.
338 ' contains an array of arrays of arrays.'
339 );
340 }
341
343 }
344
345 return $memcache;
346 }
347
348
357 private static function getMemcacheServers()
358 {
359 // check if we have loaded the servers already
360 if (self::$serverGroups != null) {
361 return self::$serverGroups;
362 }
363
364 // initialize the servers-array
365 self::$serverGroups = array();
366
367 // load the configuration
369
370
371 $groups = $config->getArray('memcache_store.servers');
372
373 // iterate over all the groups in the 'memcache_store.servers' configuration option
374 foreach ($groups as $index => $group) {
375 // make sure that the group doesn't have an index. An index would be a sign of invalid configuration
376 if (!is_int($index)) {
377 throw new Exception(
378 "Invalid index on element in the 'memcache_store.servers'".
379 ' configuration option. Perhaps you have forgotten to add an array(...)'.
380 ' around one of the server groups? The invalid index was: '.$index
381 );
382 }
383
384 /*
385 * Make sure that the group is an array. Each group is an array of servers. Each server is
386 * an array of name => value pairs for that server.
387 */
388 if (!is_array($group)) {
389 throw new Exception(
390 "Invalid value for the server with index ".$index.
391 ". Remeber that the 'memcache_store.servers' configuration option".
392 ' contains an array of arrays of arrays.'
393 );
394 }
395
396 // parse and add this group to the server group list
397 self::$serverGroups[] = self::loadMemcacheServerGroup($group);
398 }
399
400 return self::$serverGroups;
401 }
402
403
416 private static function getExpireTime()
417 {
418 // get the configuration instance
420 assert($config instanceof SimpleSAML_Configuration);
421
422 // get the expire-value from the configuration
423 $expire = $config->getInteger('memcache_store.expires', 0);
424
425 // it must be a positive integer
426 if ($expire < 0) {
427 throw new Exception(
428 "The value of 'memcache_store.expires' in the configuration can't be a negative integer."
429 );
430 }
431
432 /* If the configuration option is 0, then we should return 0. This allows the user to specify that the data
433 * shouldn't expire.
434 */
435 if ($expire == 0) {
436 return 0;
437 }
438
439 /* The expire option is given as the number of seconds into the future an item should expire. We convert this
440 * to an actual timestamp.
441 */
442 $expireTime = time() + $expire;
443
444 return $expireTime;
445 }
446
447
455 public static function getStats()
456 {
457 $ret = array();
458
459 foreach (self::getMemcacheServers() as $sg) {
460 $stats = method_exists($sg, 'getExtendedStats') ? $sg->getExtendedStats() : $sg->getStats();
461 foreach ($stats as $server => $data) {
462 if ($data === false) {
463 throw new Exception('Failed to get memcache server status.');
464 }
465 }
466
468
469 $ret = array_merge_recursive($ret, $stats);
470 }
471
472 return $ret;
473 }
474
475
482 public static function getRawStats()
483 {
484 $ret = array();
485
486 foreach (self::getMemcacheServers() as $sg) {
487 $stats = method_exists($sg, 'getExtendedStats') ? $sg->getExtendedStats() : $sg->getStats();
488 $ret[] = $stats;
489 }
490
491 return $ret;
492 }
493
494}
An exception for terminatinating execution or to throw for unit testing.
static warning($string)
Definition: Logger.php:179
static debug($string)
Definition: Logger.php:213
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:357
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:311
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:482
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:416
static addMemcacheServer($memcache, $server)
This function adds a server from the 'memcache_store.servers' configuration option to a Memcache obje...
Definition: Memcache.php:215
static getStats()
This function retrieves statistics about all memcache server groups.
Definition: Memcache.php:455
$key
Definition: croninfo.php:18
$server
Definition: getUserInfo.php:12
$index
Definition: metadata.php:60
$expire
Definition: saml2-acs.php:140
$info
Definition: index.php:5
$stats
$ret
Definition: parser.php:6
foreach($authData as $name=> $values) $memcache