Skip to content
Snippets Groups Projects
Commit 2866087b authored by Jaime Perez Crespo's avatar Jaime Perez Crespo
Browse files

Reformat SimpleSAML_Memcache.

parent 539312e6
Branches
Tags
No related merge requests found
<?php <?php
/** /**
* This file implements functions to read and write to a group of memcache * This file implements functions to read and write to a group of memcache
* servers. * servers.
...@@ -16,428 +17,437 @@ ...@@ -16,428 +17,437 @@
* @author Olav Morken, UNINETT AS. * @author Olav Morken, UNINETT AS.
* @package SimpleSAMLphp * @package SimpleSAMLphp
*/ */
class SimpleSAML_Memcache { class SimpleSAML_Memcache
{
/**
* Cache of the memcache servers we are using. /**
* * Cache of the memcache servers we are using.
* @var Memcache[]|null *
*/ * @var Memcache[]|null
private static $serverGroups = NULL; */
private static $serverGroups = null;
/**
* Find data stored with a given key. /**
* * Find data stored with a given key.
* @param string $key The key of the data. *
* @return mixed The data stored with the given key, or null if no data matching the key was found. * @param string $key The key of the data.
*/ *
public static function get($key) { * @return mixed The data stored with the given key, or null if no data matching the key was found.
SimpleSAML_Logger::debug("loading key $key from memcache"); */
public static function get($key)
$latestInfo = NULL; {
$latestTime = 0.0; SimpleSAML_Logger::debug("loading key $key from memcache");
$latestData = NULL;
$mustUpdate = FALSE; $latestInfo = null;
$latestTime = 0.0;
/* Search all the servers for the given id. */ $latestData = null;
foreach(self::getMemcacheServers() as $server) { $mustUpdate = false;
$serializedInfo = $server->get($key);
if($serializedInfo === FALSE) { // search all the servers for the given id
/* Either the server is down, or we don't have the value stored on that server. */ foreach (self::getMemcacheServers() as $server) {
$mustUpdate = TRUE; $serializedInfo = $server->get($key);
continue; if ($serializedInfo === false) {
} // either the server is down, or we don't have the value stored on that server
$mustUpdate = true;
/* Unserialize the object. */ continue;
$info = unserialize($serializedInfo); }
/* // unserialize the object
* Make sure that this is an array with two keys: $info = unserialize($serializedInfo);
* - 'timestamp': The time the data was saved.
* - 'data': The data. /*
*/ * Make sure that this is an array with two keys:
if(!is_array($info)) { * - 'timestamp': The time the data was saved.
SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' . * - 'data': The data.
' Data was not an array.'); */
continue; if (!is_array($info)) {
} SimpleSAML_Logger::warning(
if(!array_key_exists('timestamp', $info)) { 'Retrieved invalid data from a memcache server. Data was not an array.'
SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' . );
' Missing timestamp.'); continue;
continue; }
} if (!array_key_exists('timestamp', $info)) {
if(!array_key_exists('data', $info)) { SimpleSAML_Logger::warning(
SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' . 'Retrieved invalid data from a memcache server. Missing timestamp.'
' Missing data.'); );
continue; continue;
} }
if (!array_key_exists('data', $info)) {
if($latestInfo === NULL) { SimpleSAML_Logger::warning(
/* First info found. */ 'Retrieved invalid data from a memcache server. Missing data.'
$latestInfo = $serializedInfo; );
$latestTime = $info['timestamp']; continue;
$latestData = $info['data']; }
continue;
} if ($latestInfo === null) {
// first info found
if($info['timestamp'] === $latestTime && $serializedInfo === $latestInfo) { $latestInfo = $serializedInfo;
/* This data matches the data from the other server(s). */ $latestTime = $info['timestamp'];
continue; $latestData = $info['data'];
} continue;
}
/*
* Different data from different servers. We need to update at least one if ($info['timestamp'] === $latestTime && $serializedInfo === $latestInfo) {
* of them to maintain synchronization. // this data matches the data from the other server(s)
*/ continue;
}
$mustUpdate = TRUE;
// different data from different servers. We need to update at least one of them to maintain sync.
/* Update if data in $info is newer than $latestData. */ $mustUpdate = true;
if($latestTime < $info['timestamp']) {
$latestInfo = $serializedInfo; // update if data in $info is newer than $latestData
$latestTime = $info['timestamp']; if ($latestTime < $info['timestamp']) {
$latestData = $info['data']; $latestInfo = $serializedInfo;
} $latestTime = $info['timestamp'];
} $latestData = $info['data'];
}
if($latestData === NULL) { }
/* We didn't find any data matching the key. */
SimpleSAML_Logger::debug("key $key not found in memcache"); if ($latestData === null) {
return NULL; // we didn't find any data matching the key
} SimpleSAML_Logger::debug("key $key not found in memcache");
return null;
if($mustUpdate) { }
/* We found data matching the key, but some of the servers need updating. */
SimpleSAML_Logger::debug("Memcache servers out of sync for $key, forcing sync"); if ($mustUpdate) {
self::set($key, $latestData); // we found data matching the key, but some of the servers need updating
} SimpleSAML_Logger::debug("Memcache servers out of sync for $key, forcing sync");
self::set($key, $latestData);
return $latestData; }
}
return $latestData;
}
/**
* Save a key-value pair to the memcache servers.
* /**
* @param string $key The key of the data. * Save a key-value pair to the memcache servers.
* @param mixed $value The value of the data. *
* @param integer|null $expire The expiration timestamp of the data. * @param string $key The key of the data.
*/ * @param mixed $value The value of the data.
public static function set($key, $value, $expire = NULL) { * @param integer|null $expire The expiration timestamp of the data.
SimpleSAML_Logger::debug("saving key $key to memcache"); */
$savedInfo = array( public static function set($key, $value, $expire = null)
'timestamp' => microtime(TRUE), {
'data' => $value SimpleSAML_Logger::debug("saving key $key to memcache");
); $savedInfo = array(
'timestamp' => microtime(true),
if ($expire === NULL) { 'data' => $value
$expire = self::getExpireTime(); );
}
if ($expire === null) {
$savedInfoSerialized = serialize($savedInfo); $expire = self::getExpireTime();
}
/* Store this object to all groups of memcache servers. */
foreach(self::getMemcacheServers() as $server) { $savedInfoSerialized = serialize($savedInfo);
$server->set($key, $savedInfoSerialized, 0, $expire);
} // store this object to all groups of memcache servers
} foreach (self::getMemcacheServers() as $server) {
$server->set($key, $savedInfoSerialized, 0, $expire);
}
/** }
* Delete a key-value pair from the memcache servers.
*
* @param string $key The key we should delete. /**
*/ * Delete a key-value pair from the memcache servers.
public static function delete($key) { *
assert('is_string($key)'); * @param string $key The key we should delete.
SimpleSAML_Logger::debug("deleting key $key from memcache"); */
public static function delete($key)
/* Store this object to all groups of memcache servers. */ {
foreach(self::getMemcacheServers() as $server) { assert('is_string($key)');
$server->delete($key); SimpleSAML_Logger::debug("deleting key $key from memcache");
}
} // store this object to all groups of memcache servers
foreach (self::getMemcacheServers() as $server) {
$server->delete($key);
/** }
* This function adds a server from the 'memcache_store.servers' }
* configuration option to a Memcache object.
*
* The server parameter is an array with the following keys: /**
* - hostname * This function adds a server from the 'memcache_store.servers'
* Hostname or ip address to the memcache server. * configuration option to a Memcache object.
* - port (optional) *
* port number the memcache server is running on. This * The server parameter is an array with the following keys:
* defaults to memcache.default_port if no value is given. * - hostname
* The default value of memcache.default_port is 11211. * Hostname or ip address to the memcache server.
* - weight (optional) * - port (optional)
* The weight of this server in the load balancing * port number the memcache server is running on. This
* cluster. * defaults to memcache.default_port if no value is given.
* - timeout (optional) * The default value of memcache.default_port is 11211.
* The timeout for contacting this server, in seconds. * - weight (optional)
* The default value is 3 seconds. * The weight of this server in the load balancing
* * cluster.
* @param Memcache $memcache The Memcache object we should add this server to. * - timeout (optional)
* @param array $server An associative array with the configuration options for the server to add. * The timeout for contacting this server, in seconds.
* * The default value is 3 seconds.
* @throws Exception If any configuration option for the server is invalid. *
*/ * @param Memcache $memcache The Memcache object we should add this server to.
private static function addMemcacheServer($memcache, $server) { * @param array $server An associative array with the configuration options for the server to add.
*
/* The hostname option is required. */ * @throws Exception If any configuration option for the server is invalid.
if(!array_key_exists('hostname', $server)) { */
throw new Exception('hostname setting missing from server in the' . private static function addMemcacheServer($memcache, $server)
' \'memcache_store.servers\' configuration option.'); {
} // the hostname option is required
if (!array_key_exists('hostname', $server)) {
$hostname = $server['hostname']; throw new Exception(
"hostname setting missing from server in the 'memcache_store.servers' configuration option."
/* The hostname must be a valid string. */ );
if(!is_string($hostname)) { }
throw new Exception('Invalid hostname for server in the' .
' \'memcache_store.servers\' configuration option. The hostname is' . $hostname = $server['hostname'];
' supposed to be a string.');
} // the hostname must be a valid string
if (!is_string($hostname)) {
/* Check if the user has specified a port number. */ throw new Exception(
if(array_key_exists('port', $server)) { "Invalid hostname for server in the 'memcache_store.servers' configuration option. The hostname is".
/* Get the port number from the array, and validate it. */ ' supposed to be a string.'
$port = (int)$server['port']; );
if(($port <= 0) || ($port > 65535)) { }
throw new Exception('Invalid port for server in the' .
' \'memcache_store.servers\' configuration option. The port number' . // check if the user has specified a port number
' is supposed to be an integer between 0 and 65535.'); if (array_key_exists('port', $server)) {
} // get the port number from the array, and validate it
} else { $port = (int) $server['port'];
/* Use the default port number from the ini-file. */ if (($port <= 0) || ($port > 65535)) {
$port = (int)ini_get('memcache.default_port'); throw new Exception(
if($port <= 0 || $port > 65535) { "Invalid port for server in the 'memcache_store.servers' configuration option. The port number".
/* Invalid port number from the ini-file. fall back to the default. */ ' is supposed to be an integer between 0 and 65535.'
$port = 11211; );
} }
} } else {
// use the default port number from the ini-file
/* Check if the user has specified a weight for this server. */ $port = (int) ini_get('memcache.default_port');
if(array_key_exists('weight', $server)) { if ($port <= 0 || $port > 65535) {
/* Get the weight and validate it. */ // invalid port number from the ini-file. fall back to the default
$weight = (int)$server['weight']; $port = 11211;
if($weight <= 0) { }
throw new Exception('Invalid weight for server in the' . }
' \'memcache_store.servers\' configuration option. The weight is' .
' supposed to be a positive integer.'); // check if the user has specified a weight for this server
} if (array_key_exists('weight', $server)) {
} else { // get the weight and validate it
/* Use a default weight of 1. */ $weight = (int) $server['weight'];
$weight = 1; if ($weight <= 0) {
} throw new Exception(
"Invalid weight for server in the 'memcache_store.servers' configuration option. The weight is".
/* Check if the user has specified a timeout for this server. */ ' supposed to be a positive integer.'
if(array_key_exists('timeout', $server)) { );
/* Get the timeout and validate it. */ }
$timeout = (int)$server['timeout']; } else {
if($timeout <= 0) { // use a default weight of 1
throw new Exception('Invalid timeout for server in the' . $weight = 1;
' \'memcache_store.servers\' configuration option. The timeout is' . }
' supposed to be a positive integer.');
} // check if the user has specified a timeout for this server
} else { if (array_key_exists('timeout', $server)) {
/* Use a default timeout of 3 seconds. */ // get the timeout and validate it
$timeout = 3; $timeout = (int) $server['timeout'];
} if ($timeout <= 0) {
throw new Exception(
/* Add this server to the Memcache object. */ "Invalid timeout for server in the 'memcache_store.servers' configuration option. The timeout is".
$memcache->addServer($hostname, $port, TRUE, $weight, $timeout); ' supposed to be a positive integer.'
} );
}
} else {
/** // use a default timeout of 3 seconds
* This function takes in a list of servers belonging to a group and $timeout = 3;
* creates a Memcache object from the servers in the group. }
*
* @param array $group Array of servers which should be created as a group. // add this server to the Memcache object
* @return Memcache A Memcache object of the servers in the group $memcache->addServer($hostname, $port, true, $weight, $timeout);
* }
* @throws Exception If the servers configuration is invalid.
*/
private static function loadMemcacheServerGroup(array $group) { /**
* This function takes in a list of servers belonging to a group and
if(!class_exists('Memcache')) { * creates a Memcache object from the servers in the group.
throw new Exception('Missing Memcache class. Is the memcache extension installed?'); *
} * @param array $group Array of servers which should be created as a group.
*
/* Create the Memcache object. */ * @return Memcache A Memcache object of the servers in the group
$memcache = new Memcache(); *
* @throws Exception If the servers configuration is invalid.
/* Iterate over all the servers in the group and add them to the Memcache object. */ */
foreach($group as $index => $server) { private static function loadMemcacheServerGroup(array $group)
/* {
* Make sure that we don't have an index. An index would be a sign of invalid configuration. if (!class_exists('Memcache')) {
*/ throw new Exception('Missing Memcache class. Is the memcache extension installed?');
if(!is_int($index)) { }
throw new Exception('Invalid index on element in the' .
' \'memcache_store.servers\' configuration option. Perhaps you' . // create the Memcache object
' have forgotten to add an array(...) around one of the server groups?' . $memcache = new Memcache();
' The invalid index was: ' . $index);
} // iterate over all the servers in the group and add them to the Memcache object
foreach ($group as $index => $server) {
/* // make sure that we don't have an index. An index would be a sign of invalid configuration
* Make sure that the server object is an array. Each server is an array with if (!is_int($index)) {
* name-value pairs. throw new Exception(
*/ "Invalid index on element in the 'memcache_store.servers' configuration option. Perhaps you".
if(!is_array($server)) { ' have forgotten to add an array(...) around one of the server groups? The invalid index was: '.
throw new Exception('Invalid value for the server with index ' . $index . $index
'. Remeber that the \'memcache_store.servers\' configuration option' . );
' contains an array of arrays of arrays.'); }
}
// make sure that the server object is an array. Each server is an array with name-value pairs
self::addMemcacheServer($memcache, $server); if (!is_array($server)) {
} throw new Exception(
'Invalid value for the server with index '.$index.
return $memcache; '. Remeber that the \'memcache_store.servers\' configuration option'.
} ' contains an array of arrays of arrays.'
);
}
/**
* This function gets a list of all configured memcache servers. This list is initialized based self::addMemcacheServer($memcache, $server);
* on the content of 'memcache_store.servers' in the configuration. }
*
* @return Memcache[] Array with Memcache objects. return $memcache;
* }
* @throws Exception If the servers configuration is invalid.
*/
private static function getMemcacheServers() { /**
* This function gets a list of all configured memcache servers. This list is initialized based
/* Check if we have loaded the servers already. */ * on the content of 'memcache_store.servers' in the configuration.
if(self::$serverGroups != NULL) { *
return self::$serverGroups; * @return Memcache[] Array with Memcache objects.
} *
* @throws Exception If the servers configuration is invalid.
/* Initialize the servers-array. */ */
self::$serverGroups = array(); private static function getMemcacheServers()
{
/* Load the configuration. */ // check if we have loaded the servers already
$config = SimpleSAML_Configuration::getInstance(); if (self::$serverGroups != null) {
return self::$serverGroups;
}
$groups = $config->getArray('memcache_store.servers');
// initialize the servers-array
/* Iterate over all the groups in the 'memcache_store.servers' configuration option. */ self::$serverGroups = array();
foreach($groups as $index => $group) {
/* // load the configuration
* Make sure that the group doesn't have an index. An index would be a sign of $config = SimpleSAML_Configuration::getInstance();
* invalid configuration.
*/
if(!is_int($index)) { $groups = $config->getArray('memcache_store.servers');
throw new Exception('Invalid index on element in the \'memcache_store.servers\'' .
' configuration option. Perhaps you have forgotten to add an array(...)' . // iterate over all the groups in the 'memcache_store.servers' configuration option
' around one of the server groups? The invalid index was: ' . $index); foreach ($groups as $index => $group) {
} // make sure that the group doesn't have an index. An index would be a sign of invalid configuration
if (!is_int($index)) {
/* throw new Exception(
* Make sure that the group is an array. Each group is an array of servers. Each server is "Invalid index on element in the 'memcache_store.servers'".
* an array of name => value pairs for that server. ' configuration option. Perhaps you have forgotten to add an array(...)'.
*/ ' around one of the server groups? The invalid index was: '.$index
if(!is_array($group)) { );
throw new Exception('Invalid value for the server with index ' . $index . }
'. Remeber that the \'memcache_store.servers\' configuration option' .
' contains an array of arrays of arrays.'); /*
} * Make sure that the group is an array. Each group is an array of servers. Each server is
* an array of name => value pairs for that server.
/* Parse and add this group to the server group list. */ */
self::$serverGroups[] = self::loadMemcacheServerGroup($group); if (!is_array($group)) {
} throw new Exception(
"Invalid value for the server with index ".$index.
return self::$serverGroups; ". Remeber that the 'memcache_store.servers' configuration option".
} ' contains an array of arrays of arrays.'
);
}
/**
* This is a helper-function which returns the expire value of data // parse and add this group to the server group list
* we should store to the memcache servers. self::$serverGroups[] = self::loadMemcacheServerGroup($group);
* }
* The value is set depending on the configuration. If no value is
* set in the configuration, then we will use a default value of 0. return self::$serverGroups;
* 0 means that the item will never expire. }
*
* @return integer The value which should be passed in the set(...) calls to the memcache objects.
* /**
* @throws Exception If the option 'memcache_store.expires' has a negative value. * This is a helper-function which returns the expire value of data
*/ * we should store to the memcache servers.
private static function getExpireTime() *
{ * The value is set depending on the configuration. If no value is
/* Get the configuration instance. */ * set in the configuration, then we will use a default value of 0.
$config = SimpleSAML_Configuration::getInstance(); * 0 means that the item will never expire.
assert($config instanceof SimpleSAML_Configuration); *
* @return integer The value which should be passed in the set(...) calls to the memcache objects.
/* Get the expire-value from the configuration. */ *
$expire = $config->getInteger('memcache_store.expires', 0); * @throws Exception If the option 'memcache_store.expires' has a negative value.
*/
/* It must be a positive integer. */ private static function getExpireTime()
if($expire < 0) { {
throw new Exception('The value of \'memcache_store.expires\' in the' . // get the configuration instance
' configuration can\'t be a negative integer.'); $config = SimpleSAML_Configuration::getInstance();
} assert($config instanceof SimpleSAML_Configuration);
/* If the configuration option is 0, then we should // get the expire-value from the configuration
* return 0. This allows the user to specify that the data $expire = $config->getInteger('memcache_store.expires', 0);
* shouldn't expire.
*/ // it must be a positive integer
if($expire == 0) { if ($expire < 0) {
return 0; throw new Exception(
} "The value of 'memcache_store.expires' in the configuration can't be a negative integer."
);
/* The expire option is given as the number of seconds into the }
* future an item should expire. We convert this to an actual
* timestamp. /* If the configuration option is 0, then we should return 0. This allows the user to specify that the data
*/ * shouldn't expire.
$expireTime = time() + $expire; */
if ($expire == 0) {
return $expireTime; return 0;
} }
/* The expire option is given as the number of seconds into the future an item should expire. We convert this
/** * to an actual timestamp.
* This function retrieves statistics about all memcache server groups. */
* $expireTime = time() + $expire;
* @return array Array with the names of each stat and an array with the value for each server group.
* return $expireTime;
* @throws Exception If memcache server status couldn't be retrieved. }
*/
public static function getStats()
{ /**
$ret = array(); * This function retrieves statistics about all memcache server groups.
*
foreach(self::getMemcacheServers() as $sg) { * @return array Array with the names of each stat and an array with the value for each server group.
$stats = $sg->getExtendedStats(); *
if($stats === FALSE) { * @throws Exception If memcache server status couldn't be retrieved.
throw new Exception('Failed to get memcache server status.'); */
} public static function getStats()
{
$stats = SimpleSAML\Utils\Arrays::transpose($stats); $ret = array();
$ret = array_merge_recursive($ret, $stats); foreach (self::getMemcacheServers() as $sg) {
} $stats = $sg->getExtendedStats();
if ($stats === false) {
return $ret; throw new Exception('Failed to get memcache server status.');
} }
$stats = SimpleSAML\Utils\Arrays::transpose($stats);
/**
* Retrieve statistics directly in the form returned by getExtendedStats, for $ret = array_merge_recursive($ret, $stats);
* all server groups. }
*
* @return array An array with the extended stats output for each server group. return $ret;
*/ }
public static function getRawStats() {
$ret = array();
/**
foreach(self::getMemcacheServers() as $sg) { * Retrieve statistics directly in the form returned by getExtendedStats, for
$stats = $sg->getExtendedStats(); * all server groups.
$ret[] = $stats; *
} * @return array An array with the extended stats output for each server group.
*/
return $ret; public static function getRawStats()
} {
$ret = array();
foreach (self::getMemcacheServers() as $sg) {
$stats = $sg->getExtendedStats();
$ret[] = $stats;
}
return $ret;
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment