Initial support for memcached.
authorBrion Vibber <brion@users.mediawiki.org>
Mon, 11 Aug 2003 13:53:20 +0000 (13:53 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Mon, 11 Aug 2003 13:53:20 +0000 (13:53 +0000)
Includes PHP MemCachedClient 1.0.8 by Ryan Gilfether, under GPL license.

docs/memcached.doc [new file with mode: 0644]
docs/php-memcached/ChangeLog [new file with mode: 0644]
docs/php-memcached/Documentation [new file with mode: 0644]
includes/DefaultSettings.php
includes/LinkCache.php
includes/MemCachedClient.inc.php [new file with mode: 0644]
includes/Setup.php
includes/User.php
includes/UserTalkUpdate.php

diff --git a/docs/memcached.doc b/docs/memcached.doc
new file mode 100644 (file)
index 0000000..c79865d
--- /dev/null
@@ -0,0 +1,105 @@
+memcached support for MediaWiki:
+
+From ca August 2003, MediaWiki has optional support for memcached, a
+"high-performance, distributed memory object caching system".
+For general information on it, see: http://www.danga.com/memcached/
+
+Memcached is likely more trouble than a small site will need, but
+for a larger site with heavy load, like Wikipedia, it should help
+lighten the load on the database servers by caching data and objects
+in memory.
+
+== Requirements ==
+
+* PHP must be compiled with --enable-sockets
+
+* libevent: http://www.monkey.org/~provos/libevent/
+  (as of 2003-08-11, 0.7a is current)
+
+* optionally, epoll-rt patch for Linux kernel:
+  http://www.xmailserver.org/linux-patches/nio-improve.html
+
+* memcached: http://www.danga.com/memcached/download.bml
+  (as of this writing, 1.1.9 is current)
+  
+Memcached and libevent are under BSD-style licenses.
+
+The server should run on Linux and other Unix-like systems... you
+can run multiple servers on one machine or on multiple machines on
+a network; storage can be distributed across multiple servers, and
+multiple web servers can use the same cache cluster.
+
+
+********************* W A R N I N G ! ! ! ! ! ***********************
+Memcached has no security or authentication. Please ensure that your
+server is appropriately firewalled, and that the port(s) used for
+memcached servers are not publicly accessible. Otherwise, anyone on
+the internet can put data into and read data from your cache.
+
+An attacker familiar with MediaWiki internals could use this to give
+themselves developer access and delete all data from the wiki's
+database, as well as getting all users' password hashes and e-mail
+addresses.
+********************* W A R N I N G ! ! ! ! ! ***********************
+
+== Setup ==
+
+If you want to start small, just run one memcached on your web
+server:
+
+  memcached -d -l 127.0.0.1 -p 11000 -m 64
+
+(to run in daemon mode, accessible only via loopback interface,
+on port 11000, using up to 64MB of memory)
+
+In your LocalSettings.php file, set:
+
+  $wgUseMemCached = true;
+  $wgMemCachedServers = array( "127.0.0.1:11000" );
+
+The wiki should then use memcached to cache various data. To use
+multiple servers (physically separate boxes or multiple caches
+on one machine on a large-memory x86 box), just add more items
+to the array. To increase the weight of a server (say, because
+it has twice the memory of the others and you want to spread
+usage evenly), make its entry a subarray:
+
+  $wgMemCachedServers = array(
+    "127.0.0.1:11000", # one gig on this box
+    array("192.168.0.1:11000", 2) # two gigs on the other box
+  );
+
+
+== PHP client for memcached ==
+
+As of this writing, MediaWiki includes version 1.0.8 of the PHP
+memcached client by Ryan Gilfether <hotrodder@rocketmail.com>.
+You'll find some documentation for it in the 'php-memcached'
+subdirectory under the present one.
+
+We intend to track updates, but if you want to check for the lastest
+released version, see http://www.danga.com/memcached/apis.bml
+
+If you don't set $wgUseMemCached, we still create a MemCacheClient,
+but requests to it are no-ops and we always fall through to the
+database. If the cache daemon can't be contacted, it should also
+disable itself fairly smoothly.
+
+== Keys used ==
+
+User:
+       key: $wgDBname:user:user_id:$sId
+       ex: wikidb:user:user_id:51
+       stores: instance of class User
+       set in: User::loadFromSession()
+       cleared by: User::saveSettings()
+
+LinkCache:
+       key: $wgDBname:linkcache:title:$title
+       ex: wikidb:linkcache:title:Wikipedia:Welcome,_Newcomers!
+       stores: cur_id of page, or 0 if page does not exist
+       set in: LinkCache::addLink()
+       cleared by: LinkCache::clearBadLink()
+               should be cleared on page deletion and rename
+
+... more to come ...
diff --git a/docs/php-memcached/ChangeLog b/docs/php-memcached/ChangeLog
new file mode 100644 (file)
index 0000000..bfabb49
--- /dev/null
@@ -0,0 +1,36 @@
+Release 1.0.8
+-------------
+* whitespace/punctuation/wording cleanups
+
+Release 1.0.7
+-------------
+* added 3 functions which handle error reporting
+  error() - returns error number of last error generated, else returns 0
+  error_string() - returns a string description of error number retuned
+  error_clear() - clears the last error number and error string
+* removed call to preg_match() in _loaditems()
+* only non-scalar values are serialize() before being
+  sent to the server
+* added the optional timestamp argument for delete()
+  read Documentation file for details
+* PHPDocs/PEAR style comments added
+* abstract debugging (Brion Vibber <brion@pobox.com>)
+       
+Release 1.0.6
+-------------
+* removed all array_push() calls
+* applied patch provided by Stuart Herbert<stuart@gentoo.org>
+  corrects possible endless loop. Available at
+  http://bugs.gentoo.org/show_bug.cgi?id=25385
+* fixed problem with storing large binary files
+* added more error checking, specifically on all socket functions
+* added support for the INCR and DECR commands
+  which increment or decrement a value stored in MemCached
+* Documentation removed from source and is now available
+  in the file Documentation
+
+Release 1.0.4
+-------------
+* initial release, version numbers kept
+  in sync with MemCached version
+* capable of storing any datatype in MemCached
diff --git a/docs/php-memcached/Documentation b/docs/php-memcached/Documentation
new file mode 100644 (file)
index 0000000..0d09d17
--- /dev/null
@@ -0,0 +1,257 @@
+Ryan Gilfether <hotrodder@rocketmail.com>
+http://www.gilfether.com
+This module is Copyright (c) 2003 Ryan Gilfether.
+All rights reserved.
+
+You may distribute under the terms of the GNU General Public License
+This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
+
+See the memcached website: http://www.danga.com/memcached/
+
+
+// Takes one parameter, a array of options.  The most important key is
+// options["servers"], but that can also be set later with the set_servers()
+// method.  The servers must be an array of hosts, each of which is
+// either a scalar of the form <10.0.0.10:11211> or an array of the
+// former and an integer weight value.  (the default weight if
+// unspecified is 1.)  It's recommended that weight values be kept as low
+// as possible, as this module currently allocates memory for bucket
+// distribution proportional to the total host weights.
+// $options["debug"] turns the debugging on if set to true
+MemCachedClient::MemCachedClient($options);
+
+// sets up the list of servers and the ports to connect to
+// takes an array of servers in the same format as in the constructor
+MemCachedClient::set_servers($servers);
+
+// Retrieves a key from the memcache.  Returns the value (automatically
+// unserialized, if necessary) or FALSE if it fails.
+// The $key can optionally be an array, with the first element being the
+// hash value, if you want to avoid making this module calculate a hash
+// value.  You may prefer, for example, to keep all of a given user's
+// objects on the same memcache server, so you could use the user's
+// unique id as the hash value.
+// Possible errors set are:
+//             MC_ERR_GET
+MemCachedClient::get($key);
+
+// just like get(), but takes an array of keys, returns FALSE on error
+// Possible errors set are:
+//             MC_ERR_NOT_ACTIVE
+MemCachedClient::get_multi($keys)
+
+// Unconditionally sets a key to a given value in the memcache.  Returns true
+// if it was stored successfully.
+// The $key can optionally be an arrayref, with the first element being the
+// hash value, as described above.
+// returns TRUE on success else FALSE
+// Possible errors set are:
+//             MC_ERR_NOT_ACTIVE
+//             MC_ERR_GET_SOCK
+//             MC_ERR_SOCKET_WRITE
+//             MC_ERR_SOCKET_READ
+//             MC_ERR_SET
+MemCachedClient::set($key, $value, $exptime);
+
+// Like set(), but only stores in memcache if the key doesn't already exist.
+// returns TRUE on success else FALSE
+// Possible errors set are:
+//             MC_ERR_NOT_ACTIVE
+//             MC_ERR_GET_SOCK
+//             MC_ERR_SOCKET_WRITE
+//             MC_ERR_SOCKET_READ
+//             MC_ERR_SET
+MemCachedClient::add($key, $value, $exptime);
+
+// Like set(), but only stores in memcache if the key already exists.
+// returns TRUE on success else FALSE
+// Possible errors set are:
+//             MC_ERR_NOT_ACTIVE
+//             MC_ERR_GET_SOCK
+//             MC_ERR_SOCKET_WRITE
+//             MC_ERR_SOCKET_READ
+//             MC_ERR_SET
+MemCachedClient::replace($key, $value, $exptime);
+
+// removes the key from the MemCache
+// $time is the amount of time in seconds (or Unix time) until which
+// the client wishes the server to refuse "add" and "replace" commands
+// with this key. For this amount of item, the item is put into a
+// delete queue, which means that it won't possible to retrieve it by
+// the "get" command, but "add" and "replace" command with this key
+// will also fail (the "set" command will succeed, however). After the
+// time passes, the item is finally deleted from server memory.
+// The parameter $time is optional, and, if absent, defaults to 0
+// (which means that the item will be deleted immediately and further
+// storage commands with this key will succeed).
+// returns TRUE on success else returns FALSE
+// Possible errors set are:
+//             MC_ERR_NOT_ACTIVE
+//             MC_ERR_GET_SOCK
+//             MC_ERR_SOCKET_WRITE
+//             MC_ERR_SOCKET_READ
+//             MC_ERR_DELETE
+MemCachedClient::delete($key, $time = 0);
+
+// Sends a command to the server to atomically increment the value for
+// $key by $value, or by 1 if $value is undefined.  Returns FALSE if $key
+// doesn't exist on server, otherwise it returns the new value after
+// incrementing.  Value should be zero or greater.  Overflow on server
+// is not checked.  Be aware of values approaching 2**32.  See decr.
+// Possible errors set are:
+//             MC_ERR_NOT_ACTIVE
+//             MC_ERR_GET_SOCK
+//             MC_ERR_SOCKET_WRITE
+//             MC_ERR_SOCKET_READ
+// returns new value on success, else returns FALSE
+// ONLY WORKS WITH NUMERIC VALUES
+MemCachedClient::incr($key[, $value]);
+
+// Like incr, but decrements.  Unlike incr, underflow is checked and new
+// values are capped at 0.  If server value is 1, a decrement of 2
+// returns 0, not -1.
+// Possible errors set are:
+//             MC_ERR_NOT_ACTIVE
+//             MC_ERR_GET_SOCK
+//             MC_ERR_SOCKET_WRITE
+//             MC_ERR_SOCKET_READ
+// returns new value on success, else returns FALSE
+// ONLY WORKS WITH NUMERIC VALUES
+MemCachedClient::decr($key[, $value]);
+
+// disconnects from all servers
+MemCachedClient::disconnect_all();
+
+// if $do_debug is set to true, will print out
+// debugging info, else debug is turned off
+MemCachedClient::set_debug($do_debug);
+
+// remove all cached hosts that are no longer good
+MemCachedClient::forget_dead_hosts();
+
+// When a function returns FALSE, an error code is set.
+// This funtion will return the error code.
+// See error_string()
+// returns last error code set
+MemCachedClient::error()
+
+// Returns a string describing the error set in error()
+// See error()
+// returns a string describing the error code given
+MemCachedClient::error_string()
+
+// Resets the error number and error string
+MemCachedClient::error_clear()
+
+Error codes are as follows:
+MC_ERR_NOT_ACTIVE              // no active servers
+MC_ERR_SOCKET_WRITE            // socket_write() failed
+MC_ERR_SOCKET_READ             // socket_read() failed
+MC_ERR_SOCKET_CONNECT  // failed to connect to host
+MC_ERR_DELETE                  // delete() did not recieve DELETED command
+MC_ERR_HOST_FORMAT             // sock_to_host() invalid host format
+MC_ERR_HOST_DEAD               // sock_to_host() host is dead
+MC_ERR_GET_SOCK                        // get_sock() failed to find a valid socket
+MC_ERR_SET                             // _set() failed to receive the STORED response
+MC_ERR_LOADITEM_HEADER // _load_items failed to receive valid data header
+MC_ERR_LOADITEM_END            // _load_items failed to receive END response
+MC_ERR_LOADITEM_BYTES  // _load_items bytes read larger than bytes available
+MC_ERR_GET                             // failed to get value associated with key
+
+
+
+EXAMPLE:
+<?php
+require("MemCachedClient.inc.php");
+
+// set the servers, with the last one having an interger weight value of 3
+$options["servers"] = array("10.0.0.15:11000","10.0.0.16:11001",array("10.0.0.17:11002", 3));
+$options["debug"] = false;
+
+$memc = new MemCachedClient($options);
+
+
+/***********************
+ * STORE AN ARRAY
+ ***********************/
+$myarr = array("one","two", 3);
+$memc->set("key_one", $myarr);
+$val = $memc->get("key_one");
+print $val[0]."\n";    // prints 'one'
+print $val[1]."\n";    // prints 'two'
+print $val[2]."\n";    // prints 3
+
+
+print "\n";
+
+
+/***********************
+ * STORE A CLASS
+ ***********************/
+class tester
+{
+       var $one;
+       var $two;
+       var $three;
+}
+
+$t = new tester;
+$t->one = "one";
+$t->two = "two";
+$t->three = 3;
+$memc->set("key_two", $t);
+$val = $memc->get("key_two");
+print $val->one."\n";
+print $val->two."\n";
+print $val->three."\n";
+
+
+print "\n";
+
+
+/***********************
+ * STORE A STRING
+ ***********************/
+$memc->set("key_three", "my string");
+$val = $memc->get("key_three");
+print $val;            // prints 'my string'
+
+$memc->delete("key_one");
+$memc->delete("key_two");
+$memc->delete("key_three");
+
+$memc->disconnect_all();
+
+
+
+print "\n";
+
+
+/***********************
+ * STORE A BINARY FILE
+ ***********************/
+
+ // first read the file and save it in memcache
+$fp = fopen( "./image.jpg", "rb" ) ;
+if ( !$fp )
+{
+       print "Could not open ./file.dat!\n" ;
+       exit ;
+}
+$data = fread( $fp, filesize( "./image.jpg" ) ) ;
+fclose( $fp ) ;
+print "Data length is " . strlen( $data ) . "\n" ;
+$memc->set( "key", $data ) ;
+
+// now open a file for writing and write the data
+// retrieved from memcache
+$fp = fopen("./test.jpg","wb");
+$data = $memc->get( "key" ) ;
+print "Data length is " . strlen( $data ) . "\n" ;
+fwrite($fp,$data,strlen( $data ));
+fclose($fp);
+
+
+?>
+
+
index 98fcbde..031c7e5 100644 (file)
@@ -38,6 +38,13 @@ $wgDBminWordLen     = 4;
 $wgDBtransactions      = false; # Set to true if using InnoDB tables
 $wgDBmysql4                    = false; # Set to true to use enhanced fulltext search
 
+# memcached settings
+# See docs/memcached.doc
+#
+$wgUseMemCached     = false;
+$wgMemCachedServers = array( "127.0.0.1:11000" );
+$wgMemCachedDebug   = false;
+
 # Language settings
 #
 $wgLanguageCode     = "en";
index 0955edd..62bf9de 100644 (file)
@@ -62,6 +62,8 @@ class LinkCache {
                if ( isset( $index ) ) {
                        unset( $this->mBadLinks[$index] );
                }
+               global $wgMemc, $wgDBname;
+               $wgMemc->delete( "$wgDBname:linkcache:title:$title" );
        }
 
        function suspend() { $this->mActive = false; }
@@ -76,6 +78,7 @@ class LinkCache {
                $id = $this->getGoodLinkID( $title );
                if ( 0 != $id ) { return $id; }
 
+               global $wgMemc, $wgDBname;
                wfProfileIn( "LinkCache::addLink-checkdatabase" );
 
                $nt = Title::newFromDBkey( $title );
@@ -83,15 +86,20 @@ class LinkCache {
                $t = $nt->getDBkey();
 
                if ( "" == $t ) { return 0; }
-               $sql = "SELECT HIGH_PRIORITY cur_id FROM cur WHERE cur_namespace=" .
-                 "{$ns} AND cur_title='" . wfStrencode( $t ) . "'";
-               $res = wfQuery( $sql, "LinkCache::addLink" );
 
-               if ( 0 == wfNumRows( $res ) ) {
-                       $id = 0;
-               } else {
-                       $s = wfFetchObject( $res );
-                       $id = $s->cur_id;
+               $id = $wgMemc->get( $key = "$wgDBname:linkcache:title:$title" );
+               if( $id === FALSE ) {
+                       $sql = "SELECT HIGH_PRIORITY cur_id FROM cur WHERE cur_namespace=" .
+                         "{$ns} AND cur_title='" . wfStrencode( $t ) . "'";
+                       $res = wfQuery( $sql, "LinkCache::addLink" );
+
+                       if ( 0 == wfNumRows( $res ) ) {
+                               $id = 0;
+                       } else {
+                               $s = wfFetchObject( $res );
+                               $id = $s->cur_id;
+                       }
+                       $wgMemc->add( $key, $id, time()+3600 );
                }
                if ( 0 == $id ) { $this->addBadLink( $title ); }
                else { $this->addGoodLink( $id, $title ); }
@@ -200,5 +208,6 @@ class LinkCache {
                $this->mBadLinks = array();
                $this->mImageLinks = array();
        }
+       
 }
 ?>
diff --git a/includes/MemCachedClient.inc.php b/includes/MemCachedClient.inc.php
new file mode 100644 (file)
index 0000000..b1a35a2
--- /dev/null
@@ -0,0 +1,1104 @@
+<?php
+/*
+ * MemCached PHP client
+ * Copyright (c) 2003
+ * Ryan Gilfether <hotrodder@rocketmail.com>
+ * http://www.gilfether.com
+ *
+ * Originally translated from Brad Fitzpatrick's <brad@danga.com> MemCached Perl client
+ * See the memcached website:
+ * http://www.danga.com/memcached/
+ *
+ * This module is Copyright (c) 2003 Ryan Gilfether.
+ * All rights reserved.
+ * You may distribute under the terms of the GNU General Public License
+ * This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
+ *
+ */
+
+/**
+ * version string
+ */
+define("MC_VERSION", "1.0.8");
+/**
+ * int, buffer size used for sending and receiving
+ * data from sockets
+ */
+define("MC_BUFFER_SZ", 1024);
+/**
+ * MemCached error numbers
+ */
+define("MC_ERR_NOT_ACTIVE", 1001);     // no active servers
+define("MC_ERR_SOCKET_WRITE", 1002);   // socket_write() failed
+define("MC_ERR_SOCKET_READ", 1003);    // socket_read() failed
+define("MC_ERR_SOCKET_CONNECT", 1004); // failed to connect to host
+define("MC_ERR_DELETE", 1005);         // delete() did not recieve DELETED command
+define("MC_ERR_HOST_FORMAT", 1006);    // sock_to_host() invalid host format
+define("MC_ERR_HOST_DEAD", 1007);      // sock_to_host() host is dead
+define("MC_ERR_GET_SOCK", 1008);       // get_sock() failed to find a valid socket
+define("MC_ERR_SET", 1009);            // _set() failed to receive the STORED response
+define("MC_ERR_GET_KEY", 1010);                // _load_items no values returned for key(s)
+define("MC_ERR_LOADITEM_END", 1011);   // _load_items failed to receive END response
+define("MC_ERR_LOADITEM_BYTES", 1012); // _load_items bytes read larger than bytes available
+
+
+/**
+ * MemCached PHP client Class.
+ *
+ * Communicates with the MemCached server, and executes the MemCached protocol
+ * MemCached available at http://www.danga.com/memcached
+ *
+ * @author Ryan Gilfether <ryan@gilfether.com>
+ * @package MemCachedClient
+ * @access public
+ * @version 1.0.7
+ */
+class MemCachedClient
+{
+    /**
+     * array of servers no long available
+     * @var array
+     */
+    var $host_dead;
+    /**
+     * array of open sockets
+     * @var array
+     */
+    var $cache_sock;
+    /**
+     * determine if debugging is either on or off
+     * @var bool
+     */
+    var $debug;
+    /**
+     * array of servers to attempt to use, "host:port" string format
+     * @var array
+     */
+    var $servers;
+    /**
+     * count of currently active connections to servers
+     * @var int
+     */
+    var $active;
+    /**
+     * error code if one is set
+     * @var int
+     */
+    var $errno;
+    /**
+     * string describing error
+     * @var string
+     */
+    var $errstr;
+
+
+    /**
+     * Constructor
+     *
+     * Creates a new MemCachedClient object
+        * Takes one parameter, a array of options.  The most important key is
+        * $options["servers"], but that can also be set later with the set_servers()
+        * method.  The servers must be an array of hosts, each of which is
+        * either a scalar of the form <10.0.0.10:11211> or an array of the
+        * former and an integer weight value.  (the default weight if
+        * unspecified is 1.)  It's recommended that weight values be kept as low
+        * as possible, as this module currently allocates memory for bucket
+        * distribution proportional to the total host weights.
+        * $options["debug"] turns the debugging on if set to true
+     *
+     * @access public
+     * @param array $option an array of servers and debug status
+     * @return object MemCachedClient the new MemCachedClient object
+     */
+       function MemCachedClient($options = 0)
+       {
+               if(is_array($options))
+               {
+                       $this->set_servers($options["servers"]);
+                       $this->debug = $options["debug"];
+                       $this->cache_sock = array();
+               }
+
+               $this->errno = 0;
+               $this->errstr = "";
+       }
+
+
+       /**
+        * sets up the list of servers and the ports to connect to
+        * takes an array of servers in the same format as in the constructor
+        *
+        * @access public
+        * @param array $servers array of servers in the format described in the constructor
+        */
+       function set_servers($servers)
+       {
+               $this->servers = $servers;
+               $this->active = count($this->servers);
+       }
+
+
+       /**
+        * if $do_debug is set to true, will print out
+        * debugging info, else debug is turned off
+        *
+        * @access public
+        * @param bool $do_debug set to true to turn debugging on, false to turn off
+        */
+       function set_debug($do_debug)
+       {
+               $this->debug = $do_debug;
+       }
+
+
+       /**
+        * remove all cached hosts that are no longer good
+        *
+        * @access public
+        */
+       function forget_dead_hosts()
+       {
+               unset($this->host_dead);
+       }
+
+
+       /**
+        * disconnects from all servers
+        *
+        * @access public
+        */
+       function disconnect_all()
+       {
+               foreach($this->cache_sock as $sock)
+                       socket_close($sock);
+
+               unset($this->cache_sock);
+               $this->active = 0;
+       }
+
+
+       /**
+        * removes the key from the MemCache
+        * $time is the amount of time in seconds (or Unix time) until which
+        * the client wishes the server to refuse "add" and "replace" commands
+        * with this key. For this amount of item, the item is put into a
+        * delete queue, which means that it won't possible to retrieve it by
+        * the "get" command, but "add" and "replace" command with this key
+        * will also fail (the "set" command will succeed, however). After the
+        * time passes, the item is finally deleted from server memory.
+        * The parameter $time is optional, and, if absent, defaults to 0
+        * (which means that the item will be deleted immediately and further
+        * storage commands with this key will succeed).
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *              MC_ERR_DELETE
+        *
+        * @access public
+        * @param string $key the key to delete
+        * @param timestamp $time optional, the amount of time server will refuse commands on key
+        * @return bool TRUE on success, FALSE if key does not exist
+        */
+       function delete($key, $time = 0)
+       {
+               if(!$this->active)
+               {
+                       $this->errno = MC_ERR_NOT_ACTIVE;
+                       $this->errstr = "No active servers are available";
+
+                       if($this->debug)
+                               $this->_debug("delete(): There are no active servers available.");
+
+                       return FALSE;
+               }
+
+               $sock = $this->get_sock($key);
+
+               if(!is_resource($sock))
+               {
+                       $this->errno = MC_ERR_GET_SOCK;
+                       $this->errstr = "Unable to retrieve a valid socket.";
+
+                       if($this->debug)
+                               $this->_debug("delete(): get_sock() returned an invalid socket.");
+
+                       return FALSE;
+               }
+
+               if(is_array($key))
+                       $key = $key[1];
+
+               $cmd = "delete $key $time\r\n";
+               $cmd_len = strlen($cmd);
+               $offset = 0;
+
+               // now send the command
+               while($offset < $cmd_len)
+               {
+                       $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
+
+                       if($result !== FALSE)
+                               $offset += $result;
+                       else if($offset < $cmd_len)
+                       {
+                               $this->errno = MC_ERR_SOCKET_WRITE;
+                               $this->errstr = "Failed to write to socket.";
+
+                               if($this->debug)
+                               {
+                                       $sockerr = socket_last_error($sock);
+                                       $this->_debug("delete(): socket_write() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr));
+                               }
+
+                               return FALSE;
+                       }
+               }
+
+               // now read the server's response
+               if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE)
+               {
+                       $this->errno = MC_ERR_SOCKET_READ;
+                       $this->errstr = "Failed to read from socket.";
+
+                       if($this->debug)
+                       {
+                               $sockerr = socket_last_error($sock);
+                               $this->_debug("delete(): socket_read() returned FALSE. Socket Error $sockerr: ".socket_strerror($sockerr));
+                       }
+
+                       return FALSE;
+               }
+
+               // remove the \r\n from the end
+               $retval = rtrim($retval);
+
+               // now read the server's response
+               if($retval == "DELETED")
+                       return TRUE;
+               else
+               {
+                       // something went wrong, create the error
+                       $this->errno = MC_ERR_DELETE;
+                       $this->errstr = "Failed to receive DELETED response from server.";
+
+                       if($this->debug)
+                               $this->_debug("delete(): Failed to receive DELETED response from server. Received $retval instead.");
+
+                       return FALSE;
+               }
+       }
+
+
+       /**
+        * Like set(), but only stores in memcache if the key doesn't already exist.
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *              MC_ERR_SET
+        *
+        * @access public
+        * @param string $key the key to set
+        * @param mixed $val the value of the key
+        * @param timestamp $exptime optional, the to to live of the key
+        * @return bool TRUE on success, else FALSE
+        */
+       function add($key, $val, $exptime = 0)
+       {
+               return $this->_set("add", $key, $val, $exptime);
+       }
+
+
+       /**
+        * Like set(), but only stores in memcache if the key already exists.
+        * returns TRUE on success else FALSE
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *              MC_ERR_SET
+        *
+        * @access public
+        * @param string $key the key to set
+        * @param mixed $val the value of the key
+        * @param timestamp $exptime optional, the to to live of the key
+        * @return bool TRUE on success, else FALSE
+        */
+       function replace($key, $val, $exptime = 0)
+       {
+               return $this->_set("replace", $key, $val, $exptime);
+       }
+
+
+       /**
+        * Unconditionally sets a key to a given value in the memcache.  Returns true
+        * if it was stored successfully.
+        * The $key can optionally be an arrayref, with the first element being the
+        * hash value, as described above.
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *              MC_ERR_SET
+        *
+        * @access public
+        * @param string $key the key to set
+        * @param mixed $val the value of the key
+        * @param timestamp $exptime optional, the to to live of the key
+        * @return bool TRUE on success, else FALSE
+        */
+       function set($key, $val, $exptime = 0)
+       {
+               return $this->_set("set", $key, $val, $exptime);
+       }
+
+
+       /**
+        * Retrieves a key from the memcache.  Returns the value (automatically
+        * unserialized, if necessary) or FALSE if it fails.
+        * The $key can optionally be an array, with the first element being the
+        * hash value, if you want to avoid making this module calculate a hash
+        * value.  You may prefer, for example, to keep all of a given user's
+        * objects on the same memcache server, so you could use the user's
+        * unique id as the hash value.
+        * Possible errors set are:
+        *              MC_ERR_GET_KEY
+        *
+        * @access public
+        * @param string $key the key to retrieve
+        * @return mixed the value of the key, FALSE on error
+        */
+       function get($key)
+       {
+               $val =& $this->get_multi($key);
+
+               if(!$val)
+               {
+                       $this->errno = MC_ERR_GET_KEY;
+                       $this->errstr = "No value found for key $key";
+
+                       if($this->debug)
+                               $this->_debug("get(): No value found for key $key");
+
+                       return FALSE;
+               }
+
+               return $val[$key];
+       }
+
+
+       /**
+        * just like get(), but takes an array of keys, returns FALSE on error
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *
+        * @access public
+        * @param array $keys the keys to retrieve
+        * @return array the value of each key, FALSE on error
+        */
+       function get_multi($keys)
+       {
+               $sock_keys = array();
+               $socks = array();
+               $val = 0;
+
+               if(!$this->active)
+               {
+                       $this->errno = MC_ERR_NOT_ACTIVE;
+                       $this->errstr = "No active servers are available";
+
+                       if($this->debug)
+                               $this->_debug("get_multi(): There are no active servers available.");
+
+                       return FALSE;
+               }
+
+               if(!is_array($keys))
+               {
+                       $arr[] = $keys;
+                       $keys = $arr;
+               }
+
+               foreach($keys as $k)
+               {
+                       $sock = $this->get_sock($k);
+
+                       if($sock)
+                       {
+                               $k = is_array($k) ? $k[1] : $k;
+
+                               if(@!is_array($sock_keys[$sock]))
+                                       $sock_keys[$sock] = array();
+
+                               // if $sock_keys[$sock] doesn't exist, create it
+                               if(!$sock_keys[$sock])
+                                       $socks[] = $sock;
+
+                               $sock_keys[$sock][] = $k;
+                       }
+               }
+
+               if(!is_array($socks))
+               {
+                       $arr[] = $socks;
+                       $socks = $arr;
+               }
+
+               foreach($socks as $s)
+               {
+                       $this->_load_items($s, $val, $sock_keys[$sock]);
+               }
+
+               if($this->debug)
+               {
+                       while(list($k, $v) = @each($val))
+                               $this->_debug("MemCache: got $k = $v\n");
+               }
+
+               return $val;
+       }
+
+
+       /**
+        * Sends a command to the server to atomically increment the value for
+        * $key by $value, or by 1 if $value is undefined.  Returns FALSE if $key
+        * doesn't exist on server, otherwise it returns the new value after
+        * incrementing.  Value should be zero or greater.  Overflow on server
+        * is not checked.  Be aware of values approaching 2**32.  See decr.
+        * ONLY WORKS WITH NUMERIC VALUES
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *
+        * @access public
+        * @param string $key the keys to increment
+        * @param int $value the amount to increment the key bye
+        * @return int the new value of the key, else FALSE
+        */
+       function incr($key, $value = 1)
+       {
+               return $this->_incrdecr("incr", $key, $value);
+       }
+
+
+       /**
+        * Like incr, but decrements.  Unlike incr, underflow is checked and new
+        * values are capped at 0.  If server value is 1, a decrement of 2
+        * returns 0, not -1.
+        * ONLY WORKS WITH NUMERIC VALUES
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *
+        * @access public
+        * @param string $key the keys to increment
+        * @param int $value the amount to increment the key bye
+        * @return int the new value of the key, else FALSE
+        */
+       function decr($key, $value = 1)
+       {
+               return $this->_incrdecr("decr", $key, $value);
+       }
+
+
+       /**
+        * When a function returns FALSE, an error code is set.
+        * This funtion will return the error code.
+        * See error_string()
+        *
+        * @access public
+        * @return int the value of the last error code
+        */
+       function error()
+       {
+               return $this->errno;
+       }
+
+
+       /**
+        * Returns a string describing the error set in error()
+        * See error()
+        *
+        * @access public
+        * @return int a string describing the error code given
+        */
+       function error_string()
+       {
+               return $this->errstr;
+       }
+
+
+       /**
+        * Resets the error number and error string
+        *
+        * @access public
+        */
+       function error_clear()
+       {
+               // reset to no error
+               $this->errno = 0;
+               $this->errstr = "";
+       }
+
+
+       /*
+        * PRIVATE FUNCTIONS
+        */
+
+
+       /**
+        * connects to a server
+        * The $host may either a string int the form of host:port or an array of the
+        * former and an integer weight value.  (the default weight if
+        * unspecified is 1.) See the constructor for details
+        * Possible errors set are:
+        *              MC_ERR_HOST_FORMAT
+        *              MC_ERR_HOST_DEAD
+        *              MC_ERR_SOCKET_CONNECT
+        *
+        * @access private
+        * @param mixed $host either an array or a string
+        * @return resource the socket of the new connection, else FALSE
+        */
+       function sock_to_host($host)
+       {
+               if(is_array($host))
+                       $host = array_shift($host);
+
+               $now = time();
+
+               // seperate the ip from the port, index 0 = ip, index 1 = port
+               $conn = explode(":", $host);
+               if(count($conn) != 2)
+               {
+                       $this->errno = MC_ERR_HOST_FORMAT;
+                       $this->errstr = "Host address was not in the format of host:port";
+
+                       if($this->debug)
+                               $this->_debug("sock_to_host(): Host address was not in the format of host:port");
+
+                       return FALSE;
+               }
+
+               if(@($this->host_dead[$host] && $this->host_dead[$host] > $now) ||
+               @($this->host_dead[$conn[0]] && $this->host_dead[$conn[0]] > $now))
+               {
+                       $this->errno = MC_ERR_HOST_DEAD;
+                       $this->errstr = "Host $host is not available.";
+
+                       if($this->debug)
+                               $this->_debug("sock_to_host(): Host $host is not available.");
+
+                       return FALSE;
+               }
+
+               // connect to the server, if it fails, add it to the host_dead below
+               $sock = socket_create (AF_INET, SOCK_STREAM, getprotobyname("TCP"));
+
+               // we need surpress the error message if a connection fails
+               if(!@socket_connect($sock, $conn[0], $conn[1]))
+               {
+                       $this->host_dead[$host]=$this->host_dead[$conn[0]]=$now+60+intval(rand(0, 10));
+
+                       $this->errno = MC_ERR_SOCKET_CONNECT;
+                       $this->errstr = "Failed to connect to ".$conn[0].":".$conn[1];
+
+                       if($this->debug)
+                               $this->_debug("sock_to_host(): Failed to connect to ".$conn[0].":".$conn[1]);
+
+                       return FALSE;
+               }
+
+               // success, add to the list of sockets
+               $cache_sock[$host] = $sock;
+
+               return $sock;
+       }
+
+
+       /**
+        * retrieves the socket associated with a key
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *
+        * @access private
+        * @param string $key the key to retrieve the socket from
+        * @return resource the socket of the connection, else FALSE
+        */
+       function get_sock($key)
+       {
+               $buckets = 0;
+
+               if(!$this->active)
+               {
+                       $this->errno = MC_ERR_NOT_ACTIVE;
+                       $this->errstr = "No active servers are available";
+
+                       if($this->debug)
+                               $this->_debug("get_sock(): There are no active servers available.");
+
+                       return FALSE;
+               }
+
+               $hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key);
+
+               if(!$buckets)
+               {
+                       $bu = $buckets = array();
+
+                       foreach($this->servers as $v)
+                       {
+                               if(is_array($v))
+                               {
+                                       for($i = 1;  $i <= $v[1]; ++$i)
+                                               $bu[] =  $v[0];
+                               }
+                               else
+                                       $bu[] = $v;
+                       }
+
+                       $buckets = $bu;
+               }
+
+               $real_key = is_array($key) ? $key[1] : $key;
+               $tries = 0;
+               while($tries < 20)
+               {
+                       $host = @$buckets[$hv % count($buckets)];
+                       $sock = $this->sock_to_host($host);
+
+                       if(is_resource($sock))
+                               return $sock;
+
+                       $hv += $this->_hashfunc($tries.$real_key);
+                       ++$tries;
+               }
+
+               $this->errno = MC_ERR_GET_SOCK;
+               $this->errstr = "Unable to retrieve a valid socket.";
+
+               if($this->debug)
+                       $this->_debug("get_sock(): Unable to retrieve a valid socket.");
+
+               return FALSE;
+       }
+
+
+       /**
+        * increments or decrements a numerical value in memcached. this function is
+        * called from incr() and decr()
+        * ONLY WORKS WITH NUMERIC VALUES
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *
+        * @access private
+        * @param string $cmdname the command to send, either incr or decr
+        * @param string $key the key to perform the command on
+        * @param mixed $value the value to incr or decr the key value by
+        * @return int the new value of the key, FALSE if something went wrong
+        */
+       function _incrdecr($cmdname, $key, $value)
+       {
+               if(!$this->active)
+               {
+                       $this->errno = MC_ERR_NOT_ACTIVE;
+                       $this->errstr = "No active servers are available";
+
+                       if($this->debug)
+                               $this->_debug("_incrdecr(): There are no active servers available.");
+
+                       return FALSE;
+               }
+
+               $sock = $this->get_sock($key);
+               if(!is_resource($sock))
+               {
+                       $this->errno = MC_ERR_GET_SOCK;
+                       $this->errstr = "Unable to retrieve a valid socket.";
+
+                       if($this->debug)
+                               $this->_debug("_incrdecr(): Invalid socket returned by get_sock().");
+
+                       return FALSE;
+               }
+
+               if($value == "")
+                       $value = 1;
+
+               $cmd = "$cmdname $key $value\r\n";
+               $cmd_len = strlen($cmd);
+               $offset = 0;
+
+               // write the command to the server
+               while($offset < $cmd_len)
+               {
+                       $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
+
+                       if($result !== FALSE)
+                               $offset += $result;
+                       else if($offset < $cmd_len)
+                       {
+                               $this->errno = MC_ERR_SOCKET_WRITE;
+                               $this->errstr = "Failed to write to socket.";
+
+                               if($this->debug)
+                               {
+                                       $sockerr = socket_last_error($sock);
+                                       $this->_debug("_incrdecr(): socket_write() returned FALSE. Error $errno: ".socket_strerror($sockerr));
+                               }
+
+                               return FALSE;
+                       }
+               }
+
+               // now read the server's response
+               if(($retval = socket_read($sock, MC_BUFFER_SZ, PHP_NORMAL_READ)) === FALSE)
+               {
+                       $this->errno = MC_ERR_SOCKET_READ;
+                       $this->errstr = "Failed to read from socket.";
+
+                       if($this->debug)
+                       {
+                               $sockerr = socket_last_error($sock);
+                               $this->_debug("_incrdecr(): socket_read() returned FALSE. Socket Error $errno: ".socket_strerror($sockerr));
+                       }
+
+                       return FALSE;
+               }
+
+               // strip the /r/n from the end and return value
+               return trim($retval);
+       }
+
+
+       /**
+        * sends the command to the server
+        * Possible errors set are:
+        *              MC_ERR_NOT_ACTIVE
+        *              MC_ERR_GET_SOCK
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *              MC_ERR_SET
+        *
+        * @access private
+        * @param string $cmdname the command to send, either incr or decr
+        * @param string $key the key to perform the command on
+        * @param mixed $value the value to set the key to
+        * @param timestamp $exptime expiration time of the key
+        * @return bool TRUE on success, else FALSE
+        */
+       function _set($cmdname, $key, $val, $exptime = 0)
+       {
+               if(!$this->active)
+               {
+                       $this->errno = MC_ERR_NOT_ACTIVE;
+                       $this->errstr = "No active servers are available";
+
+                       if($this->debug)
+                               $this->_debug("_set(): No active servers are available.");
+
+                       return FALSE;
+               }
+
+               $sock = $this->get_sock($key);
+               if(!is_resource($sock))
+               {
+                       $this->errno = MC_ERR_GET_SOCK;
+                       $this->errstr = "Unable to retrieve a valid socket.";
+
+                       if($this->debug)
+                               $this->_debug("_set(): Invalid socket returned by get_sock().");
+
+                       return FALSE;
+               }
+
+               $flags = 0;
+               $key = is_array($key) ? $key[1] : $key;
+
+               $raw_val = $val;
+
+               // if the value is not scalar, we need to serialize it
+               if(!is_scalar($val))
+               {
+                       $val = serialize($val);
+                       $flags |= 1;
+               }
+
+               $len = strlen($val);
+               if (!is_int($exptime))
+                       $exptime = 0;
+
+               // send off the request
+               $cmd = "$cmdname $key $flags $exptime $len\r\n$val\r\n";
+               $cmd_len = strlen($cmd);
+               $offset = 0;
+
+               // write the command to the server
+               while($offset < $cmd_len)
+               {
+                       $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
+
+                       if($result !== FALSE)
+                               $offset += $result;
+                       else if($offset < $cmd_len)
+                       {
+                               $this->errno = MC_ERR_SOCKET_WRITE;
+                               $this->errstr = "Failed to write to socket.";
+
+                               if($this->debug)
+                               {
+                                       $errno = socket_last_error($sock);
+                                       $this->_debug("_set(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno));
+                               }
+
+                               return FALSE;
+                       }
+               }
+
+               // now read the server's response
+               if(($l_szResponse = socket_read($sock, 6, PHP_NORMAL_READ)) === FALSE)
+               {
+                       $this->errno = MC_ERR_SOCKET_READ;
+                       $this->errstr = "Failed to read from socket.";
+
+                       if($this->debug)
+                       {
+                               $errno = socket_last_error($sock);
+                               $this->_debug("_set(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno));
+                       }
+
+                       return FALSE;
+               }
+
+               if($l_szResponse == "STORED")
+               {
+                       if($this->debug)
+                               $this->_debug("MemCache: $cmdname $key = $raw_val");
+
+                       return TRUE;
+               }
+
+               $this->errno = MC_ERR_SET;
+               $this->errstr = "Failed to receive the STORED response from the server.";
+
+               if($this->debug)
+                       $this->_debug("_set(): Did not receive STORED as the server response! Received $l_szResponse instead.");
+
+               return FALSE;
+       }
+
+
+       /**
+        * retrieves the value, and returns it unserialized
+        * Possible errors set are:
+        *              MC_ERR_SOCKET_WRITE
+        *              MC_ERR_SOCKET_READ
+        *              MC_ERR_GET_KEY
+        *              MC_ERR_LOADITEM_END
+        *              MC_ERR_LOADITEM_BYTES
+        *
+        * @access private
+        * @param resource $sock the socket to connection we are retriving from
+        * @param array $val reference to the values retrieved
+        * @param mixed $sock_keys either a string or an array of keys to retrieve
+        * @return array TRUE on success, else FALSE
+        */
+       function _load_items($sock, &$val, $sock_keys)
+       {
+               $val = array();
+               $cmd = "get ";
+
+               if(!is_array($sock_keys))
+               {
+                       $arr[] = $sock_keys;
+                       $sock_keys = $arr;
+               }
+
+               foreach($sock_keys as $sk)
+                       $cmd .= $sk." ";
+
+               $cmd .="\r\n";
+               $cmd_len = strlen($cmd);
+               $offset = 0;
+
+               // write the command to the server
+               while($offset < $cmd_len)
+               {
+                       $result = socket_write($sock, substr($cmd, $offset, MC_BUFFER_SZ), MC_BUFFER_SZ);
+
+                       if($result !== FALSE)
+                               $offset += $result;
+                       else if($offset < $cmd_len)
+                       {
+                               $this->errno = MC_ERR_SOCKET_WRITE;
+                               $this->errstr = "Failed to write to socket.";
+
+                               if($this->debug)
+                               {
+                                       $errno = socket_last_error($sock);
+                                       $this->_debug("_load_items(): socket_write() returned FALSE. Error $errno: ".socket_strerror($errno));
+                               }
+
+                               return FALSE;
+                       }
+               }
+
+               $len = 0;
+               $buf = "";
+               $flags_array = array();
+
+               // now read the response from the server
+               while($line = socket_read($sock, MC_BUFFER_SZ, PHP_BINARY_READ))
+               {
+                       // check for a socket_read error
+                       if($line === FALSE)
+                       {
+                               $this->errno = MC_ERR_SOCKET_READ;
+                               $this->errstr = "Failed to read from socket.";
+
+                               if($this->debug)
+                               {
+                                       $errno = socket_last_error($sock);
+                                       $this->_debug("_load_items(): socket_read() returned FALSE. Error $errno: ".socket_strerror($errno));
+                               }
+
+                               return FALSE;
+                       }
+
+                       if($len == 0)
+                       {
+                               $header = substr($line, 0, strpos($line, "\r\n"));
+                               $matches = explode(" ", $header);
+
+                               if(is_string($matches[1]) && is_numeric($matches[2]) && is_numeric($matches[3]))
+                               {
+                                       $rk = $matches[1];
+                                       $flags = $matches[2];
+                                       $len = $matches[3];
+
+                                       if($flags)
+                                               $flags_array[$rk] = $flags;
+
+                                       $len_array[$rk] = $len;
+                                       $bytes_read = 0;
+
+                                       // get the left over data after the header is read
+                                       $line = substr($line, strpos($line, "\r\n")+1, strlen($line));
+                               }
+                               else
+                               {
+                                       $this->errno = MC_ERR_GET_KEY;
+                                       $this->errstr = "Requested key(s) returned no values.";
+
+                                       // something went wrong, we never recieved the header
+                                       if($this->debug)
+                                               $this->_debug("_load_items(): Requested key(s) returned no values.");
+
+                                       return FALSE;
+                               }
+                       }
+
+                       // skip over the extra return or newline
+                       if($line == "\r" || $line == "\n")
+                               continue;
+
+                       $bytes_read += strlen($line);
+                       $buf .= $line;
+
+                       // we read the all of the data, take in account
+                       // for the /r/nEND/r/n
+                       if($bytes_read == ($len + 7))
+                       {
+                               $end = substr($buf, $len+2, 3);
+                               if($end == "END")
+                               {
+                                       $val[$rk] = substr($buf, 0, $len);
+
+                                       foreach($sock_keys as $sk)
+                                       {
+                                               if(!isset($val[$sk]))
+                                                       continue;
+
+                                               if(strlen($val[$sk]) != $len_array[$sk])
+                                                       continue;
+
+                                               if(@$flags_array[$sk] & 1)
+                                                       $val[$sk] = unserialize($val[$sk]);
+                                       }
+
+                                       return TRUE;
+                               }
+                               else
+                               {
+                                       $this->errno = MC_ERR_LOADITEM_END;
+                                       $this->errstr = "Failed to receive END response from server.";
+
+                                       if($this->debug)
+                                               $this->_debug("_load_items(): Failed to receive END. Received $end instead.");
+
+                                       return FALSE;
+                               }
+                       }
+
+                       // take in consideration for the "\r\nEND\r\n"
+                       if($bytes_read > ($len + 7))
+                       {
+                               $this->errno = MC_ERR_LOADITEM_BYTES;
+                               $this->errstr = "Bytes read from server greater than size of data.";
+
+                               if($this->debug)
+                                       $this->_debug("_load_items(): Bytes read is greater than requested data size.");
+
+                               return FALSE;
+                       }
+
+               }
+       }
+
+
+       /**
+        * creates our hash
+        *
+        * @access private
+        * @param int $num
+        * @return hash
+        */
+       function _hashfunc($num)
+       {
+               $hash = 0;
+
+               foreach(preg_split('//', $num, -1, PREG_SPLIT_NO_EMPTY) as $v)
+               {
+                       $hash = $hash * 33 + ord($v);
+               }
+
+               return $hash;
+       }
+
+    /**
+     * function that can be overridden to handle debug output
+     * by default debug info is print to the screen
+     *
+     * @access private
+     * @param $text string to output debug info
+     */
+    function _debug($text)
+    {
+               print $text . "\r\n";
+    }
+}
+
+?>
\ No newline at end of file
index 7b0c646..fd2a0f8 100644 (file)
@@ -17,9 +17,25 @@ include_once( "$IP/User.php" );
 include_once( "$IP/LinkCache.php" );
 include_once( "$IP/Title.php" );
 include_once( "$IP/Article.php" );
+require( "$IP/MemCachedClient.inc.php" );
+
+wfDebug( "\n\n" );
 
 global $wgUser, $wgLang, $wgOut, $wgTitle;
 global $wgArticle, $wgDeferredUpdateList, $wgLinkCache;
+global $wgMemc;
+
+class MemCachedClientforWiki extends MemCachedClient {
+       function _debug( $text ) {
+               wfDebug( "memcached: $text\n" );
+       }
+}
+
+$wgMemc = new MemCachedClientforWiki();
+if( $wgUseMemCached ) {
+       $wgMemc->set_servers( $wgMemCachedServers );
+       $wgMemc->set_debug( $wgMemcachedDebug );
+}
 
 $wgOut = new OutputPage();
 $wgLangClass = "Language" . ucfirst( $wgLanguageCode );
@@ -29,8 +45,7 @@ if( ! class_exists( $wgLangClass ) ) {
 }
 $wgLang = new $wgLangClass();
 
-$wgUser = new User();
-$wgUser->loadFromSession();
+$wgUser = User::loadFromSession();
 $wgDeferredUpdateList = array();
 $wgLinkCache = new LinkCache();
 
index 8306392..ffef535 100644 (file)
@@ -129,23 +129,22 @@ class User {
                return $this->mBlockreason;
        }
 
-       function loadFromSession()
+       /* static */ function loadFromSession()
        {
                global $HTTP_COOKIE_VARS, $wsUserID, $wsUserName, $wsUserPassword;
+               global $wgMemc, $wgDBname;
 
                if ( isset( $wsUserID ) ) {
                        if ( 0 != $wsUserID ) {
                                $sId = $wsUserID;
                        } else {
-                               $this->mId = 0;
-                               return;
+                               return new User();
                        }
                } else if ( isset( $HTTP_COOKIE_VARS["wcUserID"] ) ) {
                        $sId = $HTTP_COOKIE_VARS["wcUserID"];
                        $wsUserID = $sId;
                } else {
-                       $this->mId = 0;
-                       return;
+                       return new User();
                }
                if ( isset( $wsUserName ) ) {
                        $sName = $wsUserName;
@@ -153,30 +152,40 @@ class User {
                        $sName = $HTTP_COOKIE_VARS["wcUserName"];
                        $wsUserName = $sName;
                } else {
-                       $this->mId = 0;
-                       return;
+                       return new User();
                }
 
                $passwordCorrect = FALSE;
-               $this->mId = $sId;
-               $this->loadFromDatabase();
+               $user = $wgMemc->get( $key = "$wgDBname:user:user_id:$sId" );
+               if($makenew = !$user) {
+                       wfDebug( "User::loadFromSession() unable to load from memcached\n" );
+                       $user = new User();
+                       $user->mId = $sId;
+                       $user->loadFromDatabase();
+               } else {
+                       wfDebug( "User::loadFromSession() got from cache!\n" );
+               }
 
                if ( isset( $wsUserPassword ) ) {
-                       $passwordCorrect = $wsUserPassword == $this->mPassword;
+                       $passwordCorrect = $wsUserPassword == $user->mPassword;
                } else if ( isset( $HTTP_COOKIE_VARS["wcUserPassword"] ) ) {
-                       $this->mCookiePassword = $HTTP_COOKIE_VARS["wcUserPassword"];
-                       $wsUserPassword = $this->addSalt( $this->mCookiePassword );
-                       $passwordCorrect = $wsUserPassword == $this->mPassword;
+                       $user->mCookiePassword = $HTTP_COOKIE_VARS["wcUserPassword"];
+                       $wsUserPassword = $user->addSalt( $user->mCookiePassword );
+                       $passwordCorrect = $wsUserPassword == $user->mPassword;
                } else {
-                       $this->mId = 0;
-                       $this->loadDefaults(); # Can't log in from session
-                       return;
+                       return new User(); # Can't log in from session
                }
 
-               if ( ( $sName == $this->mName ) && $passwordCorrect ) {
-                       return;
+               if ( ( $sName == $user->mName ) && $passwordCorrect ) {
+                       if($makenew) {
+                               if($wgMemc->set( $key, $user ))
+                                       wfDebug( "User::loadFromSession() successfully saved user\n" );
+                               else
+                                       wfDebug( "User::loadFromSession() unable to save to memcached\n" );
+                       }
+                       return $user;
                }
-               $this->loadDefaults(); # Can't log in from session
+               return new User(); # Can't log in from session
        }
 
        function loadFromDatabase()
@@ -491,7 +500,7 @@ class User {
 
        function saveSettings()
        {
-               global $wgUser;
+               global $wgMemc, $wgDBname;
 
                if ( ! $this->mNewtalk ) {
                        if( $this->mId ) {
@@ -515,6 +524,8 @@ class User {
                  "user_touched= '" . wfStrencode( $this->mTouched ) .
                  "' WHERE user_id={$this->mId}";
                wfQuery( $sql, "User::saveSettings" );
+               #$wgMemc->replace( "$wgDBname:user:user_id:$this->mId", $this );
+               $wgMemc->delete( "$wgDBname:user:user_id:$this->mId" );
        }
 
        # Checks if a user with the given name exists
index 011a7ba..777f5f4 100644 (file)
@@ -15,7 +15,7 @@ class UserTalkUpdate {
        function doUpdate()
        {
        
-               global $wgUser, $wgLang;
+               global $wgUser, $wgLang, $wgMemc, $wgDBname;
                $fname = "UserTalkUpdate::doUpdate";
 
                # If namespace isn't User_talk:, do nothing.
@@ -27,7 +27,6 @@ class UserTalkUpdate {
                # If the user talk page is our own, clear the flag
                # whether we are reading it or writing it.
                if ( 0 == strcmp( $this->mTitle, $wgUser->getName() ) ) {
-                       
                        $wgUser->setNewtalk( 0 );                       
                        $wgUser->saveSettings();
 
@@ -39,13 +38,12 @@ class UserTalkUpdate {
                                $user->setID(User::idFromName($this->mTitle));
                                if ($id=$user->getID()) {                                                                       
                                        $sql = "INSERT INTO user_newtalk (user_id) values ({$id})";
-                                       
+                                       $wgMemc->delete( "$wgDBname:user:user_id:$id" );
                                } else { #anon
                                        
                                        if(preg_match("/^\d{1,3}\.\d{1,3}.\d{1,3}\.\d{1,3}$/",$this->mTitle)) { #real anon (user:xxx.xxx.xxx.xxx)
                                        
                                                $sql = "INSERT INTO user_newtalk (user_id,user_ip) values (0,\"{$this->mTitle}\")";             
-                                               
                                        }                                       
                                
                                }