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) {
158  $expire = self::getExpireTime();
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 
341  self::addMemcacheServer($memcache, $server);
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 }
$expire
Definition: saml2-acs.php:140
foreach($authData as $name=> $values) $memcache
static transpose($array)
This function transposes a two-dimensional array, so that $a[&#39;k1&#39;][&#39;k2&#39;] becomes $a[&#39;k2&#39;][&#39;k1&#39;].
Definition: Arrays.php:39
static getMemcacheServers()
This function gets a list of all configured memcache servers.
Definition: Memcache.php:356
$config
Definition: bootstrap.php:15
static getRawStats()
Retrieve statistics directly in the form returned by getExtendedStats, for all server groups...
Definition: Memcache.php:481
static debug($string)
Definition: Logger.php:211
$index
Definition: metadata.php:60
$server
Definition: sabredav.php:48
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 warning($string)
Definition: Logger.php:177
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
$ret
Definition: parser.php:6
$info
Definition: index.php:5
$key
Definition: croninfo.php:18
static $serverGroups
Definition: Memcache.php:28
static addMemcacheServer($memcache, $server)
This function adds a server from the &#39;memcache_store.servers&#39; configuration option to a Memcache obje...
Definition: Memcache.php:214
static getInstance($instancename='simplesaml')
Get a configuration file by its instance name.
static getStats()
This function retrieves statistics about all memcache server groups.
Definition: Memcache.php:454
$data
Definition: bench.php:6