diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..485dee6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/README.md b/README.md
index 827e1b9..a70ebbd 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,24 @@ databases.
 This repository is used to make SphinxSearch API PHP Client available through 
 [composer](http://getcomposer.org/).
 
+    {
+        "require": {
+            "silexneutron/sphinxsearch-api": "dev-master"
+        }
+    }
+
+
+There is also [branch](https://github.com/romainneutron/Sphinx-Search-API-PHP-Client/tree/psr0) with [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)
+file structure and class-only constants (for example `SEARCHD_COMMAND_SEARCH` moved
+to `SphinxSearchApi_Client::SEARCHD_COMMAND_SEARCH`).
+
+    {
+        "require": {
+            "silexneutron/sphinxsearch-api": "dev-psr0"
+        }
+    }
+
+
 # License
 
 This is licensed under the GNU General Public License
diff --git a/composer.json b/composer.json
index 358a6fa..f762da6 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,6 @@
     "type": "library",
     "keywords": ["Sphinx Search","search-engine","api"],
     "homepage": "http://sphinxsearch.com/",
-    "version": "2.0.6",
     "authors": [
         {
             "name": "Andrew Aksyonoff",
@@ -16,6 +15,8 @@
         }
     ],
     "autoload": {
-        "classmap": ["sphinxapi.php"]
+        "psr-0": {
+            "SphinxSearchApi": "src/"
+        }
     }
 }
diff --git a/sphinxapi.php b/src/SphinxSearchApi/Client.php
similarity index 75%
rename from sphinxapi.php
rename to src/SphinxSearchApi/Client.php
index 87bad03..285da29 100644
--- a/sphinxapi.php
+++ b/src/SphinxSearchApi/Client.php
@@ -1,7 +1,6 @@
 <?php
-
 //
-// $Id: sphinxapi.php 3439 2012-10-10 05:47:48Z kevg $
+// $Id: sphinxapi.php 3782 2013-04-06 18:22:58Z kevg $
 //
 
 //
@@ -19,83 +18,6 @@
 // PHP version of Sphinx searchd client (PHP API)
 /////////////////////////////////////////////////////////////////////////////
 
-/// known searchd commands
-define ( "SEARCHD_COMMAND_SEARCH",		0 );
-define ( "SEARCHD_COMMAND_EXCERPT",		1 );
-define ( "SEARCHD_COMMAND_UPDATE",		2 );
-define ( "SEARCHD_COMMAND_KEYWORDS",	3 );
-define ( "SEARCHD_COMMAND_PERSIST",		4 );
-define ( "SEARCHD_COMMAND_STATUS",		5 );
-define ( "SEARCHD_COMMAND_FLUSHATTRS",	7 );
-
-/// current client-side command implementation versions
-define ( "VER_COMMAND_SEARCH",		0x119 );
-define ( "VER_COMMAND_EXCERPT",		0x104 );
-define ( "VER_COMMAND_UPDATE",		0x102 );
-define ( "VER_COMMAND_KEYWORDS",	0x100 );
-define ( "VER_COMMAND_STATUS",		0x100 );
-define ( "VER_COMMAND_QUERY",		0x100 );
-define ( "VER_COMMAND_FLUSHATTRS",	0x100 );
-
-/// known searchd status codes
-define ( "SEARCHD_OK",				0 );
-define ( "SEARCHD_ERROR",			1 );
-define ( "SEARCHD_RETRY",			2 );
-define ( "SEARCHD_WARNING",			3 );
-
-/// known match modes
-define ( "SPH_MATCH_ALL",			0 );
-define ( "SPH_MATCH_ANY",			1 );
-define ( "SPH_MATCH_PHRASE",		2 );
-define ( "SPH_MATCH_BOOLEAN",		3 );
-define ( "SPH_MATCH_EXTENDED",		4 );
-define ( "SPH_MATCH_FULLSCAN",		5 );
-define ( "SPH_MATCH_EXTENDED2",		6 );	// extended engine V2 (TEMPORARY, WILL BE REMOVED)
-
-/// known ranking modes (ext2 only)
-define ( "SPH_RANK_PROXIMITY_BM25",	0 );	///< default mode, phrase proximity major factor and BM25 minor one
-define ( "SPH_RANK_BM25",			1 );	///< statistical mode, BM25 ranking only (faster but worse quality)
-define ( "SPH_RANK_NONE",			2 );	///< no ranking, all matches get a weight of 1
-define ( "SPH_RANK_WORDCOUNT",		3 );	///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
-define ( "SPH_RANK_PROXIMITY",		4 );
-define ( "SPH_RANK_MATCHANY",		5 );
-define ( "SPH_RANK_FIELDMASK",		6 );
-define ( "SPH_RANK_SPH04",			7 );
-define ( "SPH_RANK_EXPR",			8 );
-define ( "SPH_RANK_TOTAL",			9 );
-
-/// known sort modes
-define ( "SPH_SORT_RELEVANCE",		0 );
-define ( "SPH_SORT_ATTR_DESC",		1 );
-define ( "SPH_SORT_ATTR_ASC",		2 );
-define ( "SPH_SORT_TIME_SEGMENTS", 	3 );
-define ( "SPH_SORT_EXTENDED", 		4 );
-define ( "SPH_SORT_EXPR", 			5 );
-
-/// known filter types
-define ( "SPH_FILTER_VALUES",		0 );
-define ( "SPH_FILTER_RANGE",		1 );
-define ( "SPH_FILTER_FLOATRANGE",	2 );
-
-/// known attribute types
-define ( "SPH_ATTR_INTEGER",		1 );
-define ( "SPH_ATTR_TIMESTAMP",		2 );
-define ( "SPH_ATTR_ORDINAL",		3 );
-define ( "SPH_ATTR_BOOL",			4 );
-define ( "SPH_ATTR_FLOAT",			5 );
-define ( "SPH_ATTR_BIGINT",			6 );
-define ( "SPH_ATTR_STRING",			7 );
-define ( "SPH_ATTR_MULTI",			0x40000001 );
-define ( "SPH_ATTR_MULTI64",			0x40000002 );
-
-/// known grouping functions
-define ( "SPH_GROUPBY_DAY",			0 );
-define ( "SPH_GROUPBY_WEEK",		1 );
-define ( "SPH_GROUPBY_MONTH",		2 );
-define ( "SPH_GROUPBY_YEAR",		3 );
-define ( "SPH_GROUPBY_ATTR",		4 );
-define ( "SPH_GROUPBY_ATTRPAIR",	5 );
-
 // important properties of PHP's integers:
 //  - always signed (one bit short of PHP_INT_SIZE)
 //  - conversion from string to int is saturated
@@ -122,269 +44,86 @@
 //  - return ints if we can
 //  - otherwise format number into a string
 
-/// pack 64-bit signed
-function sphPackI64 ( $v )
-{
-	assert ( is_numeric($v) );
-	
-	// x64
-	if ( PHP_INT_SIZE>=8 )
-	{
-		$v = (int)$v;
-		return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
-	}
-
-	// x32, int
-	if ( is_int($v) )
-		return pack ( "NN", $v < 0 ? -1 : 0, $v );
-
-	// x32, bcmath	
-	if ( function_exists("bcmul") )
-	{
-		if ( bccomp ( $v, 0 ) == -1 )
-			$v = bcadd ( "18446744073709551616", $v );
-		$h = bcdiv ( $v, "4294967296", 0 );
-		$l = bcmod ( $v, "4294967296" );
-		return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
-	}
-
-	// x32, no-bcmath
-	$p = max(0, strlen($v) - 13);
-	$lo = abs((float)substr($v, $p));
-	$hi = abs((float)substr($v, 0, $p));
-
-	$m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
-	$q = floor($m/4294967296.0);
-	$l = $m - ($q*4294967296.0);
-	$h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
-
-	if ( $v<0 )
-	{
-		if ( $l==0 )
-			$h = 4294967296.0 - $h;
-		else
-		{
-			$h = 4294967295.0 - $h;
-			$l = 4294967296.0 - $l;
-		}
-	}
-	return pack ( "NN", $h, $l );
-}
-
-/// pack 64-bit unsigned
-function sphPackU64 ( $v )
-{
-	assert ( is_numeric($v) );
-	
-	// x64
-	if ( PHP_INT_SIZE>=8 )
-	{
-		assert ( $v>=0 );
-		
-		// x64, int
-		if ( is_int($v) )
-			return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
-						  
-		// x64, bcmath
-		if ( function_exists("bcmul") )
-		{
-			$h = bcdiv ( $v, 4294967296, 0 );
-			$l = bcmod ( $v, 4294967296 );
-			return pack ( "NN", $h, $l );
-		}
-		
-		// x64, no-bcmath
-		$p = max ( 0, strlen($v) - 13 );
-		$lo = (int)substr ( $v, $p );
-		$hi = (int)substr ( $v, 0, $p );
-	
-		$m = $lo + $hi*1316134912;
-		$l = $m % 4294967296;
-		$h = $hi*2328 + (int)($m/4294967296);
-
-		return pack ( "NN", $h, $l );
-	}
-
-	// x32, int
-	if ( is_int($v) )
-		return pack ( "NN", 0, $v );
-	
-	// x32, bcmath
-	if ( function_exists("bcmul") )
-	{
-		$h = bcdiv ( $v, "4294967296", 0 );
-		$l = bcmod ( $v, "4294967296" );
-		return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
-	}
-
-	// x32, no-bcmath
-	$p = max(0, strlen($v) - 13);
-	$lo = (float)substr($v, $p);
-	$hi = (float)substr($v, 0, $p);
-	
-	$m = $lo + $hi*1316134912.0;
-	$q = floor($m / 4294967296.0);
-	$l = $m - ($q * 4294967296.0);
-	$h = $hi*2328.0 + $q;
-
-	return pack ( "NN", $h, $l );
-}
-
-// unpack 64-bit unsigned
-function sphUnpackU64 ( $v )
+/// sphinx searchd client class
+class SphinxSearchApi_Client
 {
-	list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
-
-	if ( PHP_INT_SIZE>=8 )
-	{
-		if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
-		if ( $lo<0 ) $lo += (1<<32);
-
-		// x64, int
-		if ( $hi<=2147483647 )
-			return ($hi<<32) + $lo;
-
-		// x64, bcmath
-		if ( function_exists("bcmul") )
-			return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
-
-		// x64, no-bcmath
-		$C = 100000;
-		$h = ((int)($hi / $C) << 32) + (int)($lo / $C);
-		$l = (($hi % $C) << 32) + ($lo % $C);
-		if ( $l>$C )
-		{
-			$h += (int)($l / $C);
-			$l  = $l % $C;
-		}
-
-		if ( $h==0 )
-			return $l;
-		return sprintf ( "%d%05d", $h, $l );
-	}
-
-	// x32, int
-	if ( $hi==0 )
-	{
-		if ( $lo>0 )
-			return $lo;
-		return sprintf ( "%u", $lo );
-	}
-
-	$hi = sprintf ( "%u", $hi );
-	$lo = sprintf ( "%u", $lo );
-
-	// x32, bcmath
-	if ( function_exists("bcmul") )
-		return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
-	
-	// x32, no-bcmath
-	$hi = (float)$hi;
-	$lo = (float)$lo;
-	
-	$q = floor($hi/10000000.0);
-	$r = $hi - $q*10000000.0;
-	$m = $lo + $r*4967296.0;
-	$mq = floor($m/10000000.0);
-	$l = $m - $mq*10000000.0;
-	$h = $q*4294967296.0 + $r*429.0 + $mq;
-
-	$h = sprintf ( "%.0f", $h );
-	$l = sprintf ( "%07.0f", $l );
-	if ( $h=="0" )
-		return sprintf( "%.0f", (float)$l );
-	return $h . $l;
-}
+/// known searchd commands
+    const SEARCHD_COMMAND_SEARCH = 0;
+    const SEARCHD_COMMAND_EXCERPT = 1;
+    const SEARCHD_COMMAND_UPDATE = 2;
+    const SEARCHD_COMMAND_KEYWORDS = 3;
+    const SEARCHD_COMMAND_PERSIST = 4;
+    const SEARCHD_COMMAND_STATUS = 5;
+    const SEARCHD_COMMAND_FLUSHATTRS = 7;
 
-// unpack 64-bit signed
-function sphUnpackI64 ( $v )
-{
-	list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
+/// current client-side command implementation versions
+    const VER_COMMAND_SEARCH = 0x119;
+    const VER_COMMAND_EXCERPT = 0x104;
+    const VER_COMMAND_UPDATE = 0x102;
+    const VER_COMMAND_KEYWORDS = 0x100;
+    const VER_COMMAND_STATUS = 0x100;
+    const VER_COMMAND_QUERY = 0x100;
+    const VER_COMMAND_FLUSHATTRS = 0x100;
 
-	// x64
-	if ( PHP_INT_SIZE>=8 )
-	{
-		if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
-		if ( $lo<0 ) $lo += (1<<32);
+/// known searchd status codes
+    const SEARCHD_OK = 0;
+    const SEARCHD_ERROR = 1;
+    const SEARCHD_RETRY = 2;
+    const SEARCHD_WARNING = 3;
 
-		return ($hi<<32) + $lo;
-	}
+/// known match modes
+    const SPH_MATCH_ALL = 0;
+    const SPH_MATCH_ANY = 1;
+    const SPH_MATCH_PHRASE = 2;
+    const SPH_MATCH_BOOLEAN = 3;
+    const SPH_MATCH_EXTENDED = 4;
+    const SPH_MATCH_FULLSCAN = 5;
+    const SPH_MATCH_EXTENDED2 = 6;	// extended engine V2 (TEMPORARY, WILL BE REMOVED)
 
-	// x32, int
-	if ( $hi==0 )
-	{
-		if ( $lo>0 )
-			return $lo;
-		return sprintf ( "%u", $lo );
-	}
-	// x32, int
-	elseif ( $hi==-1 )
-	{
-		if ( $lo<0 )
-			return $lo;
-		return sprintf ( "%.0f", $lo - 4294967296.0 );
-	}
-	
-	$neg = "";
-	$c = 0;
-	if ( $hi<0 )
-	{
-		$hi = ~$hi;
-		$lo = ~$lo;
-		$c = 1;
-		$neg = "-";
-	}	
-
-	$hi = sprintf ( "%u", $hi );
-	$lo = sprintf ( "%u", $lo );
-
-	// x32, bcmath
-	if ( function_exists("bcmul") )
-		return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c );
-
-	// x32, no-bcmath
-	$hi = (float)$hi;
-	$lo = (float)$lo;
-	
-	$q = floor($hi/10000000.0);
-	$r = $hi - $q*10000000.0;
-	$m = $lo + $r*4967296.0;
-	$mq = floor($m/10000000.0);
-	$l = $m - $mq*10000000.0 + $c;
-	$h = $q*4294967296.0 + $r*429.0 + $mq;
-	if ( $l==10000000 )
-	{
-		$l = 0;
-		$h += 1;
-	}
+/// known ranking modes (ext2 only)
+    const SPH_RANK_PROXIMITY_BM25 = 0;	///< default mode, phrase proximity major factor and BM25 minor one
+    const SPH_RANK_BM25 = 1;	///< statistical mode, BM25 ranking only (faster but worse quality)
+    const SPH_RANK_NONE = 2;	///< no ranking, all matches get a weight of 1
+    const SPH_RANK_WORDCOUNT = 3;	///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
+    const SPH_RANK_PROXIMITY = 4;
+    const SPH_RANK_MATCHANY = 5;
+    const SPH_RANK_FIELDMASK = 6;
+    const SPH_RANK_SPH04 = 7;
+    const SPH_RANK_EXPR = 8;
+    const SPH_RANK_TOTAL = 9;
 
-	$h = sprintf ( "%.0f", $h );
-	$l = sprintf ( "%07.0f", $l );
-	if ( $h=="0" )
-		return $neg . sprintf( "%.0f", (float)$l );
-	return $neg . $h . $l;
-}
+/// known sort modes
+    const SPH_SORT_RELEVANCE = 0;
+    const SPH_SORT_ATTR_DESC = 1;
+    const SPH_SORT_ATTR_ASC = 2;
+    const SPH_SORT_TIME_SEGMENTS = 3;
+    const SPH_SORT_EXTENDED = 4;
+    const SPH_SORT_EXPR = 5;
 
+/// known filter types
+    const SPH_FILTER_VALUES = 0;
+    const SPH_FILTER_RANGE = 1;
+    const SPH_FILTER_FLOATRANGE = 2;
 
-function sphFixUint ( $value )
-{
-	if ( PHP_INT_SIZE>=8 )
-	{
-		// x64 route, workaround broken unpack() in 5.2.2+
-		if ( $value<0 ) $value += (1<<32);
-		return $value;
-	}
-	else
-	{
-		// x32 route, workaround php signed/unsigned braindamage
-		return sprintf ( "%u", $value );
-	}
-}
+/// known attribute types
+    const SPH_ATTR_INTEGER = 1;
+    const SPH_ATTR_TIMESTAMP = 2;
+    const SPH_ATTR_ORDINAL = 3;
+    const SPH_ATTR_BOOL = 4;
+    const SPH_ATTR_FLOAT = 5;
+    const SPH_ATTR_BIGINT = 6;
+    const SPH_ATTR_STRING = 7;
+    const SPH_ATTR_MULTI = 0x40000001;
+    const SPH_ATTR_MULTI64 = 0x40000002;
 
+/// known grouping functions
+    const SPH_GROUPBY_DAY = 0;
+    const SPH_GROUPBY_WEEK = 1;
+    const SPH_GROUPBY_MONTH = 2;
+    const SPH_GROUPBY_YEAR = 3;
+    const SPH_GROUPBY_ATTR = 4;
+    const SPH_GROUPBY_ATTRPAIR = 5;
 
-/// sphinx searchd client class
-class SphinxClient
-{
 	var $_host;			///< searchd host (default is "localhost")
 	var $_port;			///< searchd port (default is 9312)
 	var $_offset;		///< how many records to seek from result-set start (default is 0)
@@ -427,7 +166,7 @@ class SphinxClient
 	/////////////////////////////////////////////////////////////////////////////
 
 	/// create a new client object and fill defaults
-	function SphinxClient ()
+	public function __construct()
 	{
 		// per-client-object settings
 		$this->_host		= "localhost";
@@ -438,15 +177,15 @@ function SphinxClient ()
 		// per-query settings
 		$this->_offset		= 0;
 		$this->_limit		= 20;
-		$this->_mode		= SPH_MATCH_ALL;
+		$this->_mode		= self::SPH_MATCH_ALL;
 		$this->_weights		= array ();
-		$this->_sort		= SPH_SORT_RELEVANCE;
+		$this->_sort		= self::SPH_SORT_RELEVANCE;
 		$this->_sortby		= "";
 		$this->_min_id		= 0;
 		$this->_max_id		= 0;
 		$this->_filters		= array ();
 		$this->_groupby		= "";
-		$this->_groupfunc	= SPH_GROUPBY_DAY;
+		$this->_groupfunc	= self::SPH_GROUPBY_DAY;
 		$this->_groupsort	= "@group desc";
 		$this->_groupdistinct= "";
 		$this->_maxmatches	= 1000;
@@ -455,7 +194,7 @@ function SphinxClient ()
 		$this->_retrydelay	= 0;
 		$this->_anchor		= array ();
 		$this->_indexweights= array ();
-		$this->_ranker		= SPH_RANK_PROXIMITY_BM25;
+		$this->_ranker		= self::SPH_RANK_PROXIMITY_BM25;
 		$this->_rankexpr	= "";
 		$this->_maxquerytime= 0;
 		$this->_fieldweights= array();
@@ -470,6 +209,8 @@ function SphinxClient ()
 		$this->_mbenc		= "";
 		$this->_arrayresult	= false;
 		$this->_timeout		= 0;
+        
+        $this->Utils = new SphinxSearchApi_Utils;
 	}
 
 	function __destruct()
@@ -510,13 +251,12 @@ function SetServer ( $host, $port = 0 )
 			$this->_path = $host;
 			return;
 		}
-				
+
 		$this->_host = $host;
-		if ( is_int($port) )
-			if ( $port )
-				$this->_port = $port;
+		$port = intval($port);
+		assert ( 0<=$port && $port<65536 );
+		$this->_port = ( $port==0 ) ? 9312 : $port;
 		$this->_path = '';
-
 	}
 
 	/// set server connection timeout (0 to remove)
@@ -591,14 +331,14 @@ function _Connect ()
 			$fp = @fsockopen ( $host, $port, $errno, $errstr );
 		else
 			$fp = @fsockopen ( $host, $port, $errno, $errstr, $this->_timeout );
-		
+
 		if ( !$fp )
 		{
 			if ( $this->_path )
 				$location = $this->_path;
 			else
 				$location = "{$this->_host}:{$this->_port}";
-			
+
 			$errstr = trim ( $errstr );
 			$this->_error = "connection to $location failed (errno=$errno, msg=$errstr)";
 			$this->_connerror = true;
@@ -664,23 +404,23 @@ function _GetResponse ( $fp, $client_ver )
 		}
 
 		// check status
-		if ( $status==SEARCHD_WARNING )
+		if ( $status==self::SEARCHD_WARNING )
 		{
 			list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) );
 			$this->_warning = substr ( $response, 4, $wlen );
 			return substr ( $response, 4+$wlen );
 		}
-		if ( $status==SEARCHD_ERROR )
+		if ( $status==self::SEARCHD_ERROR )
 		{
 			$this->_error = "searchd error: " . substr ( $response, 4 );
 			return false;
 		}
-		if ( $status==SEARCHD_RETRY )
+		if ( $status==self::SEARCHD_RETRY )
 		{
 			$this->_error = "temporary searchd error: " . substr ( $response, 4 );
 			return false;
 		}
-		if ( $status!=SEARCHD_OK )
+		if ( $status!=self::SEARCHD_OK )
 		{
 			$this->_error = "unknown status code '$status'";
 			return false;
@@ -729,20 +469,20 @@ function SetMaxQueryTime ( $max )
 	/// set matching mode
 	function SetMatchMode ( $mode )
 	{
-		assert ( $mode==SPH_MATCH_ALL
-			|| $mode==SPH_MATCH_ANY
-			|| $mode==SPH_MATCH_PHRASE
-			|| $mode==SPH_MATCH_BOOLEAN
-			|| $mode==SPH_MATCH_EXTENDED
-			|| $mode==SPH_MATCH_FULLSCAN
-			|| $mode==SPH_MATCH_EXTENDED2 );
+		assert ( $mode==self::SPH_MATCH_ALL
+			|| $mode==self::SPH_MATCH_ANY
+			|| $mode==self::SPH_MATCH_PHRASE
+			|| $mode==self::SPH_MATCH_BOOLEAN
+			|| $mode==self::SPH_MATCH_EXTENDED
+			|| $mode==self::SPH_MATCH_FULLSCAN
+			|| $mode==self::SPH_MATCH_EXTENDED2 );
 		$this->_mode = $mode;
 	}
 
 	/// set ranking mode
 	function SetRankingMode ( $ranker, $rankexpr="" )
 	{
-		assert ( $ranker===0 || $ranker>=1 && $ranker<SPH_RANK_TOTAL );
+		assert ( $ranker===0 || $ranker>=1 && $ranker<self::SPH_RANK_TOTAL );
 		assert ( is_string($rankexpr) );
 		$this->_ranker = $ranker;
 		$this->_rankexpr = $rankexpr;
@@ -752,14 +492,14 @@ function SetRankingMode ( $ranker, $rankexpr="" )
 	function SetSortMode ( $mode, $sortby="" )
 	{
 		assert (
-			$mode==SPH_SORT_RELEVANCE ||
-			$mode==SPH_SORT_ATTR_DESC ||
-			$mode==SPH_SORT_ATTR_ASC ||
-			$mode==SPH_SORT_TIME_SEGMENTS ||
-			$mode==SPH_SORT_EXTENDED ||
-			$mode==SPH_SORT_EXPR );
+			$mode==self::SPH_SORT_RELEVANCE ||
+			$mode==self::SPH_SORT_ATTR_DESC ||
+			$mode==self::SPH_SORT_ATTR_ASC ||
+			$mode==self::SPH_SORT_TIME_SEGMENTS ||
+			$mode==self::SPH_SORT_EXTENDED ||
+			$mode==self::SPH_SORT_EXPR );
 		assert ( is_string($sortby) );
-		assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 );
+		assert ( $mode==self::SPH_SORT_RELEVANCE || strlen($sortby)>0 );
 
 		$this->_sort = $mode;
 		$this->_sortby = $sortby;
@@ -824,7 +564,7 @@ function SetFilter ( $attribute, $values, $exclude=false )
 			foreach ( $values as $value )
 				assert ( is_numeric($value) );
 
-			$this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values );
+			$this->_filters[] = array ( "type"=>self::SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values );
 		}
 	}
 
@@ -837,7 +577,7 @@ function SetFilterRange ( $attribute, $min, $max, $exclude=false )
 		assert ( is_numeric($max) );
 		assert ( $min<=$max );
 
-		$this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
+		$this->_filters[] = array ( "type"=>self::SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
 	}
 
 	/// set float range filter
@@ -849,7 +589,7 @@ function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false )
 		assert ( is_float($max) );
 		assert ( $min<=$max );
 
-		$this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
+		$this->_filters[] = array ( "type"=>self::SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
 	}
 
 	/// setup anchor point for geosphere distance calculations
@@ -870,12 +610,12 @@ function SetGroupBy ( $attribute, $func, $groupsort="@group desc" )
 	{
 		assert ( is_string($attribute) );
 		assert ( is_string($groupsort) );
-		assert ( $func==SPH_GROUPBY_DAY
-			|| $func==SPH_GROUPBY_WEEK
-			|| $func==SPH_GROUPBY_MONTH
-			|| $func==SPH_GROUPBY_YEAR
-			|| $func==SPH_GROUPBY_ATTR
-			|| $func==SPH_GROUPBY_ATTRPAIR );
+		assert ( $func==self::SPH_GROUPBY_DAY
+			|| $func==self::SPH_GROUPBY_WEEK
+			|| $func==self::SPH_GROUPBY_MONTH
+			|| $func==self::SPH_GROUPBY_YEAR
+			|| $func==self::SPH_GROUPBY_ATTR
+			|| $func==self::SPH_GROUPBY_ATTRPAIR );
 
 		$this->_groupby = $attribute;
 		$this->_groupfunc = $func;
@@ -912,7 +652,7 @@ function SetArrayResult ( $arrayresult )
 	function SetOverride ( $attrname, $attrtype, $values )
 	{
 		assert ( is_string ( $attrname ) );
-		assert ( in_array ( $attrtype, array ( SPH_ATTR_INTEGER, SPH_ATTR_TIMESTAMP, SPH_ATTR_BOOL, SPH_ATTR_FLOAT, SPH_ATTR_BIGINT ) ) );
+		assert ( in_array ( $attrtype, array ( self::SPH_ATTR_INTEGER, self::SPH_ATTR_TIMESTAMP, self::SPH_ATTR_BOOL, self::SPH_ATTR_FLOAT, self::SPH_ATTR_BIGINT ) ) );
 		assert ( is_array ( $values ) );
 
 		$this->_overrides[$attrname] = array ( "attr"=>$attrname, "type"=>$attrtype, "values"=>$values );
@@ -938,7 +678,7 @@ function ResetFilters ()
 	function ResetGroupBy ()
 	{
 		$this->_groupby		= "";
-		$this->_groupfunc	= SPH_GROUPBY_DAY;
+		$this->_groupfunc	= self::SPH_GROUPBY_DAY;
 		$this->_groupsort	= "@group desc";
 		$this->_groupdistinct= "";
 	}
@@ -966,7 +706,7 @@ function Query ( $query, $index="*", $comment="" )
 
 		$this->_error = $results[0]["error"];
 		$this->_warning = $results[0]["warning"];
-		if ( $results[0]["status"]==SEARCHD_ERROR )
+		if ( $results[0]["status"]==self::SEARCHD_ERROR )
 			return false;
 		else
 			return $results[0];
@@ -989,7 +729,7 @@ function AddQuery ( $query, $index="*", $comment="" )
 
 		// build request
 		$req = pack ( "NNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker );
-		if ( $this->_ranker==SPH_RANK_EXPR )
+		if ( $this->_ranker==self::SPH_RANK_EXPR )
 			$req .= pack ( "N", strlen($this->_rankexpr) ) . $this->_rankexpr;
 		$req .= pack ( "N", $this->_sort ); // (deprecated) sort mode
 		$req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby;
@@ -999,7 +739,7 @@ function AddQuery ( $query, $index="*", $comment="" )
 			$req .= pack ( "N", (int)$weight );
 		$req .= pack ( "N", strlen($index) ) . $index; // indexes
 		$req .= pack ( "N", 1 ); // id64 range marker
-		$req .= sphPackU64 ( $this->_min_id ) . sphPackU64 ( $this->_max_id ); // id64 range
+		$req .= $this->Utils->sphPackU64 ( $this->_min_id ) . $this->Utils->sphPackU64 ( $this->_max_id ); // id64 range
 
 		// filters
 		$req .= pack ( "N", count($this->_filters) );
@@ -1009,17 +749,17 @@ function AddQuery ( $query, $index="*", $comment="" )
 			$req .= pack ( "N", $filter["type"] );
 			switch ( $filter["type"] )
 			{
-				case SPH_FILTER_VALUES:
+				case self::SPH_FILTER_VALUES:
 					$req .= pack ( "N", count($filter["values"]) );
 					foreach ( $filter["values"] as $value )
-						$req .= sphPackI64 ( $value );
+						$req .= $this->Utils->sphPackI64 ( $value );
 					break;
 
-				case SPH_FILTER_RANGE:
-					$req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] );
+				case self::SPH_FILTER_RANGE:
+					$req .= $this->Utils->sphPackI64 ( $filter["min"] ) . $this->Utils->sphPackI64 ( $filter["max"] );
 					break;
 
-				case SPH_FILTER_FLOATRANGE:
+				case self::SPH_FILTER_FLOATRANGE:
 					$req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
 					break;
 
@@ -1076,11 +816,11 @@ function AddQuery ( $query, $index="*", $comment="" )
 				assert ( is_numeric($id) );
 				assert ( is_numeric($val) );
 
-				$req .= sphPackU64 ( $id );
+				$req .= $this->Utils->sphPackU64 ( $id );
 				switch ( $entry["type"] )
 				{
-					case SPH_ATTR_FLOAT:	$req .= $this->_PackFloat ( $val ); break;
-					case SPH_ATTR_BIGINT:	$req .= sphPackI64 ( $val ); break;
+					case self::SPH_ATTR_FLOAT:	$req .= $this->_PackFloat ( $val ); break;
+					case self::SPH_ATTR_BIGINT:	$req .= $this->Utils->sphPackI64 ( $val ); break;
 					default:				$req .= pack ( "N", $val ); break;
 				}
 			}
@@ -1119,10 +859,10 @@ function RunQueries ()
 		$nreqs = count($this->_reqs);
 		$req = join ( "", $this->_reqs );
 		$len = 8+strlen($req);
-		$req = pack ( "nnNNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs ) . $req; // add header
+		$req = pack ( "nnNNN", self::SEARCHD_COMMAND_SEARCH, self::VER_COMMAND_SEARCH, $len, 0, $nreqs ) . $req; // add header
 
 		if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
-			 !( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) )
+			 !( $response = $this->_GetResponse ( $fp, self::VER_COMMAND_SEARCH ) ) )
 		{
 			$this->_MBPop ();
 			return false;
@@ -1153,12 +893,12 @@ function _ParseSearchResponse ( $response, $nreqs )
 			// extract status
 			list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
 			$result["status"] = $status;
-			if ( $status!=SEARCHD_OK )
+			if ( $status!=self::SEARCHD_OK )
 			{
 				list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
 				$message = substr ( $response, $p, $len ); $p += $len;
 
-				if ( $status==SEARCHD_WARNING )
+				if ( $status==self::SEARCHD_WARNING )
 				{
 					$result["warning"] = $message;
 				} else
@@ -1204,7 +944,7 @@ function _ParseSearchResponse ( $response, $nreqs )
 				// parse document id and weight
 				if ( $id64 )
 				{
-					$doc = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
+					$doc = $this->Utils->sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
 					list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
 				}
 				else
@@ -1212,7 +952,7 @@ function _ParseSearchResponse ( $response, $nreqs )
 					list ( $doc, $weight ) = array_values ( unpack ( "N*N*",
 						substr ( $response, $p, 8 ) ) );
 					$p += 8;
-					$doc = sphFixUint($doc);
+					$doc = $this->Utils->sphFixUint($doc);
 				}
 				$weight = sprintf ( "%u", $weight );
 
@@ -1227,48 +967,48 @@ function _ParseSearchResponse ( $response, $nreqs )
 				foreach ( $attrs as $attr=>$type )
 				{
 					// handle 64bit ints
-					if ( $type==SPH_ATTR_BIGINT )
+					if ( $type==self::SPH_ATTR_BIGINT )
 					{
-						$attrvals[$attr] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
+						$attrvals[$attr] = $this->Utils->sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
 						continue;
 					}
 
 					// handle floats
-					if ( $type==SPH_ATTR_FLOAT )
+					if ( $type==self::SPH_ATTR_FLOAT )
 					{
 						list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
-						list(,$fval) = unpack ( "f*", pack ( "L", $uval ) ); 
+						list(,$fval) = unpack ( "f*", pack ( "L", $uval ) );
 						$attrvals[$attr] = $fval;
 						continue;
 					}
 
 					// handle everything else as unsigned ints
 					list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
-					if ( $type==SPH_ATTR_MULTI )
+					if ( $type==self::SPH_ATTR_MULTI )
 					{
 						$attrvals[$attr] = array ();
 						$nvalues = $val;
 						while ( $nvalues-->0 && $p<$max )
 						{
 							list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
-							$attrvals[$attr][] = sphFixUint($val);
+							$attrvals[$attr][] = $this->Utils->sphFixUint($val);
 						}
-					} else if ( $type==SPH_ATTR_MULTI64 )
+					} else if ( $type==self::SPH_ATTR_MULTI64 )
 					{
 						$attrvals[$attr] = array ();
 						$nvalues = $val;
 						while ( $nvalues>0 && $p<$max )
 						{
-							$attrvals[$attr][] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
+							$attrvals[$attr][] = $this->Utils->sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
 							$nvalues -= 2;
 						}
-					} else if ( $type==SPH_ATTR_STRING )
+					} else if ( $type==self::SPH_ATTR_STRING )
 					{
 						$attrvals[$attr] = substr ( $response, $p, $val );
-						$p += $val;						
+						$p += $val;
 					} else
 					{
-						$attrvals[$attr] = sphFixUint($val);
+						$attrvals[$attr] = $this->Utils->sphFixUint($val);
 					}
 				}
 
@@ -1346,7 +1086,7 @@ function BuildExcerpts ( $docs, $index, $words, $opts=array() )
 		if ( !isset($opts["passage_boundary"]) )	$opts["passage_boundary"] = "none";
 		if ( !isset($opts["emit_zones"]) )			$opts["emit_zones"] = false;
 		if ( !isset($opts["load_files_scattered"]) )		$opts["load_files_scattered"] = false;
-		
+
 
 		/////////////////
 		// build request
@@ -1390,9 +1130,9 @@ function BuildExcerpts ( $docs, $index, $words, $opts=array() )
 		////////////////////////////
 
 		$len = strlen($req);
-		$req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header
+		$req = pack ( "nnN", self::SEARCHD_COMMAND_EXCERPT, self::VER_COMMAND_EXCERPT, $len ) . $req; // add header
 		if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
-			 !( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ) )
+			 !( $response = $this->_GetResponse ( $fp, self::VER_COMMAND_EXCERPT ) ) )
 		{
 			$this->_MBPop ();
 			return false;
@@ -1460,9 +1200,9 @@ function BuildKeywords ( $query, $index, $hits )
 		////////////////////////////
 
 		$len = strlen($req);
-		$req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header
+		$req = pack ( "nnN", self::SEARCHD_COMMAND_KEYWORDS, self::VER_COMMAND_KEYWORDS, $len ) . $req; // add header
 		if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
-			 !( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ) )
+			 !( $response = $this->_GetResponse ( $fp, self::VER_COMMAND_KEYWORDS ) ) )
 		{
 			$this->_MBPop ();
 			return false;
@@ -1565,7 +1305,7 @@ function UpdateAttributes ( $index, $attrs, $values, $mva=false )
 		$req .= pack ( "N", count($values) );
 		foreach ( $values as $id=>$entry )
 		{
-			$req .= sphPackU64 ( $id );
+			$req .= $this->Utils->sphPackU64 ( $id );
 			foreach ( $entry as $v )
 			{
 				$req .= pack ( "N", $mva ? count($v) : $v );
@@ -1583,14 +1323,14 @@ function UpdateAttributes ( $index, $attrs, $values, $mva=false )
 		}
 
 		$len = strlen($req);
-		$req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header
+		$req = pack ( "nnN", self::SEARCHD_COMMAND_UPDATE, self::VER_COMMAND_UPDATE, $len ) . $req; // add header
 		if ( !$this->_Send ( $fp, $req, $len+8 ) )
 		{
 			$this->_MBPop ();
 			return -1;
 		}
 
-		if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) ))
+		if (!( $response = $this->_GetResponse ( $fp, self::VER_COMMAND_UPDATE ) ))
 		{
 			$this->_MBPop ();
 			return -1;
@@ -1617,7 +1357,7 @@ function Open()
 			return false;
 
 		// command, command version = 0, body length = 4, body = 1
-		$req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 );
+		$req = pack ( "nnNN", self::SEARCHD_COMMAND_PERSIST, 0, 4, 1 );
 		if ( !$this->_Send ( $fp, $req, 12 ) )
 			return false;
 
@@ -1635,7 +1375,7 @@ function Close()
 
 		fclose ( $this->_socket );
 		$this->_socket = false;
-		
+
 		return true;
 	}
 
@@ -1652,9 +1392,9 @@ function Status ()
 			return false;
 		}
 
-		$req = pack ( "nnNN", SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1
+		$req = pack ( "nnNN", self::SEARCHD_COMMAND_STATUS, self::VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1
 		if ( !( $this->_Send ( $fp, $req, 12 ) ) ||
-			 !( $response = $this->_GetResponse ( $fp, VER_COMMAND_STATUS ) ) )
+			 !( $response = $this->_GetResponse ( $fp, self::VER_COMMAND_STATUS ) ) )
 		{
 			$this->_MBPop ();
 			return false;
@@ -1689,9 +1429,9 @@ function FlushAttributes ()
 			return -1;
 		}
 
-		$req = pack ( "nnN", SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0 ); // len=0
+		$req = pack ( "nnN", self::SEARCHD_COMMAND_FLUSHATTRS, self::VER_COMMAND_FLUSHATTRS, 0 ); // len=0
 		if ( !( $this->_Send ( $fp, $req, 8 ) ) ||
-			 !( $response = $this->_GetResponse ( $fp, VER_COMMAND_FLUSHATTRS ) ) )
+			 !( $response = $this->_GetResponse ( $fp, self::VER_COMMAND_FLUSHATTRS ) ) )
 		{
 			$this->_MBPop ();
 			return -1;
@@ -1709,5 +1449,5 @@ function FlushAttributes ()
 }
 
 //
-// $Id: sphinxapi.php 3439 2012-10-10 05:47:48Z kevg $
+// $Id: sphinxapi.php 3782 2013-04-06 18:22:58Z kevg $
 //
diff --git a/src/SphinxSearchApi/Utils.php b/src/SphinxSearchApi/Utils.php
new file mode 100644
index 0000000..ab6a581
--- /dev/null
+++ b/src/SphinxSearchApi/Utils.php
@@ -0,0 +1,262 @@
+<?php
+class SphinxSearchApi_Utils
+{
+    /// pack 64-bit signed
+    public function sphPackI64 ( $v )
+    {
+        assert ( is_numeric($v) );
+
+        // x64
+        if ( PHP_INT_SIZE>=8 )
+        {
+            $v = (int)$v;
+            return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
+        }
+
+        // x32, int
+        if ( is_int($v) )
+            return pack ( "NN", $v < 0 ? -1 : 0, $v );
+
+        // x32, bcmath
+        if ( function_exists("bcmul") )
+        {
+            if ( bccomp ( $v, 0 ) == -1 )
+                $v = bcadd ( "18446744073709551616", $v );
+            $h = bcdiv ( $v, "4294967296", 0 );
+            $l = bcmod ( $v, "4294967296" );
+            return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
+        }
+
+        // x32, no-bcmath
+        $p = max(0, strlen($v) - 13);
+        $lo = abs((float)substr($v, $p));
+        $hi = abs((float)substr($v, 0, $p));
+
+        $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
+        $q = floor($m/4294967296.0);
+        $l = $m - ($q*4294967296.0);
+        $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
+
+        if ( $v<0 )
+        {
+            if ( $l==0 )
+                $h = 4294967296.0 - $h;
+            else
+            {
+                $h = 4294967295.0 - $h;
+                $l = 4294967296.0 - $l;
+            }
+        }
+        return pack ( "NN", $h, $l );
+    }
+
+    /// pack 64-bit unsigned
+    public function sphPackU64 ( $v )
+    {
+        assert ( is_numeric($v) );
+
+        // x64
+        if ( PHP_INT_SIZE>=8 )
+        {
+            assert ( $v>=0 );
+
+            // x64, int
+            if ( is_int($v) )
+                return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
+
+            // x64, bcmath
+            if ( function_exists("bcmul") )
+            {
+                $h = bcdiv ( $v, 4294967296, 0 );
+                $l = bcmod ( $v, 4294967296 );
+                return pack ( "NN", $h, $l );
+            }
+
+            // x64, no-bcmath
+            $p = max ( 0, strlen($v) - 13 );
+            $lo = (int)substr ( $v, $p );
+            $hi = (int)substr ( $v, 0, $p );
+
+            $m = $lo + $hi*1316134912;
+            $l = $m % 4294967296;
+            $h = $hi*2328 + (int)($m/4294967296);
+
+            return pack ( "NN", $h, $l );
+        }
+
+        // x32, int
+        if ( is_int($v) )
+            return pack ( "NN", 0, $v );
+
+        // x32, bcmath
+        if ( function_exists("bcmul") )
+        {
+            $h = bcdiv ( $v, "4294967296", 0 );
+            $l = bcmod ( $v, "4294967296" );
+            return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
+        }
+
+        // x32, no-bcmath
+        $p = max(0, strlen($v) - 13);
+        $lo = (float)substr($v, $p);
+        $hi = (float)substr($v, 0, $p);
+
+        $m = $lo + $hi*1316134912.0;
+        $q = floor($m / 4294967296.0);
+        $l = $m - ($q * 4294967296.0);
+        $h = $hi*2328.0 + $q;
+
+        return pack ( "NN", $h, $l );
+    }
+
+    // unpack 64-bit unsigned
+    public function sphUnpackU64 ( $v )
+    {
+        list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
+
+        if ( PHP_INT_SIZE>=8 )
+        {
+            if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
+            if ( $lo<0 ) $lo += (1<<32);
+
+            // x64, int
+            if ( $hi<=2147483647 )
+                return ($hi<<32) + $lo;
+
+            // x64, bcmath
+            if ( function_exists("bcmul") )
+                return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
+
+            // x64, no-bcmath
+            $C = 100000;
+            $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
+            $l = (($hi % $C) << 32) + ($lo % $C);
+            if ( $l>$C )
+            {
+                $h += (int)($l / $C);
+                $l  = $l % $C;
+            }
+
+            if ( $h==0 )
+                return $l;
+            return sprintf ( "%d%05d", $h, $l );
+        }
+
+        // x32, int
+        if ( $hi==0 )
+        {
+            if ( $lo>0 )
+                return $lo;
+            return sprintf ( "%u", $lo );
+        }
+
+        $hi = sprintf ( "%u", $hi );
+        $lo = sprintf ( "%u", $lo );
+
+        // x32, bcmath
+        if ( function_exists("bcmul") )
+            return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
+
+        // x32, no-bcmath
+        $hi = (float)$hi;
+        $lo = (float)$lo;
+
+        $q = floor($hi/10000000.0);
+        $r = $hi - $q*10000000.0;
+        $m = $lo + $r*4967296.0;
+        $mq = floor($m/10000000.0);
+        $l = $m - $mq*10000000.0;
+        $h = $q*4294967296.0 + $r*429.0 + $mq;
+
+        $h = sprintf ( "%.0f", $h );
+        $l = sprintf ( "%07.0f", $l );
+        if ( $h=="0" )
+            return sprintf( "%.0f", (float)$l );
+        return $h . $l;
+    }
+
+    // unpack 64-bit signed
+    public function sphUnpackI64 ( $v )
+    {
+        list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
+
+        // x64
+        if ( PHP_INT_SIZE>=8 )
+        {
+            if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
+            if ( $lo<0 ) $lo += (1<<32);
+
+            return ($hi<<32) + $lo;
+        }
+
+        // x32, int
+        if ( $hi==0 )
+        {
+            if ( $lo>0 )
+                return $lo;
+            return sprintf ( "%u", $lo );
+        }
+        // x32, int
+        elseif ( $hi==-1 )
+        {
+            if ( $lo<0 )
+                return $lo;
+            return sprintf ( "%.0f", $lo - 4294967296.0 );
+        }
+
+        $neg = "";
+        $c = 0;
+        if ( $hi<0 )
+        {
+            $hi = ~$hi;
+            $lo = ~$lo;
+            $c = 1;
+            $neg = "-";
+        }
+
+        $hi = sprintf ( "%u", $hi );
+        $lo = sprintf ( "%u", $lo );
+
+        // x32, bcmath
+        if ( function_exists("bcmul") )
+            return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c );
+
+        // x32, no-bcmath
+        $hi = (float)$hi;
+        $lo = (float)$lo;
+
+        $q = floor($hi/10000000.0);
+        $r = $hi - $q*10000000.0;
+        $m = $lo + $r*4967296.0;
+        $mq = floor($m/10000000.0);
+        $l = $m - $mq*10000000.0 + $c;
+        $h = $q*4294967296.0 + $r*429.0 + $mq;
+        if ( $l==10000000 )
+        {
+            $l = 0;
+            $h += 1;
+        }
+
+        $h = sprintf ( "%.0f", $h );
+        $l = sprintf ( "%07.0f", $l );
+        if ( $h=="0" )
+            return $neg . sprintf( "%.0f", (float)$l );
+        return $neg . $h . $l;
+    }
+
+
+    public function sphFixUint ( $value )
+    {
+        if ( PHP_INT_SIZE>=8 )
+        {
+            // x64 route, workaround broken unpack() in 5.2.2+
+            if ( $value<0 ) $value += (1<<32);
+            return $value;
+        }
+        else
+        {
+            // x32 route, workaround php signed/unsigned braindamage
+            return sprintf ( "%u", $value );
+        }
+    }
+}
\ No newline at end of file