📄 sphinxapi.php
字号:
<?php
//
// $Id$
//
//
// Copyright (c) 2001-2008, Andrew Aksyonoff. All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License. You should have
// received a copy of the GPL license along with this program; if you
// did not, you can find it at http://www.gnu.org/
//
/////////////////////////////////////////////////////////////////////////////
// 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 );
/// current client-side command implementation versions
define ( "VER_COMMAND_SEARCH", 0x113 );
define ( "VER_COMMAND_EXCERPT", 0x100 );
define ( "VER_COMMAND_UPDATE", 0x101 );
define ( "VER_COMMAND_KEYWORDS", 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
/// 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_MULTI", 0x40000000 );
/// 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 );
/// portably pack numeric to 64 unsigned bits, network order
function sphPack64 ( $v )
{
assert ( is_numeric($v) );
// x64 route
if ( PHP_INT_SIZE>=8 )
{
$i = (int)$v;
return pack ( "NN", $i>>32, $i&((1<<32)-1) );
}
// x32 route, bcmath
$x = "4294967296";
if ( function_exists("bcmul") )
{
$h = bcdiv ( $v, $x, 0 );
$l = bcmod ( $v, $x );
return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
}
// x32 route, 15 or less decimal digits
// we can use float, because its actually double and has 52 precision bits
if ( strlen($v)<=15 )
{
$f = (float)$v;
$h = (int)($f/$x);
$l = (int)($f-$x*$h);
return pack ( "NN", $h, $l );
}
// x32 route, 16 or more decimal digits
// well, let me know if you *really* need this
die ( "INTERNAL ERROR: packing more than 15-digit numeric on 32-bit PHP is not implemented yet (contact support)" );
}
/// portably unpack 64 unsigned bits, network order to numeric
function sphUnpack64 ( $v )
{
list($h,$l) = array_values ( unpack ( "N*N*", $v ) );
// x64 route
if ( PHP_INT_SIZE>=8 )
{
if ( $h<0 ) $h += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
if ( $l<0 ) $l += (1<<32);
return ($h<<32) + $l;
}
// x32 route
$h = sprintf ( "%u", $h );
$l = sprintf ( "%u", $l );
$x = "4294967296";
// bcmath
if ( function_exists("bcmul") )
return bcadd ( $l, bcmul ( $x, $h ) );
// no bcmath, 15 or less decimal digits
// we can use float, because its actually double and has 52 precision bits
if ( $h<1048576 )
{
$f = ((float)$h)*$x + (float)$l;
return sprintf ( "%.0f", $f ); // builtin conversion is only about 39-40 bits precise!
}
// x32 route, 16 or more decimal digits
// well, let me know if you *really* need this
die ( "INTERNAL ERROR: unpacking more than 15-digit numeric on 32-bit PHP is not implemented yet (contact support)" );
}
/// sphinx searchd client class
class SphinxClient
{
var $_host; ///< searchd host (default is "localhost")
var $_port; ///< searchd port (default is 3312)
var $_offset; ///< how many records to seek from result-set start (default is 0)
var $_limit; ///< how many records to return from result-set starting at offset (default is 20)
var $_mode; ///< query matching mode (default is SPH_MATCH_ALL)
var $_weights; ///< per-field weights (default is 1 for all fields)
var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
var $_sortby; ///< attribute to sort by (defualt is "")
var $_min_id; ///< min ID to match (default is 0, which means no limit)
var $_max_id; ///< max ID to match (default is 0, which means no limit)
var $_filters; ///< search filters
var $_groupby; ///< group-by attribute name
var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with)
var $_groupsort; ///< group-by sorting clause (to sort groups in result set with)
var $_groupdistinct;///< group-by count-distinct attribute
var $_maxmatches; ///< max matches to retrieve
var $_cutoff; ///< cutoff to stop searching at (default is 0)
var $_retrycount; ///< distributed retries count
var $_retrydelay; ///< distributed retries delay
var $_anchor; ///< geographical anchor point
var $_indexweights; ///< per-index weights
var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit)
var $_fieldweights; ///< per-field-name weights
var $_error; ///< last error message
var $_warning; ///< last warning message
var $_reqs; ///< requests array for multi-query
var $_mbenc; ///< stored mbstring encoding
var $_arrayresult; ///< whether $result["matches"] should be a hash or an array
/////////////////////////////////////////////////////////////////////////////
// common stuff
/////////////////////////////////////////////////////////////////////////////
/// create a new client object and fill defaults
function SphinxClient ()
{
// per-client-object settings
$this->_host = "localhost";
$this->_port = 3312;
// per-query settings
$this->_offset = 0;
$this->_limit = 20;
$this->_mode = SPH_MATCH_ALL;
$this->_weights = array ();
$this->_sort = SPH_SORT_RELEVANCE;
$this->_sortby = "";
$this->_min_id = 0;
$this->_max_id = 0;
$this->_filters = array ();
$this->_groupby = "";
$this->_groupfunc = SPH_GROUPBY_DAY;
$this->_groupsort = "@group desc";
$this->_groupdistinct= "";
$this->_maxmatches = 1000;
$this->_cutoff = 0;
$this->_retrycount = 0;
$this->_retrydelay = 0;
$this->_anchor = array ();
$this->_indexweights= array ();
$this->_ranker = SPH_RANK_PROXIMITY_BM25;
$this->_maxquerytime= 0;
$this->_fieldweights= array();
$this->_error = ""; // per-reply fields (for single-query case)
$this->_warning = "";
$this->_reqs = array (); // requests storage (for multi-query case)
$this->_mbenc = "";
$this->_arrayresult = false;
}
/// get last error message (string)
function GetLastError ()
{
return $this->_error;
}
/// get last warning message (string)
function GetLastWarning ()
{
return $this->_warning;
}
/// set searchd host name (string) and port (integer)
function SetServer ( $host, $port )
{
assert ( is_string($host) );
assert ( is_int($port) );
$this->_host = $host;
$this->_port = $port;
}
/////////////////////////////////////////////////////////////////////////////
/// enter mbstring workaround mode
function _MBPush ()
{
$this->_mbenc = "";
if ( ini_get ( "mbstring.func_overload" ) & 2 )
{
$this->_mbenc = mb_internal_encoding();
mb_internal_encoding ( "latin1" );
}
}
/// leave mbstring workaround mode
function _MBPop ()
{
if ( $this->_mbenc )
mb_internal_encoding ( $this->_mbenc );
}
/// connect to searchd server
function _Connect ()
{
return fopen('php://stdout', 'w');
}
function _OldConnect()
{
if (!( $fp = @fsockopen ( $this->_host, $this->_port ) ) )
{
$this->_error = "connection to {$this->_host}:{$this->_port} failed";
return false;
}
// check version
list(,$v) = unpack ( "N*", fread ( $fp, 4 ) );
$v = (int)$v;
if ( $v<1 )
{
fclose ( $fp );
$this->_error = "expected searchd protocol version 1+, got version '$v'";
return false;
}
// all ok, send my version
fwrite ( $fp, pack ( "N", 1 ) );
return $fp;
}
/// get and check response packet from searchd server
function _GetResponse ( $fp, $client_ver )
{
return false;
}
function _OldGetResponse ( $fp, $client_ver )
{
$response = "";
$len = 0;
$header = fread ( $fp, 8 );
if ( strlen($header)==8 )
{
list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) );
$left = $len;
while ( $left>0 && !feof($fp) )
{
$chunk = fread ( $fp, $left );
if ( $chunk )
{
$response .= $chunk;
$left -= strlen($chunk);
}
}
}
fclose ( $fp );
// check response
$read = strlen ( $response );
if ( !$response || $read!=$len )
{
$this->_error = $len
? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
: "received zero-sized searchd response";
return false;
}
// check status
if ( $status==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 )
{
$this->_error = "searchd error: " . substr ( $response, 4 );
return false;
}
if ( $status==SEARCHD_RETRY )
{
$this->_error = "temporary searchd error: " . substr ( $response, 4 );
return false;
}
if ( $status!=SEARCHD_OK )
{
$this->_error = "unknown status code '$status'";
return false;
}
// check version
if ( $ver<$client_ver )
{
$this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work",
$ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff );
}
return $response;
}
/////////////////////////////////////////////////////////////////////////////
// searching
/////////////////////////////////////////////////////////////////////////////
/// set offset and count into result set,
/// and optionally set max-matches and cutoff limits
function SetLimits ( $offset, $limit, $max=0, $cutoff=0 )
{
assert ( is_int($offset) );
assert ( is_int($limit) );
assert ( $offset>=0 );
assert ( $limit>0 );
assert ( $max>=0 );
$this->_offset = $offset;
$this->_limit = $limit;
if ( $max>0 )
$this->_maxmatches = $max;
if ( $cutoff>0 )
$this->_cutoff = $cutoff;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -