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) {
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  }
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 
342  self::addMemcacheServer($memcache, $server);
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 }
$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:357
static getRawStats()
Retrieve statistics directly in the form returned by getExtendedStats, for all server groups...
Definition: Memcache.php:482
static debug($string)
Definition: Logger.php:213
$index
Definition: metadata.php:60
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
$stats
static warning($string)
Definition: Logger.php:179
Create styles array
The data for the language used.
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
$server
Definition: getUserInfo.php:12
$ret
Definition: parser.php:6
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
$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:215
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:455