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