From 332aed88eb317410a9ef02766fbdc4e6ee4e3675 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Wed, 14 May 2008 11:06:30 +0000
Subject: [PATCH] Changed MemcacheStore to use the Memcache class.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@558 44740490-163a-0410-bde0-09ae8108e29a
---
 lib/SimpleSAML/MemcacheStore.php | 560 ++++---------------------------
 1 file changed, 59 insertions(+), 501 deletions(-)

diff --git a/lib/SimpleSAML/MemcacheStore.php b/lib/SimpleSAML/MemcacheStore.php
index 90c7cd1d9..1cc9206f8 100644
--- a/lib/SimpleSAML/MemcacheStore.php
+++ b/lib/SimpleSAML/MemcacheStore.php
@@ -1,31 +1,12 @@
 <?php 
 
-/* We need access to the configuration from config/config.php. */
-require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Configuration.php');
-
-/* For access to SimpleSAML_Utilities::transposeArray. */
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php');
-
-/* For the interface that objects can export to allow us to see if it
- * is modified or not.
- */
 require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/ModifiedInfo.php');
+require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Memcache.php');
 
-/*
- * This file is part of SimpleSAMLphp. See the file COPYING in the
- * root of the distribution for licence information.
- *
- * This file implements a storage class which stores data to one or more
- * groups memcache servers.
- *
- * The goals of this storage class is to provide failover, redudancy and load
- * balancing. This is accomplished by storing the data object to several
- * groups of memcache servers. Each data object is replicated to every group
- * of memcache servers, but it is only stored to one server in each group.
- *
- * For this code to work correctly, all web servers accessing the data must
- * have the same clock (as measured by the time()-function). Different clock
- * values will lead to incorrect behaviour.
+/**
+ * This class provides a class with behaviour similar to the $_SESSION variable.
+ * Data is automatically saved on exit.
  *
  * @author Olav Morken, UNINETT AS.
  * @package simpleSAMLphp
@@ -34,23 +15,16 @@ require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSA
 class SimpleSAML_MemcacheStore {
 
 
-	/* This variable contains the last commit time of this object.
-	 * This is used to determine which of the memcache servers contain
-	 * the newest version.
-	 *
-	 * This variable is serialized.
-	 */
-	private $lastCommitTime = NULL;
-
-
-	/* This variable contains the id for this data.
+	/**
+	 * This variable contains the id for this data.
 	 *
 	 * This variable is serialized.
 	 */
 	private $id = NULL;
 
 
-	/* This variable contains an array with all key-value pairs stored
+	/**
+	 * This variable contains an array with all key-value pairs stored
 	 * in this object.
 	 *
 	 * This variable is serialized.
@@ -58,7 +32,8 @@ class SimpleSAML_MemcacheStore {
 	private $data = NULL;
 
 
-	/* This variable contains the serialized data which is currently
+	/**
+	 * This variable contains the serialized data which is currently
 	 * stored on the memcache servers. By comparing the data which is
 	 * stored against the current data, we can determine whether we
 	 * should update the data.
@@ -71,115 +46,47 @@ class SimpleSAML_MemcacheStore {
 	private $savedData = NULL;
 
 
-	/* Private cache of the memcache servers we are using. */
-	private static $serverGroups = NULL;
-
 
-
-	/* This function is used to find an existing storage object. It will
-	 * return NULL if no storage object with the given id is found.
-	 *
-	 * Parameters:
-	 *  $id        The id of the storage object we are looking for. A id
-	 *             consists of lowercase alphanumeric characters.
+	/**
+	 * This function is used to find an existing storage object. It will return NULL if no storage object
+	 * with the given id is found.
 	 *
-	 * Returns:
-	 *  The corresponding MemcacheStorage object if the data is found
-	 *  or NULL if it isn't found.
+	 * @param $id  The id of the storage object we are looking for. A id consists of lowercase
+	 *             alphanumeric characters.
+	 * @return The corresponding MemcacheStorage object if the data is found or NULL if it isn't found.
 	 */
 	public static function find($id) {
 		assert(self::isValidID($id));
 
-		$mustUpdate = FALSE;
-		$latest = NULL;
-		$latestSerializedValue = NULL;
+		$serializedData = SimpleSAML_Memcache::get($id);
+		$data = unserialize($serializedData);
 
-		/* Search all the servers for the given id. */
-		foreach(self::getMemcacheServers() as $server) {
-			$serializedValue = $server->get($id);
-			if($serializedValue === FALSE) {
-				/* Either the server is down, or we don't have
-				 * the value stored on that server.
-				 */
-				$mustUpdate = TRUE;
-				continue;
-			}
-
-			/* Deserialize the object. */
-			$v = unserialize($serializedValue);
-
-			/* Make sure that the deserialized object is of the
-			 * correct type.
-			 */
-			if(!($v instanceof self)) {
-				$e = 'We retrieved an object from the' .
-				     ' memcache server which wasn\'t an' .
-				     ' instance of a MemcacheStore object.' .
-				     ' This should never happen, and is a ' .
-				     ' sign that the MemcacheStore may be ' .
-				     ' unreliable.';
-				error_log($e);
-				die($e);
-			}
-
-			if($latest === NULL) {
-				$latest = $v;
-				$latestSerializedValue = $serializedValue;
-				continue;
-			}
-
-			if($latest->lastCommitTime == $v->lastCommitTime) {
-				/* They were committed at the same time. Assume
-				 * that they are equal.
-				 */
-				continue;
-			}
-
-			/* They are different. We need to update at least one
-			 * of them to maintain synchronization.
-			 */
-
-			$mustUpdate = TRUE;
-
-			/* Update $latest if $v is newer than $latest. */
-			if($latest->lastCommitTime < $v->lastCommitTime) {
-				$latest = $v;
-				$latestSerializedValue = $serializedValue;
-			}
+		if($data === NULL) {
+			return $NULL;
 		}
 
-		/* Check if we found any stored object matching the id. */
-		if($latest === NULL) {
+		if(!($data instanceof self)) {
+			error_log('Retrieved key from memcache did not contain a MemcacheStore object.');
 			return NULL;
 		}
 
-		/* If we don't need to update this data object, then we can
-		 * store the serialized value in the MemcacheStore object. If
-		 * we need to update this object, then we don't store the
-		 * serialized value.
-		 */
-		if($mustUpdate === FALSE) {
-			$latest->savedData = $latestSerializedValue;
-		}
+		$data->savedData = $serializedData;
 
 		/* Add a call to save the data when we exit. */
-		register_shutdown_function(array($latest, 'save'));
+		register_shutdown_function(array($data, 'save'));
 
-		return $latest;
+		return $data;
 	}
 
 
-	/* This constructor is used to create a new storage object. The storage
-	 * object will be created with the specified id and the initial
-	 * content passed in the data argument.
+	/**
+	 * This constructor is used to create a new storage object. The storage object will be created with the
+	 * specified id and the initial content passed in the data argument.
 	 *
-	 * If there exists a storage object with the specified id, then it will
-	 * be overwritten.
+	 * If there exists a storage object with the specified id, then it will be overwritten.
 	 *
-	 * Parameters:
-	 *  $id          The id of the storage object.
-	 *  $data        An array containing the initial data of the storage
-	 *               object.
+	 * @param $id    The id of the storage object.
+	 * @param $data  An array containing the initial data of the storage object.
 	 */
 	public function __construct($id, $data = array()) {
 		/* Validate arguments. */
@@ -194,22 +101,22 @@ class SimpleSAML_MemcacheStore {
 	}
 
 
-	/* This magic function is called on serialization of this class.
-	 * It returns a list of the names of the variables which should be
-	 * serialized.
+	/**
+	 * This magic function is called on serialization of this class. It returns a list of the names of the
+	 * variables which should be serialized.
+	 *
+	 * @return List of variables which should be serialized.
 	 */
 	private function __sleep() {
-		return array('lastCommitTime', 'id', 'data');
+		return array('id', 'data');
 	}
 
 
-	/* This function retrieves the specified key from this storage object.
-	 *
-	 * Parameters:
-	 *  $key    The key we should retrieve the value of.
+	/**
+	 * This function retrieves the specified key from this storage object.
 	 *
-	 * Returns:
-	 *  The value of the specified key, or NULL of the key wasn't found.
+	 * @param $key  The key we should retrieve the value of.
+	 * @return The value of the specified key, or NULL of the key wasn't found.
 	 */
 	public function get($key) {
 		if(!array_key_exists($key, $this->data)) {
@@ -220,12 +127,12 @@ class SimpleSAML_MemcacheStore {
 	}
 
 
-	/* This function sets the specified key to the specified value in this
+	/**
+	 * This function sets the specified key to the specified value in this
 	 * storage object.
 	 *
-	 * Parameters:
-	 *  $key    The key we should set.
-	 *  $value  The value we should set the key to.
+	 * @param $key    The key we should set.
+	 * @param $value  The value we should set the key to.
 	 */
 	public function set($key, $value) {
 		$this->data[$key] = $value;
@@ -238,15 +145,15 @@ class SimpleSAML_MemcacheStore {
 	}
 
 
-	/* This function determines whether we need to update the data which
+	/**
+	 * This function determines whether we need to update the data which
 	 * is stored on the memcache servers.
 	 *
 	 * If we are unable to detect a change, then we will serialize the
 	 * class and compare this to the data we have cached. We do this to
 	 * determine if any of the references have changed.
 	 *
-	 * Returns:
-	 *  TRUE if this object needs an update, FALSE if not.
+	 * @return TRUE if this object needs an update, FALSE if not.
 	 */
 	private function needUpdate() {
 		/* If $savedData is NULL, then we don't have any data stored
@@ -308,299 +215,35 @@ class SimpleSAML_MemcacheStore {
 			return FALSE;
 		}
 
-		/* We need to store the updated value to the servers. */
 		return TRUE;
 	}
 
 
-	/* This function stores this storage object to the memcache servers.
+	/**
+	 * This function stores this storage object to the memcache servers.
 	 */
 	public function save() {
 		/* First, chech whether we need to store new data. */
 		if(!$this->needUpdate()) {
-			/* This object is unchanged - we don't need to
-			 * commit.
-			 */
+			/* This object is unchanged - we don't need to commit. */
 			return;
 		}
 
-		/* Update the last-commit timestamp. */
-		$this->lastCommitTime = time();
-
-		/* Calculate the value we should store on the servers. */
+		/* Serialize this object. */
 		$this->savedData = serialize($this);
 
-		/* Store this object to all groups of memcache servers. */
-		foreach(self::getMemcacheServers() as $server) {
-			$server->set($this->id, $this->savedData, 0,
-			             self::getExpireTime());
-		}
-	}
-
-
-	/* This function adds a server from the 'memcache_store.servers'
-	 * configuration option to a Memcache object.
-	 *
-	 * Parameters:
-	 *  $memcache The Memcache object we should add this server to.
-	 *  $server   The server we should parse. This 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.
-	 */
-	private static function addMemcacheServer($memcache, $server) {
-
-		/* The hostname option is required. */
-		if(!array_key_exists('hostname', $server)) {
-			$e = 'hostname setting missing from server in the' .
-			     ' \'memcache_store.servers\' configuration' .
-			     ' option.';
-			error_log($e);
-			die($e);
-		}
-
-		$hostname = $server['hostname'];
-
-		/* The hostname must be a valid string. */
-		if(!is_string($hostname)) {
-			$e = 'Invalid hostname for server in the' .
-		             ' \'memcache_store.servers\' configuration' .
-			     ' option. The hostname is supposed to be a' .
-			     ' string.';
-			error_log($e);
-			die($e);
-		}
-
-		/* 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)) {
-				$e = 'Invalid port for server in the' .
-				     ' \'memcache_store.servers\'' .
-				     ' configuration option. The port number' .
-				     ' is supposed to be an integer between' .
-				     ' 0 and 65535.';
-				error_log($e);
-				die($e);
-			}
-		} 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) {
-				$e = 'Invalid weight for server in the' .
-				     ' \'memcache_store.servers\'' .
-				     ' configuration option. The weight is' .
-				     ' supposed to be a positive integer.';
-				error_log($e);
-				die($e);
-			}
-		} 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) {
-				$e = 'Invalid timeout for server in the' .
-				     ' \'memcache_store.servers\'' .
-				     ' configuration option. The timeout is' .
-				     ' supposed to be a positive integer.';
-				error_log($e);
-				die($e);
-			}
-		} 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.
-	 *
-	 * Parameters:
-	 *  $group   Array of servers. Each server is represented by one array
-	 *           with the hostname (and optionally port number, timeout,
-	 *           ...). See the addMemcacheServer function for more
-	 *           information.
-	 *
-	 * Returns:
-	 *  A Memcache object of the servers in the group.
-	 */
-	private static function loadMemcacheServerGroup($group) {
-		/* Create the Memcache object. */
-		$memcache = new Memcache();
-		if($memcache == NULL) {
-			$e = 'Unable to create an instance of a Memcache' .
-			     ' object. Is the memcache extension' .
-			     ' installed?';
-			error_log($e);
-			die($e);
-		}
-
-		/* 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)) {
-				$e = '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;
-				error_log($e);
-				die($e);
-			}
-
-			/* Make sure that the server object is an array. Each
-			 * server is an array with name-value pairs.
-			 */
-			if(!is_array($server)) {
-				$e = 'Invalid value for the server with' .
-				     ' index ' . $index . '. Remeber that' .
-				     ' the \'memcache_store.servers\'' .
-				     ' configuration option contains an' .
-				     ' array of arrays of arrays.';
-				error_log($e);
-				die($e);
-			}
-
-			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.
-	 *
-	 * Returns:
-	 *  Array with Memcache objects.
-	 */
-	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();
-		assert($config instanceof SimpleSAML_Configuration);
-
-
-		$groups = $config->getValue('memcache_store.servers');
-
-		/* Validate the 'memcache_store.servers' configuration
-		 * option.
-		 */
-		if(is_null($groups)) {
-			$e = 'Unable to get value of the' .
-			     ' \'memcache_store.servers\' configuration' .
-			     ' option.';
-			error_log($e);
-			die($e);
-		}
-		if(!is_array($groups)) {
-			$e = 'The value of the \'memcache_store.servers\'' .
-			     ' configuration option isn\'t an array.';
-			error_log($e);
-			die($e);
-		}
-
-		/* 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)) {
-				$e = '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;
-				error_log($e);
-				die($e);
-			}
-
-			/* 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)) {
-				$e = 'Invalid value for the server with' .
-				     ' index ' . $index . '. Remeber that' .
-				     ' the \'memcache_store.servers\'' .
-				     ' configuration option contains an' .
-				     ' array of arrays of arrays.';
-				error_log($e);
-				die($e);
-			}
-
-			/* Parse and add this group to the server group list.
-			 */
-			self::$serverGroups[] =
-				self::loadMemcacheServerGroup($group);
-		}
-
-		return self::$serverGroups;		
+		/* Write to the memcache servers. */
+		SimpleSAML_Memcache::set($this->id, $this->savedData);
 	}
 
 
-	/* This function determines whether the argument is a valid id.
+	/**
+	 * This function determines whether the argument is a valid id.
 	 * A valid id is a string containing lowercase alphanumeric
 	 * characters.
 	 *
-	 * Parameters:
-	 *  $id     The id we should validate.
-	 *
-	 * Returns:
-	 *  TRUE if the id is valid, FALSE otherwise.
+	 * @param $id  The id we should validate.
+	 * @return  TRUE if the id is valid, FALSE otherwise.
 	 */
 	private static function isValidID($id) {
 		if(!is_string($id)) {
@@ -618,90 +261,5 @@ class SimpleSAML_MemcacheStore {
 		return TRUE;
 	}
 
-
-	/* 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.
-	 *
-	 * Returns:
-	 *  The value which should be passed in the set(...) calls to the
-	 *  memcache objects.
-	 */
-	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->getValue('memcache_store.expires');
-
-		/* If 'memcache_store.expires' isn't defined in the
-		 * configuration, then we will use 0 as the expire parameter.
-		 */
-		if($expire === NULL) {
-			return 0;
-		}
-
-		/* The 'memcache_store.expires' option must be an integer. */
-		if(!is_integer($expire)) {
-			$e = 'The value of \'memcache_store.expires\' in the' .
-			     ' configuration must be a valid integer.';
-			error_log($e);
-			die($e);
-		}
-
-		/* It must be a positive integer. */
-		if($expire < 0) {
-			$e = 'The value of \'memcache_store.expires\' in the' .
-			     ' configuration can\'t be a negative integer.';
-			error_log($e);
-			die($e);
-		}
-
-		/* 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 with the names of each stat and an array with the value for each
-	 *         server group.
-	 */
-	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_Utilities::transposeArray($stats);
-
-			$ret = array_merge_recursive($ret, $stats);
-		}
-
-		return $ret;
-	}
 }
-?>
+?>
\ No newline at end of file
-- 
GitLab