From 2866087b26fe72ab6a23f7db45a7c8e2430e5000 Mon Sep 17 00:00:00 2001 From: Jaime Perez Crespo <jaime.perez@uninett.no> Date: Wed, 5 Aug 2015 15:33:07 +0200 Subject: [PATCH] Reformat SimpleSAML_Memcache. --- lib/SimpleSAML/Memcache.php | 856 ++++++++++++++++++------------------ 1 file changed, 433 insertions(+), 423 deletions(-) diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php index 6962fec82..94c439c76 100644 --- a/lib/SimpleSAML/Memcache.php +++ b/lib/SimpleSAML/Memcache.php @@ -1,5 +1,6 @@ <?php + /** * This file implements functions to read and write to a group of memcache * servers. @@ -16,428 +17,437 @@ * @author Olav Morken, UNINETT AS. * @package SimpleSAMLphp */ -class SimpleSAML_Memcache { - - /** - * Cache of the memcache servers we are using. - * - * @var Memcache[]|null - */ - private static $serverGroups = NULL; - - - /** - * 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. - */ - public static function get($key) { - SimpleSAML_Logger::debug("loading key $key from memcache"); - - $latestInfo = NULL; - $latestTime = 0.0; - $latestData = NULL; - $mustUpdate = FALSE; - - /* Search all the servers for the given id. */ - foreach(self::getMemcacheServers() as $server) { - $serializedInfo = $server->get($key); - if($serializedInfo === FALSE) { - /* Either the server is down, or we don't have the value stored on that server. */ - $mustUpdate = TRUE; - continue; - } - - /* Unserialize the object. */ - $info = unserialize($serializedInfo); - - /* - * Make sure that this is an array with two keys: - * - 'timestamp': The time the data was saved. - * - 'data': The data. - */ - if(!is_array($info)) { - SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' . - ' Data was not an array.'); - continue; - } - if(!array_key_exists('timestamp', $info)) { - SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' . - ' Missing timestamp.'); - continue; - } - if(!array_key_exists('data', $info)) { - SimpleSAML_Logger::warning('Retrieved invalid data from a memcache server.' . - ' Missing data.'); - continue; - } - - if($latestInfo === NULL) { - /* First info found. */ - $latestInfo = $serializedInfo; - $latestTime = $info['timestamp']; - $latestData = $info['data']; - continue; - } - - if($info['timestamp'] === $latestTime && $serializedInfo === $latestInfo) { - /* This data matches the data from the other server(s). */ - continue; - } - - /* - * Different data from different servers. We need to update at least one - * of them to maintain synchronization. - */ - - $mustUpdate = TRUE; - - /* Update if data in $info is newer than $latestData. */ - if($latestTime < $info['timestamp']) { - $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"); - 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"); - self::set($key, $latestData); - } - - return $latestData; - } - - - /** - * Save a key-value pair to the memcache servers. - * - * @param string $key The key of the data. - * @param mixed $value The value of the data. - * @param integer|null $expire The expiration timestamp of the data. - */ - public static function set($key, $value, $expire = NULL) { - SimpleSAML_Logger::debug("saving key $key to memcache"); - $savedInfo = array( - 'timestamp' => microtime(TRUE), - 'data' => $value - ); - - if ($expire === NULL) { - $expire = self::getExpireTime(); - } - - $savedInfoSerialized = serialize($savedInfo); - - /* 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. - */ - public static function delete($key) { - assert('is_string($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 - * Hostname or ip address to the memcache server. - * - port (optional) - * port number the memcache server is running on. This - * defaults to memcache.default_port if no value is given. - * The default value of memcache.default_port is 11211. - * - weight (optional) - * The weight of this server in the load balancing - * cluster. - * - timeout (optional) - * The timeout for contacting this server, in seconds. - * The default value is 3 seconds. - * - * @param Memcache $memcache The Memcache object we should add this server to. - * @param array $server An associative array with the configuration options for the server to add. - * - * @throws Exception If any configuration option for the server is invalid. - */ - private static function addMemcacheServer($memcache, $server) { - - /* The hostname option is required. */ - if(!array_key_exists('hostname', $server)) { - throw new Exception('hostname setting missing from server in the' . - ' \'memcache_store.servers\' configuration option.'); - } - - $hostname = $server['hostname']; - - /* 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' . - ' supposed to be a string.'); - } - - /* Check if the user has specified a port number. */ - if(array_key_exists('port', $server)) { - /* Get the port number from the array, and validate it. */ - $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' . - ' is supposed to be an integer between 0 and 65535.'); - } - } else { - /* Use the default port number from the ini-file. */ - $port = (int)ini_get('memcache.default_port'); - if($port <= 0 || $port > 65535) { - /* Invalid port number from the ini-file. fall back to the default. */ - $port = 11211; - } - } - - /* Check if the user has specified a weight for this server. */ - if(array_key_exists('weight', $server)) { - /* Get the weight and validate it. */ - $weight = (int)$server['weight']; - 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.'); - } - } else { - /* Use a default weight of 1. */ - $weight = 1; - } - - /* Check if the user has specified a timeout for this server. */ - if(array_key_exists('timeout', $server)) { - /* Get the timeout and validate it. */ - $timeout = (int)$server['timeout']; - if($timeout <= 0) { - throw new Exception('Invalid timeout for server in the' . - ' \'memcache_store.servers\' configuration option. The timeout is' . - ' supposed to be a positive integer.'); - } - } else { - /* Use a default timeout of 3 seconds. */ - $timeout = 3; - } - - /* Add this server to the Memcache object. */ - $memcache->addServer($hostname, $port, TRUE, $weight, $timeout); - } - - - /** - * This function takes in a list of servers belonging to a group and - * creates a Memcache object from the servers in the group. - * - * @param array $group Array of servers which should be created as a group. - * @return Memcache A Memcache object of the servers in the group - * - * @throws Exception If the servers configuration is invalid. - */ - private static function loadMemcacheServerGroup(array $group) { - - if(!class_exists('Memcache')) { - throw new Exception('Missing Memcache class. Is the memcache extension installed?'); - } - - /* Create the Memcache object. */ - $memcache = new Memcache(); - - /* 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. - */ - if(!is_int($index)) { - throw new Exception('Invalid index on element in the' . - ' \'memcache_store.servers\' configuration option. Perhaps you' . - ' have forgotten to add an array(...) around one of the server groups?' . - ' The invalid index was: ' . $index); - } - - /* - * Make sure that the server object is an array. Each server is an array with - * name-value pairs. - */ - if(!is_array($server)) { - 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.'); - } - - self::addMemcacheServer($memcache, $server); - } - - return $memcache; - } - - - /** - * This function gets a list of all configured memcache servers. This list is initialized based - * on the content of 'memcache_store.servers' in the configuration. - * - * @return Memcache[] Array with Memcache objects. - * - * @throws Exception If the servers configuration is invalid. - */ - private static function getMemcacheServers() { - - /* Check if we have loaded the servers already. */ - if(self::$serverGroups != NULL) { - return self::$serverGroups; - } - - /* Initialize the servers-array. */ - self::$serverGroups = array(); - - /* Load the configuration. */ - $config = SimpleSAML_Configuration::getInstance(); - - - $groups = $config->getArray('memcache_store.servers'); - - /* Iterate over all the groups in the 'memcache_store.servers' configuration option. */ - 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('Invalid index on element in the \'memcache_store.servers\'' . - ' configuration option. Perhaps you have forgotten to add an array(...)' . - ' around one of the server groups? The invalid index was: ' . $index); - } - - /* - * 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. - */ - 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.'); - } - - /* Parse and add this group to the server group list. */ - self::$serverGroups[] = self::loadMemcacheServerGroup($group); - } - - return self::$serverGroups; - } - - - /** - * This is a helper-function which returns the expire value of data - * we should store to the memcache servers. - * - * 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. - * 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. - */ - private static function getExpireTime() - { - /* Get the configuration instance. */ - $config = SimpleSAML_Configuration::getInstance(); - assert($config instanceof SimpleSAML_Configuration); - - /* Get the expire-value from the configuration. */ - $expire = $config->getInteger('memcache_store.expires', 0); - - /* It must be a positive integer. */ - if($expire < 0) { - throw new Exception('The value of \'memcache_store.expires\' in the' . - ' configuration can\'t be a negative integer.'); - } - - /* If the configuration option is 0, then we should - * return 0. This allows the user to specify that the data - * shouldn't expire. - */ - if($expire == 0) { - 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. - */ - $expireTime = time() + $expire; - - return $expireTime; - } - - - /** - * This function retrieves statistics about all memcache server groups. - * - * @return array Array with the names of each stat and an array with the value for each server group. - * - * @throws Exception If memcache server status couldn't be retrieved. - */ - public static function getStats() - { - $ret = array(); - - foreach(self::getMemcacheServers() as $sg) { - $stats = $sg->getExtendedStats(); - if($stats === FALSE) { - throw new Exception('Failed to get memcache server status.'); - } - - $stats = SimpleSAML\Utils\Arrays::transpose($stats); - - $ret = array_merge_recursive($ret, $stats); - } - - return $ret; - } - - - /** - * Retrieve statistics directly in the form returned by getExtendedStats, for - * all server groups. - * - * @return array An array with the extended stats output for each server group. - */ - public static function getRawStats() { - $ret = array(); - - foreach(self::getMemcacheServers() as $sg) { - $stats = $sg->getExtendedStats(); - $ret[] = $stats; - } - - return $ret; - } +class SimpleSAML_Memcache +{ + + /** + * Cache of the memcache servers we are using. + * + * @var Memcache[]|null + */ + private static $serverGroups = null; + + + /** + * 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. + */ + public static function get($key) + { + SimpleSAML_Logger::debug("loading key $key from memcache"); + + $latestInfo = null; + $latestTime = 0.0; + $latestData = null; + $mustUpdate = false; + + // search all the servers for the given id + foreach (self::getMemcacheServers() as $server) { + $serializedInfo = $server->get($key); + if ($serializedInfo === false) { + // either the server is down, or we don't have the value stored on that server + $mustUpdate = true; + continue; + } + + // unserialize the object + $info = unserialize($serializedInfo); + + /* + * Make sure that this is an array with two keys: + * - 'timestamp': The time the data was saved. + * - 'data': The data. + */ + if (!is_array($info)) { + SimpleSAML_Logger::warning( + 'Retrieved invalid data from a memcache server. Data was not an array.' + ); + continue; + } + if (!array_key_exists('timestamp', $info)) { + SimpleSAML_Logger::warning( + 'Retrieved invalid data from a memcache server. Missing timestamp.' + ); + continue; + } + if (!array_key_exists('data', $info)) { + SimpleSAML_Logger::warning( + 'Retrieved invalid data from a memcache server. Missing data.' + ); + continue; + } + + if ($latestInfo === null) { + // first info found + $latestInfo = $serializedInfo; + $latestTime = $info['timestamp']; + $latestData = $info['data']; + continue; + } + + if ($info['timestamp'] === $latestTime && $serializedInfo === $latestInfo) { + // this data matches the data from the other server(s) + continue; + } + + // different data from different servers. We need to update at least one of them to maintain sync. + $mustUpdate = true; + + // update if data in $info is newer than $latestData + if ($latestTime < $info['timestamp']) { + $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"); + 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"); + self::set($key, $latestData); + } + + return $latestData; + } + + + /** + * Save a key-value pair to the memcache servers. + * + * @param string $key The key of the data. + * @param mixed $value The value of the data. + * @param integer|null $expire The expiration timestamp of the data. + */ + public static function set($key, $value, $expire = null) + { + SimpleSAML_Logger::debug("saving key $key to memcache"); + $savedInfo = array( + 'timestamp' => microtime(true), + 'data' => $value + ); + + if ($expire === null) { + $expire = self::getExpireTime(); + } + + $savedInfoSerialized = serialize($savedInfo); + + // 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. + */ + public static function delete($key) + { + assert('is_string($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 + * Hostname or ip address to the memcache server. + * - port (optional) + * port number the memcache server is running on. This + * defaults to memcache.default_port if no value is given. + * The default value of memcache.default_port is 11211. + * - weight (optional) + * The weight of this server in the load balancing + * cluster. + * - timeout (optional) + * The timeout for contacting this server, in seconds. + * The default value is 3 seconds. + * + * @param Memcache $memcache The Memcache object we should add this server to. + * @param array $server An associative array with the configuration options for the server to add. + * + * @throws Exception If any configuration option for the server is invalid. + */ + private static function addMemcacheServer($memcache, $server) + { + // the hostname option is required + if (!array_key_exists('hostname', $server)) { + throw new Exception( + "hostname setting missing from server in the 'memcache_store.servers' configuration option." + ); + } + + $hostname = $server['hostname']; + + // 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". + ' supposed to be a string.' + ); + } + + // check if the user has specified a port number + if (array_key_exists('port', $server)) { + // get the port number from the array, and validate it + $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". + ' is supposed to be an integer between 0 and 65535.' + ); + } + } else { + // use the default port number from the ini-file + $port = (int) ini_get('memcache.default_port'); + if ($port <= 0 || $port > 65535) { + // invalid port number from the ini-file. fall back to the default + $port = 11211; + } + } + + // check if the user has specified a weight for this server + if (array_key_exists('weight', $server)) { + // get the weight and validate it + $weight = (int) $server['weight']; + 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.' + ); + } + } else { + // use a default weight of 1 + $weight = 1; + } + + // check if the user has specified a timeout for this server + if (array_key_exists('timeout', $server)) { + // get the timeout and validate it + $timeout = (int) $server['timeout']; + if ($timeout <= 0) { + throw new Exception( + "Invalid timeout for server in the 'memcache_store.servers' configuration option. The timeout is". + ' supposed to be a positive integer.' + ); + } + } else { + // use a default timeout of 3 seconds + $timeout = 3; + } + + // add this server to the Memcache object + $memcache->addServer($hostname, $port, true, $weight, $timeout); + } + + + /** + * This function takes in a list of servers belonging to a group and + * creates a Memcache object from the servers in the group. + * + * @param array $group Array of servers which should be created as a group. + * + * @return Memcache A Memcache object of the servers in the group + * + * @throws Exception If the servers configuration is invalid. + */ + private static function loadMemcacheServerGroup(array $group) + { + if (!class_exists('Memcache')) { + throw new Exception('Missing Memcache class. Is the memcache extension installed?'); + } + + // create the Memcache object + $memcache = new Memcache(); + + // 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 + if (!is_int($index)) { + throw new Exception( + "Invalid index on element in the 'memcache_store.servers' configuration option. Perhaps you". + ' have forgotten to add an array(...) around one of the server groups? The invalid index was: '. + $index + ); + } + + // make sure that the server object is an array. Each server is an array with name-value pairs + if (!is_array($server)) { + 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.' + ); + } + + self::addMemcacheServer($memcache, $server); + } + + return $memcache; + } + + + /** + * This function gets a list of all configured memcache servers. This list is initialized based + * on the content of 'memcache_store.servers' in the configuration. + * + * @return Memcache[] Array with Memcache objects. + * + * @throws Exception If the servers configuration is invalid. + */ + private static function getMemcacheServers() + { + // check if we have loaded the servers already + if (self::$serverGroups != null) { + return self::$serverGroups; + } + + // initialize the servers-array + self::$serverGroups = array(); + + // load the configuration + $config = SimpleSAML_Configuration::getInstance(); + + + $groups = $config->getArray('memcache_store.servers'); + + // iterate over all the groups in the 'memcache_store.servers' configuration option + 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( + "Invalid index on element in the 'memcache_store.servers'". + ' configuration option. Perhaps you have forgotten to add an array(...)'. + ' around one of the server groups? The invalid index was: '.$index + ); + } + + /* + * 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. + */ + 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.' + ); + } + + // parse and add this group to the server group list + self::$serverGroups[] = self::loadMemcacheServerGroup($group); + } + + return self::$serverGroups; + } + + + /** + * This is a helper-function which returns the expire value of data + * we should store to the memcache servers. + * + * 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. + * 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. + */ + private static function getExpireTime() + { + // get the configuration instance + $config = SimpleSAML_Configuration::getInstance(); + assert($config instanceof SimpleSAML_Configuration); + + // get the expire-value from the configuration + $expire = $config->getInteger('memcache_store.expires', 0); + + // it must be a positive integer + if ($expire < 0) { + throw new Exception( + "The value of 'memcache_store.expires' in the configuration can't be a negative integer." + ); + } + + /* If the configuration option is 0, then we should return 0. This allows the user to specify that the data + * shouldn't expire. + */ + if ($expire == 0) { + 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. + */ + $expireTime = time() + $expire; + + return $expireTime; + } + + + /** + * This function retrieves statistics about all memcache server groups. + * + * @return array Array with the names of each stat and an array with the value for each server group. + * + * @throws Exception If memcache server status couldn't be retrieved. + */ + public static function getStats() + { + $ret = array(); + + foreach (self::getMemcacheServers() as $sg) { + $stats = $sg->getExtendedStats(); + if ($stats === false) { + throw new Exception('Failed to get memcache server status.'); + } + + $stats = SimpleSAML\Utils\Arrays::transpose($stats); + + $ret = array_merge_recursive($ret, $stats); + } + + return $ret; + } + + + /** + * Retrieve statistics directly in the form returned by getExtendedStats, for + * all server groups. + * + * @return array An array with the extended stats output for each server group. + */ + public static function getRawStats() + { + $ret = array(); + + foreach (self::getMemcacheServers() as $sg) { + $stats = $sg->getExtendedStats(); + $ret[] = $stats; + } + + return $ret; + } } -- GitLab