10Syaroslav@ivinco.com<?php 20Syaroslav@ivinco.com 30Syaroslav@ivinco.com// 40Syaroslav@ivinco.com// $Id: sphinxapi.php 2055 2009-11-06 23:09:58Z shodan $ 50Syaroslav@ivinco.com// 60Syaroslav@ivinco.com 70Syaroslav@ivinco.com// 80Syaroslav@ivinco.com// Copyright (c) 2001-2008, Andrew Aksyonoff. All rights reserved. 90Syaroslav@ivinco.com// 100Syaroslav@ivinco.com// This program is free software; you can redistribute it and/or modify 110Syaroslav@ivinco.com// it under the terms of the GNU General Public License. You should have 120Syaroslav@ivinco.com// received a copy of the GPL license along with this program; if you 130Syaroslav@ivinco.com// did not, you can find it at http://www.gnu.org/ 140Syaroslav@ivinco.com// 150Syaroslav@ivinco.com 160Syaroslav@ivinco.com///////////////////////////////////////////////////////////////////////////// 170Syaroslav@ivinco.com// PHP version of Sphinx searchd client (PHP API) 180Syaroslav@ivinco.com///////////////////////////////////////////////////////////////////////////// 190Syaroslav@ivinco.com 200Syaroslav@ivinco.com/// known searchd commands 210Syaroslav@ivinco.comdefine ( "SEARCHD_COMMAND_SEARCH", 0 ); 220Syaroslav@ivinco.comdefine ( "SEARCHD_COMMAND_EXCERPT", 1 ); 230Syaroslav@ivinco.comdefine ( "SEARCHD_COMMAND_UPDATE", 2 ); 240Syaroslav@ivinco.comdefine ( "SEARCHD_COMMAND_KEYWORDS",3 ); 250Syaroslav@ivinco.comdefine ( "SEARCHD_COMMAND_PERSIST", 4 ); 260Syaroslav@ivinco.comdefine ( "SEARCHD_COMMAND_STATUS", 5 ); 270Syaroslav@ivinco.comdefine ( "SEARCHD_COMMAND_QUERY", 6 ); 280Syaroslav@ivinco.com 290Syaroslav@ivinco.com/// current client-side command implementation versions 300Syaroslav@ivinco.comdefine ( "VER_COMMAND_SEARCH", 0x116 ); 310Syaroslav@ivinco.comdefine ( "VER_COMMAND_EXCERPT", 0x100 ); 320Syaroslav@ivinco.comdefine ( "VER_COMMAND_UPDATE", 0x102 ); 330Syaroslav@ivinco.comdefine ( "VER_COMMAND_KEYWORDS", 0x100 ); 340Syaroslav@ivinco.comdefine ( "VER_COMMAND_STATUS", 0x100 ); 350Syaroslav@ivinco.comdefine ( "VER_COMMAND_QUERY", 0x100 ); 360Syaroslav@ivinco.com 370Syaroslav@ivinco.com/// known searchd status codes 380Syaroslav@ivinco.comdefine ( "SEARCHD_OK", 0 ); 390Syaroslav@ivinco.comdefine ( "SEARCHD_ERROR", 1 ); 400Syaroslav@ivinco.comdefine ( "SEARCHD_RETRY", 2 ); 410Syaroslav@ivinco.comdefine ( "SEARCHD_WARNING", 3 ); 420Syaroslav@ivinco.com 430Syaroslav@ivinco.com/// known match modes 440Syaroslav@ivinco.comdefine ( "SPH_MATCH_ALL", 0 ); 450Syaroslav@ivinco.comdefine ( "SPH_MATCH_ANY", 1 ); 460Syaroslav@ivinco.comdefine ( "SPH_MATCH_PHRASE", 2 ); 470Syaroslav@ivinco.comdefine ( "SPH_MATCH_BOOLEAN", 3 ); 480Syaroslav@ivinco.comdefine ( "SPH_MATCH_EXTENDED", 4 ); 490Syaroslav@ivinco.comdefine ( "SPH_MATCH_FULLSCAN", 5 ); 500Syaroslav@ivinco.comdefine ( "SPH_MATCH_EXTENDED2", 6 ); // extended engine V2 (TEMPORARY, WILL BE REMOVED) 510Syaroslav@ivinco.com 520Syaroslav@ivinco.com/// known ranking modes (ext2 only) 530Syaroslav@ivinco.comdefine ( "SPH_RANK_PROXIMITY_BM25", 0 ); ///< default mode, phrase proximity major factor and BM25 minor one 540Syaroslav@ivinco.comdefine ( "SPH_RANK_BM25", 1 ); ///< statistical mode, BM25 ranking only (faster but worse quality) 550Syaroslav@ivinco.comdefine ( "SPH_RANK_NONE", 2 ); ///< no ranking, all matches get a weight of 1 560Syaroslav@ivinco.comdefine ( "SPH_RANK_WORDCOUNT", 3 ); ///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts 570Syaroslav@ivinco.comdefine ( "SPH_RANK_PROXIMITY", 4 ); 580Syaroslav@ivinco.comdefine ( "SPH_RANK_MATCHANY", 5 ); 590Syaroslav@ivinco.comdefine ( "SPH_RANK_FIELDMASK", 6 ); 600Syaroslav@ivinco.com 610Syaroslav@ivinco.com/// known sort modes 620Syaroslav@ivinco.comdefine ( "SPH_SORT_RELEVANCE", 0 ); 630Syaroslav@ivinco.comdefine ( "SPH_SORT_ATTR_DESC", 1 ); 640Syaroslav@ivinco.comdefine ( "SPH_SORT_ATTR_ASC", 2 ); 650Syaroslav@ivinco.comdefine ( "SPH_SORT_TIME_SEGMENTS", 3 ); 660Syaroslav@ivinco.comdefine ( "SPH_SORT_EXTENDED", 4 ); 670Syaroslav@ivinco.comdefine ( "SPH_SORT_EXPR", 5 ); 680Syaroslav@ivinco.com 690Syaroslav@ivinco.com/// known filter types 700Syaroslav@ivinco.comdefine ( "SPH_FILTER_VALUES", 0 ); 710Syaroslav@ivinco.comdefine ( "SPH_FILTER_RANGE", 1 ); 720Syaroslav@ivinco.comdefine ( "SPH_FILTER_FLOATRANGE", 2 ); 730Syaroslav@ivinco.com 740Syaroslav@ivinco.com/// known attribute types 750Syaroslav@ivinco.comdefine ( "SPH_ATTR_INTEGER", 1 ); 760Syaroslav@ivinco.comdefine ( "SPH_ATTR_TIMESTAMP", 2 ); 770Syaroslav@ivinco.comdefine ( "SPH_ATTR_ORDINAL", 3 ); 780Syaroslav@ivinco.comdefine ( "SPH_ATTR_BOOL", 4 ); 790Syaroslav@ivinco.comdefine ( "SPH_ATTR_FLOAT", 5 ); 800Syaroslav@ivinco.comdefine ( "SPH_ATTR_BIGINT", 6 ); 810Syaroslav@ivinco.comdefine ( "SPH_ATTR_MULTI", 0x40000000 ); 820Syaroslav@ivinco.com 830Syaroslav@ivinco.com/// known grouping functions 840Syaroslav@ivinco.comdefine ( "SPH_GROUPBY_DAY", 0 ); 850Syaroslav@ivinco.comdefine ( "SPH_GROUPBY_WEEK", 1 ); 860Syaroslav@ivinco.comdefine ( "SPH_GROUPBY_MONTH", 2 ); 870Syaroslav@ivinco.comdefine ( "SPH_GROUPBY_YEAR", 3 ); 880Syaroslav@ivinco.comdefine ( "SPH_GROUPBY_ATTR", 4 ); 890Syaroslav@ivinco.comdefine ( "SPH_GROUPBY_ATTRPAIR", 5 ); 900Syaroslav@ivinco.com 910Syaroslav@ivinco.com// important properties of PHP's integers: 920Syaroslav@ivinco.com// - always signed (one bit short of PHP_INT_SIZE) 930Syaroslav@ivinco.com// - conversion from string to int is saturated 940Syaroslav@ivinco.com// - float is double 950Syaroslav@ivinco.com// - div converts arguments to floats 960Syaroslav@ivinco.com// - mod converts arguments to ints 970Syaroslav@ivinco.com 980Syaroslav@ivinco.com// the packing code below works as follows: 990Syaroslav@ivinco.com// - when we got an int, just pack it 1000Syaroslav@ivinco.com// if performance is a problem, this is the branch users should aim for 1010Syaroslav@ivinco.com// 1020Syaroslav@ivinco.com// - otherwise, we got a number in string form 1030Syaroslav@ivinco.com// this might be due to different reasons, but we assume that this is 1040Syaroslav@ivinco.com// because it didn't fit into PHP int 1050Syaroslav@ivinco.com// 1060Syaroslav@ivinco.com// - factor the string into high and low ints for packing 1070Syaroslav@ivinco.com// - if we have bcmath, then it is used 1080Syaroslav@ivinco.com// - if we don't, we have to do it manually (this is the fun part) 1090Syaroslav@ivinco.com// 1100Syaroslav@ivinco.com// - x64 branch does factoring using ints 1110Syaroslav@ivinco.com// - x32 (ab)uses floats, since we can't fit unsigned 32-bit number into an int 1120Syaroslav@ivinco.com// 1130Syaroslav@ivinco.com// unpacking routines are pretty much the same. 1140Syaroslav@ivinco.com// - return ints if we can 1150Syaroslav@ivinco.com// - otherwise format number into a string 1160Syaroslav@ivinco.com 1170Syaroslav@ivinco.com/// pack 64-bit signed 1180Syaroslav@ivinco.comfunction sphPackI64 ( $v ) 1190Syaroslav@ivinco.com{ 1200Syaroslav@ivinco.com assert ( is_numeric($v) ); 1210Syaroslav@ivinco.com 1220Syaroslav@ivinco.com // x64 1230Syaroslav@ivinco.com if ( PHP_INT_SIZE>=8 ) 1240Syaroslav@ivinco.com { 1250Syaroslav@ivinco.com $v = (int)$v; 1260Syaroslav@ivinco.com return pack ( "NN", $v>>32, $v&0xFFFFFFFF ); 1270Syaroslav@ivinco.com } 1280Syaroslav@ivinco.com 1290Syaroslav@ivinco.com // x32, int 1300Syaroslav@ivinco.com if ( is_int($v) ) 1310Syaroslav@ivinco.com return pack ( "NN", $v < 0 ? -1 : 0, $v ); 1320Syaroslav@ivinco.com 1330Syaroslav@ivinco.com // x32, bcmath 1340Syaroslav@ivinco.com if ( function_exists("bcmul") ) 1350Syaroslav@ivinco.com { 1360Syaroslav@ivinco.com if ( bccomp ( $v, 0 ) == -1 ) 1370Syaroslav@ivinco.com $v = bcadd ( "18446744073709551616", $v ); 1380Syaroslav@ivinco.com $h = bcdiv ( $v, "4294967296", 0 ); 1390Syaroslav@ivinco.com $l = bcmod ( $v, "4294967296" ); 1400Syaroslav@ivinco.com return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit 1410Syaroslav@ivinco.com } 1420Syaroslav@ivinco.com 1430Syaroslav@ivinco.com // x32, no-bcmath 1440Syaroslav@ivinco.com $p = max(0, strlen($v) - 13); 1450Syaroslav@ivinco.com $lo = abs((float)substr($v, $p)); 1460Syaroslav@ivinco.com $hi = abs((float)substr($v, 0, $p)); 1470Syaroslav@ivinco.com 1480Syaroslav@ivinco.com $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912 1490Syaroslav@ivinco.com $q = floor($m/4294967296.0); 1500Syaroslav@ivinco.com $l = $m - ($q*4294967296.0); 1510Syaroslav@ivinco.com $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328 1520Syaroslav@ivinco.com 1530Syaroslav@ivinco.com if ( $v<0 ) 1540Syaroslav@ivinco.com { 1550Syaroslav@ivinco.com if ( $l==0 ) 1560Syaroslav@ivinco.com $h = 4294967296.0 - $h; 1570Syaroslav@ivinco.com else 1580Syaroslav@ivinco.com { 1590Syaroslav@ivinco.com $h = 4294967295.0 - $h; 1600Syaroslav@ivinco.com $l = 4294967296.0 - $l; 1610Syaroslav@ivinco.com } 1620Syaroslav@ivinco.com } 1630Syaroslav@ivinco.com return pack ( "NN", $h, $l ); 1640Syaroslav@ivinco.com} 1650Syaroslav@ivinco.com 1660Syaroslav@ivinco.com/// pack 64-bit unsigned 1670Syaroslav@ivinco.comfunction sphPackU64 ( $v ) 1680Syaroslav@ivinco.com{ 1690Syaroslav@ivinco.com assert ( is_numeric($v) ); 1700Syaroslav@ivinco.com 1710Syaroslav@ivinco.com // x64 1720Syaroslav@ivinco.com if ( PHP_INT_SIZE>=8 ) 1730Syaroslav@ivinco.com { 1740Syaroslav@ivinco.com assert ( $v>=0 ); 1750Syaroslav@ivinco.com 1760Syaroslav@ivinco.com // x64, int 1770Syaroslav@ivinco.com if ( is_int($v) ) 1780Syaroslav@ivinco.com return pack ( "NN", $v>>32, $v&0xFFFFFFFF ); 1790Syaroslav@ivinco.com 1800Syaroslav@ivinco.com // x64, bcmath 1810Syaroslav@ivinco.com if ( function_exists("bcmul") ) 1820Syaroslav@ivinco.com { 1830Syaroslav@ivinco.com $h = bcdiv ( $v, 4294967296, 0 ); 1840Syaroslav@ivinco.com $l = bcmod ( $v, 4294967296 ); 1850Syaroslav@ivinco.com return pack ( "NN", $h, $l ); 1860Syaroslav@ivinco.com } 1870Syaroslav@ivinco.com 1880Syaroslav@ivinco.com // x64, no-bcmath 1890Syaroslav@ivinco.com $p = max ( 0, strlen($v) - 13 ); 1900Syaroslav@ivinco.com $lo = (int)substr ( $v, $p ); 1910Syaroslav@ivinco.com $hi = (int)substr ( $v, 0, $p ); 1920Syaroslav@ivinco.com 1930Syaroslav@ivinco.com $m = $lo + $hi*1316134912; 1940Syaroslav@ivinco.com $l = $m % 4294967296; 1950Syaroslav@ivinco.com $h = $hi*2328 + (int)($m/4294967296); 1960Syaroslav@ivinco.com 1970Syaroslav@ivinco.com return pack ( "NN", $h, $l ); 1980Syaroslav@ivinco.com } 1990Syaroslav@ivinco.com 2000Syaroslav@ivinco.com // x32, int 2010Syaroslav@ivinco.com if ( is_int($v) ) 2020Syaroslav@ivinco.com return pack ( "NN", 0, $v ); 2030Syaroslav@ivinco.com 2040Syaroslav@ivinco.com // x32, bcmath 2050Syaroslav@ivinco.com if ( function_exists("bcmul") ) 2060Syaroslav@ivinco.com { 2070Syaroslav@ivinco.com $h = bcdiv ( $v, "4294967296", 0 ); 2080Syaroslav@ivinco.com $l = bcmod ( $v, "4294967296" ); 2090Syaroslav@ivinco.com return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit 2100Syaroslav@ivinco.com } 2110Syaroslav@ivinco.com 2120Syaroslav@ivinco.com // x32, no-bcmath 2130Syaroslav@ivinco.com $p = max(0, strlen($v) - 13); 2140Syaroslav@ivinco.com $lo = (float)substr($v, $p); 2150Syaroslav@ivinco.com $hi = (float)substr($v, 0, $p); 2160Syaroslav@ivinco.com 2170Syaroslav@ivinco.com $m = $lo + $hi*1316134912.0; 2180Syaroslav@ivinco.com $q = floor($m / 4294967296.0); 2190Syaroslav@ivinco.com $l = $m - ($q * 4294967296.0); 2200Syaroslav@ivinco.com $h = $hi*2328.0 + $q; 2210Syaroslav@ivinco.com 2220Syaroslav@ivinco.com return pack ( "NN", $h, $l ); 2230Syaroslav@ivinco.com} 2240Syaroslav@ivinco.com 2250Syaroslav@ivinco.com// unpack 64-bit unsigned 2260Syaroslav@ivinco.comfunction sphUnpackU64 ( $v ) 2270Syaroslav@ivinco.com{ 2280Syaroslav@ivinco.com list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) ); 2290Syaroslav@ivinco.com 2300Syaroslav@ivinco.com if ( PHP_INT_SIZE>=8 ) 2310Syaroslav@ivinco.com { 2320Syaroslav@ivinco.com if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again 2330Syaroslav@ivinco.com if ( $lo<0 ) $lo += (1<<32); 2340Syaroslav@ivinco.com 2350Syaroslav@ivinco.com // x64, int 2360Syaroslav@ivinco.com if ( $hi<=2147483647 ) 2370Syaroslav@ivinco.com return ($hi<<32) + $lo; 2380Syaroslav@ivinco.com 2390Syaroslav@ivinco.com // x64, bcmath 2400Syaroslav@ivinco.com if ( function_exists("bcmul") ) 2410Syaroslav@ivinco.com return bcadd ( $lo, bcmul ( $hi, "4294967296" ) ); 2420Syaroslav@ivinco.com 2430Syaroslav@ivinco.com // x64, no-bcmath 2440Syaroslav@ivinco.com $C = 100000; 2450Syaroslav@ivinco.com $h = ((int)($hi / $C) << 32) + (int)($lo / $C); 2460Syaroslav@ivinco.com $l = (($hi % $C) << 32) + ($lo % $C); 2470Syaroslav@ivinco.com if ( $l>$C ) 2480Syaroslav@ivinco.com { 2490Syaroslav@ivinco.com $h += (int)($l / $C); 2500Syaroslav@ivinco.com $l = $l % $C; 2510Syaroslav@ivinco.com } 2520Syaroslav@ivinco.com 2530Syaroslav@ivinco.com if ( $h==0 ) 2540Syaroslav@ivinco.com return $l; 2550Syaroslav@ivinco.com return sprintf ( "%d%05d", $h, $l ); 2560Syaroslav@ivinco.com } 2570Syaroslav@ivinco.com 2580Syaroslav@ivinco.com // x32, int 2590Syaroslav@ivinco.com if ( $hi==0 ) 2600Syaroslav@ivinco.com { 2610Syaroslav@ivinco.com if ( $lo>0 ) 2620Syaroslav@ivinco.com return $lo; 2630Syaroslav@ivinco.com return sprintf ( "%u", $lo ); 2640Syaroslav@ivinco.com } 2650Syaroslav@ivinco.com 2660Syaroslav@ivinco.com $hi = sprintf ( "%u", $hi ); 2670Syaroslav@ivinco.com $lo = sprintf ( "%u", $lo ); 2680Syaroslav@ivinco.com 2690Syaroslav@ivinco.com // x32, bcmath 2700Syaroslav@ivinco.com if ( function_exists("bcmul") ) 2710Syaroslav@ivinco.com return bcadd ( $lo, bcmul ( $hi, "4294967296" ) ); 2720Syaroslav@ivinco.com 2730Syaroslav@ivinco.com // x32, no-bcmath 2740Syaroslav@ivinco.com $hi = (float)$hi; 2750Syaroslav@ivinco.com $lo = (float)$lo; 2760Syaroslav@ivinco.com 2770Syaroslav@ivinco.com $q = floor($hi/10000000.0); 2780Syaroslav@ivinco.com $r = $hi - $q*10000000.0; 2790Syaroslav@ivinco.com $m = $lo + $r*4967296.0; 2800Syaroslav@ivinco.com $mq = floor($m/10000000.0); 2810Syaroslav@ivinco.com $l = $m - $mq*10000000.0; 2820Syaroslav@ivinco.com $h = $q*4294967296.0 + $r*429.0 + $mq; 2830Syaroslav@ivinco.com 2840Syaroslav@ivinco.com $h = sprintf ( "%.0f", $h ); 2850Syaroslav@ivinco.com $l = sprintf ( "%07.0f", $l ); 2860Syaroslav@ivinco.com if ( $h=="0" ) 2870Syaroslav@ivinco.com return sprintf( "%.0f", (float)$l ); 2880Syaroslav@ivinco.com return $h . $l; 2890Syaroslav@ivinco.com} 2900Syaroslav@ivinco.com 2910Syaroslav@ivinco.com// unpack 64-bit signed 2920Syaroslav@ivinco.comfunction sphUnpackI64 ( $v ) 2930Syaroslav@ivinco.com{ 2940Syaroslav@ivinco.com list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) ); 2950Syaroslav@ivinco.com 2960Syaroslav@ivinco.com // x64 2970Syaroslav@ivinco.com if ( PHP_INT_SIZE>=8 ) 2980Syaroslav@ivinco.com { 2990Syaroslav@ivinco.com if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again 3000Syaroslav@ivinco.com if ( $lo<0 ) $lo += (1<<32); 3010Syaroslav@ivinco.com 3020Syaroslav@ivinco.com return ($hi<<32) + $lo; 3030Syaroslav@ivinco.com } 3040Syaroslav@ivinco.com 3050Syaroslav@ivinco.com // x32, int 3060Syaroslav@ivinco.com if ( $hi==0 ) 3070Syaroslav@ivinco.com { 3080Syaroslav@ivinco.com if ( $lo>0 ) 3090Syaroslav@ivinco.com return $lo; 3100Syaroslav@ivinco.com return sprintf ( "%u", $lo ); 3110Syaroslav@ivinco.com } 3120Syaroslav@ivinco.com // x32, int 3130Syaroslav@ivinco.com elseif ( $hi==-1 ) 3140Syaroslav@ivinco.com { 3150Syaroslav@ivinco.com if ( $lo<0 ) 3160Syaroslav@ivinco.com return $lo; 3170Syaroslav@ivinco.com return sprintf ( "%.0f", $lo - 4294967296.0 ); 3180Syaroslav@ivinco.com } 3190Syaroslav@ivinco.com 3200Syaroslav@ivinco.com $neg = ""; 3210Syaroslav@ivinco.com $c = 0; 3220Syaroslav@ivinco.com if ( $hi<0 ) 3230Syaroslav@ivinco.com { 3240Syaroslav@ivinco.com $hi = ~$hi; 3250Syaroslav@ivinco.com $lo = ~$lo; 3260Syaroslav@ivinco.com $c = 1; 3270Syaroslav@ivinco.com $neg = "-"; 3280Syaroslav@ivinco.com } 3290Syaroslav@ivinco.com 3300Syaroslav@ivinco.com $hi = sprintf ( "%u", $hi ); 3310Syaroslav@ivinco.com $lo = sprintf ( "%u", $lo ); 3320Syaroslav@ivinco.com 3330Syaroslav@ivinco.com // x32, bcmath 3340Syaroslav@ivinco.com if ( function_exists("bcmul") ) 3350Syaroslav@ivinco.com return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c ); 3360Syaroslav@ivinco.com 3370Syaroslav@ivinco.com // x32, no-bcmath 3380Syaroslav@ivinco.com $hi = (float)$hi; 3390Syaroslav@ivinco.com $lo = (float)$lo; 3400Syaroslav@ivinco.com 3410Syaroslav@ivinco.com $q = floor($hi/10000000.0); 3420Syaroslav@ivinco.com $r = $hi - $q*10000000.0; 3430Syaroslav@ivinco.com $m = $lo + $r*4967296.0; 3440Syaroslav@ivinco.com $mq = floor($m/10000000.0); 3450Syaroslav@ivinco.com $l = $m - $mq*10000000.0 + $c; 3460Syaroslav@ivinco.com $h = $q*4294967296.0 + $r*429.0 + $mq; 3470Syaroslav@ivinco.com if ( $l==10000000 ) 3480Syaroslav@ivinco.com { 3490Syaroslav@ivinco.com $l = 0; 3500Syaroslav@ivinco.com $h += 1; 3510Syaroslav@ivinco.com } 3520Syaroslav@ivinco.com 3530Syaroslav@ivinco.com $h = sprintf ( "%.0f", $h ); 3540Syaroslav@ivinco.com $l = sprintf ( "%07.0f", $l ); 3550Syaroslav@ivinco.com if ( $h=="0" ) 3560Syaroslav@ivinco.com return $neg . sprintf( "%.0f", (float)$l ); 3570Syaroslav@ivinco.com return $neg . $h . $l; 3580Syaroslav@ivinco.com} 3590Syaroslav@ivinco.com 3600Syaroslav@ivinco.com 3610Syaroslav@ivinco.comfunction sphFixUint ( $value ) 3620Syaroslav@ivinco.com{ 3630Syaroslav@ivinco.com if ( PHP_INT_SIZE>=8 ) 3640Syaroslav@ivinco.com { 3650Syaroslav@ivinco.com // x64 route, workaround broken unpack() in 5.2.2+ 3660Syaroslav@ivinco.com if ( $value<0 ) $value += (1<<32); 3670Syaroslav@ivinco.com return $value; 3680Syaroslav@ivinco.com } 3690Syaroslav@ivinco.com else 3700Syaroslav@ivinco.com { 3710Syaroslav@ivinco.com // x32 route, workaround php signed/unsigned braindamage 3720Syaroslav@ivinco.com return sprintf ( "%u", $value ); 3730Syaroslav@ivinco.com } 3740Syaroslav@ivinco.com} 3750Syaroslav@ivinco.com 376*133Sandreyif (!class_exists('SphinxClient')) { 3770Syaroslav@ivinco.com/// sphinx searchd client class 3780Syaroslav@ivinco.comclass SphinxClient 3790Syaroslav@ivinco.com{ 3800Syaroslav@ivinco.com var $_host; ///< searchd host (default is "localhost") 3810Syaroslav@ivinco.com var $_port; ///< searchd port (default is 9312) 3820Syaroslav@ivinco.com var $_offset; ///< how many records to seek from result-set start (default is 0) 3830Syaroslav@ivinco.com var $_limit; ///< how many records to return from result-set starting at offset (default is 20) 3840Syaroslav@ivinco.com var $_mode; ///< query matching mode (default is SPH_MATCH_ALL) 3850Syaroslav@ivinco.com var $_weights; ///< per-field weights (default is 1 for all fields) 3860Syaroslav@ivinco.com var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE) 3870Syaroslav@ivinco.com var $_sortby; ///< attribute to sort by (defualt is "") 3880Syaroslav@ivinco.com var $_min_id; ///< min ID to match (default is 0, which means no limit) 3890Syaroslav@ivinco.com var $_max_id; ///< max ID to match (default is 0, which means no limit) 3900Syaroslav@ivinco.com var $_filters; ///< search filters 3910Syaroslav@ivinco.com var $_groupby; ///< group-by attribute name 3920Syaroslav@ivinco.com var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with) 3930Syaroslav@ivinco.com var $_groupsort; ///< group-by sorting clause (to sort groups in result set with) 3940Syaroslav@ivinco.com var $_groupdistinct;///< group-by count-distinct attribute 3950Syaroslav@ivinco.com var $_maxmatches; ///< max matches to retrieve 3960Syaroslav@ivinco.com var $_cutoff; ///< cutoff to stop searching at (default is 0) 3970Syaroslav@ivinco.com var $_retrycount; ///< distributed retries count 3980Syaroslav@ivinco.com var $_retrydelay; ///< distributed retries delay 3990Syaroslav@ivinco.com var $_anchor; ///< geographical anchor point 4000Syaroslav@ivinco.com var $_indexweights; ///< per-index weights 4010Syaroslav@ivinco.com var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25) 4020Syaroslav@ivinco.com var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit) 4030Syaroslav@ivinco.com var $_fieldweights; ///< per-field-name weights 4040Syaroslav@ivinco.com var $_overrides; ///< per-query attribute values overrides 4050Syaroslav@ivinco.com var $_select; ///< select-list (attributes or expressions, with optional aliases) 4060Syaroslav@ivinco.com 4070Syaroslav@ivinco.com var $_error; ///< last error message 4080Syaroslav@ivinco.com var $_warning; ///< last warning message 4090Syaroslav@ivinco.com var $_connerror; ///< connection error vs remote error flag 4100Syaroslav@ivinco.com 4110Syaroslav@ivinco.com var $_reqs; ///< requests array for multi-query 4120Syaroslav@ivinco.com var $_mbenc; ///< stored mbstring encoding 4130Syaroslav@ivinco.com var $_arrayresult; ///< whether $result["matches"] should be a hash or an array 4140Syaroslav@ivinco.com var $_timeout; ///< connect timeout 4150Syaroslav@ivinco.com 4160Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 4170Syaroslav@ivinco.com // common stuff 4180Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 4190Syaroslav@ivinco.com 4200Syaroslav@ivinco.com /// create a new client object and fill defaults 4210Syaroslav@ivinco.com function SphinxClient () 4220Syaroslav@ivinco.com { 4230Syaroslav@ivinco.com // per-client-object settings 4240Syaroslav@ivinco.com $this->_host = "localhost"; 4250Syaroslav@ivinco.com $this->_port = 9312; 4260Syaroslav@ivinco.com $this->_path = false; 4270Syaroslav@ivinco.com $this->_socket = false; 4280Syaroslav@ivinco.com 4290Syaroslav@ivinco.com // per-query settings 4300Syaroslav@ivinco.com $this->_offset = 0; 4310Syaroslav@ivinco.com $this->_limit = 20; 4320Syaroslav@ivinco.com $this->_mode = SPH_MATCH_ALL; 4330Syaroslav@ivinco.com $this->_weights = array (); 4340Syaroslav@ivinco.com $this->_sort = SPH_SORT_RELEVANCE; 4350Syaroslav@ivinco.com $this->_sortby = ""; 4360Syaroslav@ivinco.com $this->_min_id = 0; 4370Syaroslav@ivinco.com $this->_max_id = 0; 4380Syaroslav@ivinco.com $this->_filters = array (); 4390Syaroslav@ivinco.com $this->_groupby = ""; 4400Syaroslav@ivinco.com $this->_groupfunc = SPH_GROUPBY_DAY; 4410Syaroslav@ivinco.com $this->_groupsort = "@group desc"; 4420Syaroslav@ivinco.com $this->_groupdistinct= ""; 4430Syaroslav@ivinco.com $this->_maxmatches = 1000; 4440Syaroslav@ivinco.com $this->_cutoff = 0; 4450Syaroslav@ivinco.com $this->_retrycount = 0; 4460Syaroslav@ivinco.com $this->_retrydelay = 0; 4470Syaroslav@ivinco.com $this->_anchor = array (); 4480Syaroslav@ivinco.com $this->_indexweights= array (); 4490Syaroslav@ivinco.com $this->_ranker = SPH_RANK_PROXIMITY_BM25; 4500Syaroslav@ivinco.com $this->_maxquerytime= 0; 4510Syaroslav@ivinco.com $this->_fieldweights= array(); 4520Syaroslav@ivinco.com $this->_overrides = array(); 4530Syaroslav@ivinco.com $this->_select = "*"; 4540Syaroslav@ivinco.com 4550Syaroslav@ivinco.com $this->_error = ""; // per-reply fields (for single-query case) 4560Syaroslav@ivinco.com $this->_warning = ""; 4570Syaroslav@ivinco.com $this->_connerror = false; 4580Syaroslav@ivinco.com 4590Syaroslav@ivinco.com $this->_reqs = array (); // requests storage (for multi-query case) 4600Syaroslav@ivinco.com $this->_mbenc = ""; 4610Syaroslav@ivinco.com $this->_arrayresult = false; 4620Syaroslav@ivinco.com $this->_timeout = 0; 4630Syaroslav@ivinco.com } 4640Syaroslav@ivinco.com 4650Syaroslav@ivinco.com function __destruct() 4660Syaroslav@ivinco.com { 4670Syaroslav@ivinco.com if ( $this->_socket !== false ) 4680Syaroslav@ivinco.com fclose ( $this->_socket ); 4690Syaroslav@ivinco.com } 4700Syaroslav@ivinco.com 4710Syaroslav@ivinco.com /// get last error message (string) 4720Syaroslav@ivinco.com function GetLastError () 4730Syaroslav@ivinco.com { 4740Syaroslav@ivinco.com return $this->_error; 4750Syaroslav@ivinco.com } 4760Syaroslav@ivinco.com 4770Syaroslav@ivinco.com /// get last warning message (string) 4780Syaroslav@ivinco.com function GetLastWarning () 4790Syaroslav@ivinco.com { 4800Syaroslav@ivinco.com return $this->_warning; 4810Syaroslav@ivinco.com } 4820Syaroslav@ivinco.com 4830Syaroslav@ivinco.com /// get last error flag (to tell network connection errors from searchd errors or broken responses) 4840Syaroslav@ivinco.com function IsConnectError() 4850Syaroslav@ivinco.com { 4860Syaroslav@ivinco.com return $this->_connerror; 4870Syaroslav@ivinco.com } 4880Syaroslav@ivinco.com 4890Syaroslav@ivinco.com /// set searchd host name (string) and port (integer) 4900Syaroslav@ivinco.com function SetServer ( $host, $port = 0 ) 4910Syaroslav@ivinco.com { 4920Syaroslav@ivinco.com assert ( is_string($host) ); 4930Syaroslav@ivinco.com if ( $host[0] == '/') 4940Syaroslav@ivinco.com { 4950Syaroslav@ivinco.com $this->_path = 'unix://' . $host; 4960Syaroslav@ivinco.com return; 4970Syaroslav@ivinco.com } 4980Syaroslav@ivinco.com if ( substr ( $host, 0, 7 )=="unix://" ) 4990Syaroslav@ivinco.com { 5000Syaroslav@ivinco.com $this->_path = $host; 5010Syaroslav@ivinco.com return; 5020Syaroslav@ivinco.com } 5030Syaroslav@ivinco.com 5040Syaroslav@ivinco.com assert ( is_int($port) ); 5050Syaroslav@ivinco.com $this->_host = $host; 5060Syaroslav@ivinco.com $this->_port = $port; 5070Syaroslav@ivinco.com $this->_path = ''; 5080Syaroslav@ivinco.com 5090Syaroslav@ivinco.com } 5100Syaroslav@ivinco.com 5110Syaroslav@ivinco.com /// set server connection timeout (0 to remove) 5120Syaroslav@ivinco.com function SetConnectTimeout ( $timeout ) 5130Syaroslav@ivinco.com { 5140Syaroslav@ivinco.com assert ( is_numeric($timeout) ); 5150Syaroslav@ivinco.com $this->_timeout = $timeout; 5160Syaroslav@ivinco.com } 5170Syaroslav@ivinco.com 5180Syaroslav@ivinco.com 5190Syaroslav@ivinco.com function _Send ( $handle, $data, $length ) 5200Syaroslav@ivinco.com { 5210Syaroslav@ivinco.com if ( feof($handle) || fwrite ( $handle, $data, $length ) !== $length ) 5220Syaroslav@ivinco.com { 5230Syaroslav@ivinco.com $this->_error = 'connection unexpectedly closed (timed out?)'; 5240Syaroslav@ivinco.com $this->_connerror = true; 5250Syaroslav@ivinco.com return false; 5260Syaroslav@ivinco.com } 5270Syaroslav@ivinco.com return true; 5280Syaroslav@ivinco.com } 5290Syaroslav@ivinco.com 5300Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 5310Syaroslav@ivinco.com 5320Syaroslav@ivinco.com /// enter mbstring workaround mode 5330Syaroslav@ivinco.com function _MBPush () 5340Syaroslav@ivinco.com { 5350Syaroslav@ivinco.com $this->_mbenc = ""; 5360Syaroslav@ivinco.com if ( ini_get ( "mbstring.func_overload" ) & 2 ) 5370Syaroslav@ivinco.com { 5380Syaroslav@ivinco.com $this->_mbenc = mb_internal_encoding(); 5390Syaroslav@ivinco.com mb_internal_encoding ( "latin1" ); 5400Syaroslav@ivinco.com } 5410Syaroslav@ivinco.com } 5420Syaroslav@ivinco.com 5430Syaroslav@ivinco.com /// leave mbstring workaround mode 5440Syaroslav@ivinco.com function _MBPop () 5450Syaroslav@ivinco.com { 5460Syaroslav@ivinco.com if ( $this->_mbenc ) 5470Syaroslav@ivinco.com mb_internal_encoding ( $this->_mbenc ); 5480Syaroslav@ivinco.com } 5490Syaroslav@ivinco.com 5500Syaroslav@ivinco.com /// connect to searchd server 5510Syaroslav@ivinco.com function _Connect () 5520Syaroslav@ivinco.com { 5530Syaroslav@ivinco.com if ( $this->_socket!==false ) 5540Syaroslav@ivinco.com { 5550Syaroslav@ivinco.com // we are in persistent connection mode, so we have a socket 5560Syaroslav@ivinco.com // however, need to check whether it's still alive 5570Syaroslav@ivinco.com if ( !@feof ( $this->_socket ) ) 5580Syaroslav@ivinco.com return $this->_socket; 5590Syaroslav@ivinco.com 5600Syaroslav@ivinco.com // force reopen 5610Syaroslav@ivinco.com $this->_socket = false; 5620Syaroslav@ivinco.com } 5630Syaroslav@ivinco.com 5640Syaroslav@ivinco.com $errno = 0; 5650Syaroslav@ivinco.com $errstr = ""; 5660Syaroslav@ivinco.com $this->_connerror = false; 5670Syaroslav@ivinco.com 5680Syaroslav@ivinco.com if ( $this->_path ) 5690Syaroslav@ivinco.com { 5700Syaroslav@ivinco.com $host = $this->_path; 5710Syaroslav@ivinco.com $port = 0; 5720Syaroslav@ivinco.com } 5730Syaroslav@ivinco.com else 5740Syaroslav@ivinco.com { 5750Syaroslav@ivinco.com $host = $this->_host; 5760Syaroslav@ivinco.com $port = $this->_port; 5770Syaroslav@ivinco.com } 5780Syaroslav@ivinco.com 5790Syaroslav@ivinco.com if ( $this->_timeout<=0 ) 5800Syaroslav@ivinco.com $fp = @fsockopen ( $host, $port, $errno, $errstr ); 5810Syaroslav@ivinco.com else 5820Syaroslav@ivinco.com $fp = @fsockopen ( $host, $port, $errno, $errstr, $this->_timeout ); 5830Syaroslav@ivinco.com 5840Syaroslav@ivinco.com if ( !$fp ) 5850Syaroslav@ivinco.com { 5860Syaroslav@ivinco.com if ( $this->_path ) 5870Syaroslav@ivinco.com $location = $this->_path; 5880Syaroslav@ivinco.com else 5890Syaroslav@ivinco.com $location = "{$this->_host}:{$this->_port}"; 5900Syaroslav@ivinco.com 5910Syaroslav@ivinco.com $errstr = trim ( $errstr ); 5920Syaroslav@ivinco.com $this->_error = "connection to $location failed (errno=$errno, msg=$errstr)"; 5930Syaroslav@ivinco.com $this->_connerror = true; 5940Syaroslav@ivinco.com return false; 5950Syaroslav@ivinco.com } 5960Syaroslav@ivinco.com 5970Syaroslav@ivinco.com // send my version 5980Syaroslav@ivinco.com // this is a subtle part. we must do it before (!) reading back from searchd. 5990Syaroslav@ivinco.com // because otherwise under some conditions (reported on FreeBSD for instance) 6000Syaroslav@ivinco.com // TCP stack could throttle write-write-read pattern because of Nagle. 6010Syaroslav@ivinco.com if ( !$this->_Send ( $fp, pack ( "N", 1 ), 4 ) ) 6020Syaroslav@ivinco.com { 6030Syaroslav@ivinco.com fclose ( $fp ); 6040Syaroslav@ivinco.com $this->_error = "failed to send client protocol version"; 6050Syaroslav@ivinco.com return false; 6060Syaroslav@ivinco.com } 6070Syaroslav@ivinco.com 6080Syaroslav@ivinco.com // check version 6090Syaroslav@ivinco.com list(,$v) = unpack ( "N*", fread ( $fp, 4 ) ); 6100Syaroslav@ivinco.com $v = (int)$v; 6110Syaroslav@ivinco.com if ( $v<1 ) 6120Syaroslav@ivinco.com { 6130Syaroslav@ivinco.com fclose ( $fp ); 6140Syaroslav@ivinco.com $this->_error = "expected searchd protocol version 1+, got version '$v'"; 6150Syaroslav@ivinco.com return false; 6160Syaroslav@ivinco.com } 6170Syaroslav@ivinco.com 6180Syaroslav@ivinco.com return $fp; 6190Syaroslav@ivinco.com } 6200Syaroslav@ivinco.com 6210Syaroslav@ivinco.com /// get and check response packet from searchd server 6220Syaroslav@ivinco.com function _GetResponse ( $fp, $client_ver ) 6230Syaroslav@ivinco.com { 6240Syaroslav@ivinco.com $response = ""; 6250Syaroslav@ivinco.com $len = 0; 6260Syaroslav@ivinco.com 6270Syaroslav@ivinco.com $header = fread ( $fp, 8 ); 6280Syaroslav@ivinco.com if ( strlen($header)==8 ) 6290Syaroslav@ivinco.com { 6300Syaroslav@ivinco.com list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) ); 6310Syaroslav@ivinco.com $left = $len; 6320Syaroslav@ivinco.com while ( $left>0 && !feof($fp) ) 6330Syaroslav@ivinco.com { 6340Syaroslav@ivinco.com $chunk = fread ( $fp, $left ); 6350Syaroslav@ivinco.com if ( $chunk ) 6360Syaroslav@ivinco.com { 6370Syaroslav@ivinco.com $response .= $chunk; 6380Syaroslav@ivinco.com $left -= strlen($chunk); 6390Syaroslav@ivinco.com } 6400Syaroslav@ivinco.com } 6410Syaroslav@ivinco.com } 6420Syaroslav@ivinco.com if ( $this->_socket === false ) 6430Syaroslav@ivinco.com fclose ( $fp ); 6440Syaroslav@ivinco.com 6450Syaroslav@ivinco.com // check response 6460Syaroslav@ivinco.com $read = strlen ( $response ); 6470Syaroslav@ivinco.com if ( !$response || $read!=$len ) 6480Syaroslav@ivinco.com { 6490Syaroslav@ivinco.com $this->_error = $len 6500Syaroslav@ivinco.com ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)" 6510Syaroslav@ivinco.com : "received zero-sized searchd response"; 6520Syaroslav@ivinco.com return false; 6530Syaroslav@ivinco.com } 6540Syaroslav@ivinco.com 6550Syaroslav@ivinco.com // check status 6560Syaroslav@ivinco.com if ( $status==SEARCHD_WARNING ) 6570Syaroslav@ivinco.com { 6580Syaroslav@ivinco.com list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) ); 6590Syaroslav@ivinco.com $this->_warning = substr ( $response, 4, $wlen ); 6600Syaroslav@ivinco.com return substr ( $response, 4+$wlen ); 6610Syaroslav@ivinco.com } 6620Syaroslav@ivinco.com if ( $status==SEARCHD_ERROR ) 6630Syaroslav@ivinco.com { 6640Syaroslav@ivinco.com $this->_error = "searchd error: " . substr ( $response, 4 ); 6650Syaroslav@ivinco.com return false; 6660Syaroslav@ivinco.com } 6670Syaroslav@ivinco.com if ( $status==SEARCHD_RETRY ) 6680Syaroslav@ivinco.com { 6690Syaroslav@ivinco.com $this->_error = "temporary searchd error: " . substr ( $response, 4 ); 6700Syaroslav@ivinco.com return false; 6710Syaroslav@ivinco.com } 6720Syaroslav@ivinco.com if ( $status!=SEARCHD_OK ) 6730Syaroslav@ivinco.com { 6740Syaroslav@ivinco.com $this->_error = "unknown status code '$status'"; 6750Syaroslav@ivinco.com return false; 6760Syaroslav@ivinco.com } 6770Syaroslav@ivinco.com 6780Syaroslav@ivinco.com // check version 6790Syaroslav@ivinco.com if ( $ver<$client_ver ) 6800Syaroslav@ivinco.com { 6810Syaroslav@ivinco.com $this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work", 6820Syaroslav@ivinco.com $ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff ); 6830Syaroslav@ivinco.com } 6840Syaroslav@ivinco.com 6850Syaroslav@ivinco.com return $response; 6860Syaroslav@ivinco.com } 6870Syaroslav@ivinco.com 6880Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 6890Syaroslav@ivinco.com // searching 6900Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 6910Syaroslav@ivinco.com 6920Syaroslav@ivinco.com /// set offset and count into result set, 6930Syaroslav@ivinco.com /// and optionally set max-matches and cutoff limits 6940Syaroslav@ivinco.com function SetLimits ( $offset, $limit, $max=0, $cutoff=0 ) 6950Syaroslav@ivinco.com { 6960Syaroslav@ivinco.com assert ( is_int($offset) ); 6970Syaroslav@ivinco.com assert ( is_int($limit) ); 6980Syaroslav@ivinco.com assert ( $offset>=0 ); 6990Syaroslav@ivinco.com assert ( $limit>0 ); 7000Syaroslav@ivinco.com assert ( $max>=0 ); 7010Syaroslav@ivinco.com $this->_offset = $offset; 7020Syaroslav@ivinco.com $this->_limit = $limit; 7030Syaroslav@ivinco.com if ( $max>0 ) 7040Syaroslav@ivinco.com $this->_maxmatches = $max; 7050Syaroslav@ivinco.com if ( $cutoff>0 ) 7060Syaroslav@ivinco.com $this->_cutoff = $cutoff; 7070Syaroslav@ivinco.com } 7080Syaroslav@ivinco.com 7090Syaroslav@ivinco.com /// set maximum query time, in milliseconds, per-index 7100Syaroslav@ivinco.com /// integer, 0 means "do not limit" 7110Syaroslav@ivinco.com function SetMaxQueryTime ( $max ) 7120Syaroslav@ivinco.com { 7130Syaroslav@ivinco.com assert ( is_int($max) ); 7140Syaroslav@ivinco.com assert ( $max>=0 ); 7150Syaroslav@ivinco.com $this->_maxquerytime = $max; 7160Syaroslav@ivinco.com } 7170Syaroslav@ivinco.com 7180Syaroslav@ivinco.com /// set matching mode 7190Syaroslav@ivinco.com function SetMatchMode ( $mode ) 7200Syaroslav@ivinco.com { 7210Syaroslav@ivinco.com assert ( $mode==SPH_MATCH_ALL 7220Syaroslav@ivinco.com || $mode==SPH_MATCH_ANY 7230Syaroslav@ivinco.com || $mode==SPH_MATCH_PHRASE 7240Syaroslav@ivinco.com || $mode==SPH_MATCH_BOOLEAN 7250Syaroslav@ivinco.com || $mode==SPH_MATCH_EXTENDED 7260Syaroslav@ivinco.com || $mode==SPH_MATCH_FULLSCAN 7270Syaroslav@ivinco.com || $mode==SPH_MATCH_EXTENDED2 ); 7280Syaroslav@ivinco.com $this->_mode = $mode; 7290Syaroslav@ivinco.com } 7300Syaroslav@ivinco.com 7310Syaroslav@ivinco.com /// set ranking mode 7320Syaroslav@ivinco.com function SetRankingMode ( $ranker ) 7330Syaroslav@ivinco.com { 7340Syaroslav@ivinco.com assert ( $ranker==SPH_RANK_PROXIMITY_BM25 7350Syaroslav@ivinco.com || $ranker==SPH_RANK_BM25 7360Syaroslav@ivinco.com || $ranker==SPH_RANK_NONE 7370Syaroslav@ivinco.com || $ranker==SPH_RANK_WORDCOUNT 7380Syaroslav@ivinco.com || $ranker==SPH_RANK_PROXIMITY ); 7390Syaroslav@ivinco.com $this->_ranker = $ranker; 7400Syaroslav@ivinco.com } 7410Syaroslav@ivinco.com 7420Syaroslav@ivinco.com /// set matches sorting mode 7430Syaroslav@ivinco.com function SetSortMode ( $mode, $sortby="" ) 7440Syaroslav@ivinco.com { 7450Syaroslav@ivinco.com assert ( 7460Syaroslav@ivinco.com $mode==SPH_SORT_RELEVANCE || 7470Syaroslav@ivinco.com $mode==SPH_SORT_ATTR_DESC || 7480Syaroslav@ivinco.com $mode==SPH_SORT_ATTR_ASC || 7490Syaroslav@ivinco.com $mode==SPH_SORT_TIME_SEGMENTS || 7500Syaroslav@ivinco.com $mode==SPH_SORT_EXTENDED || 7510Syaroslav@ivinco.com $mode==SPH_SORT_EXPR ); 7520Syaroslav@ivinco.com assert ( is_string($sortby) ); 7530Syaroslav@ivinco.com assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 ); 7540Syaroslav@ivinco.com 7550Syaroslav@ivinco.com $this->_sort = $mode; 7560Syaroslav@ivinco.com $this->_sortby = $sortby; 7570Syaroslav@ivinco.com } 7580Syaroslav@ivinco.com 7590Syaroslav@ivinco.com /// bind per-field weights by order 7600Syaroslav@ivinco.com /// DEPRECATED; use SetFieldWeights() instead 7610Syaroslav@ivinco.com function SetWeights ( $weights ) 7620Syaroslav@ivinco.com { 7630Syaroslav@ivinco.com assert ( is_array($weights) ); 7640Syaroslav@ivinco.com foreach ( $weights as $weight ) 7650Syaroslav@ivinco.com assert ( is_int($weight) ); 7660Syaroslav@ivinco.com 7670Syaroslav@ivinco.com $this->_weights = $weights; 7680Syaroslav@ivinco.com } 7690Syaroslav@ivinco.com 7700Syaroslav@ivinco.com /// bind per-field weights by name 7710Syaroslav@ivinco.com function SetFieldWeights ( $weights ) 7720Syaroslav@ivinco.com { 7730Syaroslav@ivinco.com assert ( is_array($weights) ); 7740Syaroslav@ivinco.com foreach ( $weights as $name=>$weight ) 7750Syaroslav@ivinco.com { 7760Syaroslav@ivinco.com assert ( is_string($name) ); 7770Syaroslav@ivinco.com assert ( is_int($weight) ); 7780Syaroslav@ivinco.com } 7790Syaroslav@ivinco.com $this->_fieldweights = $weights; 7800Syaroslav@ivinco.com } 7810Syaroslav@ivinco.com 7820Syaroslav@ivinco.com /// bind per-index weights by name 7830Syaroslav@ivinco.com function SetIndexWeights ( $weights ) 7840Syaroslav@ivinco.com { 7850Syaroslav@ivinco.com assert ( is_array($weights) ); 7860Syaroslav@ivinco.com foreach ( $weights as $index=>$weight ) 7870Syaroslav@ivinco.com { 7880Syaroslav@ivinco.com assert ( is_string($index) ); 7890Syaroslav@ivinco.com assert ( is_int($weight) ); 7900Syaroslav@ivinco.com } 7910Syaroslav@ivinco.com $this->_indexweights = $weights; 7920Syaroslav@ivinco.com } 7930Syaroslav@ivinco.com 7940Syaroslav@ivinco.com /// set IDs range to match 7950Syaroslav@ivinco.com /// only match records if document ID is beetwen $min and $max (inclusive) 7960Syaroslav@ivinco.com function SetIDRange ( $min, $max ) 7970Syaroslav@ivinco.com { 7980Syaroslav@ivinco.com assert ( is_numeric($min) ); 7990Syaroslav@ivinco.com assert ( is_numeric($max) ); 8000Syaroslav@ivinco.com assert ( $min<=$max ); 8010Syaroslav@ivinco.com $this->_min_id = $min; 8020Syaroslav@ivinco.com $this->_max_id = $max; 8030Syaroslav@ivinco.com } 8040Syaroslav@ivinco.com 8050Syaroslav@ivinco.com /// set values set filter 8060Syaroslav@ivinco.com /// only match records where $attribute value is in given set 8070Syaroslav@ivinco.com function SetFilter ( $attribute, $values, $exclude=false ) 8080Syaroslav@ivinco.com { 8090Syaroslav@ivinco.com assert ( is_string($attribute) ); 8100Syaroslav@ivinco.com assert ( is_array($values) ); 8110Syaroslav@ivinco.com assert ( count($values) ); 8120Syaroslav@ivinco.com 8130Syaroslav@ivinco.com if ( is_array($values) && count($values) ) 8140Syaroslav@ivinco.com { 8150Syaroslav@ivinco.com foreach ( $values as $value ) 8160Syaroslav@ivinco.com assert ( is_numeric($value) ); 8170Syaroslav@ivinco.com 8180Syaroslav@ivinco.com $this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values ); 8190Syaroslav@ivinco.com } 8200Syaroslav@ivinco.com } 8210Syaroslav@ivinco.com 8220Syaroslav@ivinco.com /// set range filter 8230Syaroslav@ivinco.com /// only match records if $attribute value is beetwen $min and $max (inclusive) 8240Syaroslav@ivinco.com function SetFilterRange ( $attribute, $min, $max, $exclude=false ) 8250Syaroslav@ivinco.com { 8260Syaroslav@ivinco.com assert ( is_string($attribute) ); 8270Syaroslav@ivinco.com assert ( is_numeric($min) ); 8280Syaroslav@ivinco.com assert ( is_numeric($max) ); 8290Syaroslav@ivinco.com assert ( $min<=$max ); 8300Syaroslav@ivinco.com 8310Syaroslav@ivinco.com $this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max ); 8320Syaroslav@ivinco.com } 8330Syaroslav@ivinco.com 8340Syaroslav@ivinco.com /// set float range filter 8350Syaroslav@ivinco.com /// only match records if $attribute value is beetwen $min and $max (inclusive) 8360Syaroslav@ivinco.com function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false ) 8370Syaroslav@ivinco.com { 8380Syaroslav@ivinco.com assert ( is_string($attribute) ); 8390Syaroslav@ivinco.com assert ( is_float($min) ); 8400Syaroslav@ivinco.com assert ( is_float($max) ); 8410Syaroslav@ivinco.com assert ( $min<=$max ); 8420Syaroslav@ivinco.com 8430Syaroslav@ivinco.com $this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max ); 8440Syaroslav@ivinco.com } 8450Syaroslav@ivinco.com 8460Syaroslav@ivinco.com /// setup anchor point for geosphere distance calculations 8470Syaroslav@ivinco.com /// required to use @geodist in filters and sorting 8480Syaroslav@ivinco.com /// latitude and longitude must be in radians 8490Syaroslav@ivinco.com function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long ) 8500Syaroslav@ivinco.com { 8510Syaroslav@ivinco.com assert ( is_string($attrlat) ); 8520Syaroslav@ivinco.com assert ( is_string($attrlong) ); 8530Syaroslav@ivinco.com assert ( is_float($lat) ); 8540Syaroslav@ivinco.com assert ( is_float($long) ); 8550Syaroslav@ivinco.com 8560Syaroslav@ivinco.com $this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long ); 8570Syaroslav@ivinco.com } 8580Syaroslav@ivinco.com 8590Syaroslav@ivinco.com /// set grouping attribute and function 8600Syaroslav@ivinco.com function SetGroupBy ( $attribute, $func, $groupsort="@group desc" ) 8610Syaroslav@ivinco.com { 8620Syaroslav@ivinco.com assert ( is_string($attribute) ); 8630Syaroslav@ivinco.com assert ( is_string($groupsort) ); 8640Syaroslav@ivinco.com assert ( $func==SPH_GROUPBY_DAY 8650Syaroslav@ivinco.com || $func==SPH_GROUPBY_WEEK 8660Syaroslav@ivinco.com || $func==SPH_GROUPBY_MONTH 8670Syaroslav@ivinco.com || $func==SPH_GROUPBY_YEAR 8680Syaroslav@ivinco.com || $func==SPH_GROUPBY_ATTR 8690Syaroslav@ivinco.com || $func==SPH_GROUPBY_ATTRPAIR ); 8700Syaroslav@ivinco.com 8710Syaroslav@ivinco.com $this->_groupby = $attribute; 8720Syaroslav@ivinco.com $this->_groupfunc = $func; 8730Syaroslav@ivinco.com $this->_groupsort = $groupsort; 8740Syaroslav@ivinco.com } 8750Syaroslav@ivinco.com 8760Syaroslav@ivinco.com /// set count-distinct attribute for group-by queries 8770Syaroslav@ivinco.com function SetGroupDistinct ( $attribute ) 8780Syaroslav@ivinco.com { 8790Syaroslav@ivinco.com assert ( is_string($attribute) ); 8800Syaroslav@ivinco.com $this->_groupdistinct = $attribute; 8810Syaroslav@ivinco.com } 8820Syaroslav@ivinco.com 8830Syaroslav@ivinco.com /// set distributed retries count and delay 8840Syaroslav@ivinco.com function SetRetries ( $count, $delay=0 ) 8850Syaroslav@ivinco.com { 8860Syaroslav@ivinco.com assert ( is_int($count) && $count>=0 ); 8870Syaroslav@ivinco.com assert ( is_int($delay) && $delay>=0 ); 8880Syaroslav@ivinco.com $this->_retrycount = $count; 8890Syaroslav@ivinco.com $this->_retrydelay = $delay; 8900Syaroslav@ivinco.com } 8910Syaroslav@ivinco.com 8920Syaroslav@ivinco.com /// set result set format (hash or array; hash by default) 8930Syaroslav@ivinco.com /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs 8940Syaroslav@ivinco.com function SetArrayResult ( $arrayresult ) 8950Syaroslav@ivinco.com { 8960Syaroslav@ivinco.com assert ( is_bool($arrayresult) ); 8970Syaroslav@ivinco.com $this->_arrayresult = $arrayresult; 8980Syaroslav@ivinco.com } 8990Syaroslav@ivinco.com 9000Syaroslav@ivinco.com /// set attribute values override 9010Syaroslav@ivinco.com /// there can be only one override per attribute 9020Syaroslav@ivinco.com /// $values must be a hash that maps document IDs to attribute values 9030Syaroslav@ivinco.com function SetOverride ( $attrname, $attrtype, $values ) 9040Syaroslav@ivinco.com { 9050Syaroslav@ivinco.com assert ( is_string ( $attrname ) ); 9060Syaroslav@ivinco.com assert ( in_array ( $attrtype, array ( SPH_ATTR_INTEGER, SPH_ATTR_TIMESTAMP, SPH_ATTR_BOOL, SPH_ATTR_FLOAT, SPH_ATTR_BIGINT ) ) ); 9070Syaroslav@ivinco.com assert ( is_array ( $values ) ); 9080Syaroslav@ivinco.com 9090Syaroslav@ivinco.com $this->_overrides[$attrname] = array ( "attr"=>$attrname, "type"=>$attrtype, "values"=>$values ); 9100Syaroslav@ivinco.com } 9110Syaroslav@ivinco.com 9120Syaroslav@ivinco.com /// set select-list (attributes or expressions), SQL-like syntax 9130Syaroslav@ivinco.com function SetSelect ( $select ) 9140Syaroslav@ivinco.com { 9150Syaroslav@ivinco.com assert ( is_string ( $select ) ); 9160Syaroslav@ivinco.com $this->_select = $select; 9170Syaroslav@ivinco.com } 9180Syaroslav@ivinco.com 9190Syaroslav@ivinco.com ////////////////////////////////////////////////////////////////////////////// 9200Syaroslav@ivinco.com 9210Syaroslav@ivinco.com /// clear all filters (for multi-queries) 9220Syaroslav@ivinco.com function ResetFilters () 9230Syaroslav@ivinco.com { 9240Syaroslav@ivinco.com $this->_filters = array(); 9250Syaroslav@ivinco.com $this->_anchor = array(); 9260Syaroslav@ivinco.com } 9270Syaroslav@ivinco.com 9280Syaroslav@ivinco.com /// clear groupby settings (for multi-queries) 9290Syaroslav@ivinco.com function ResetGroupBy () 9300Syaroslav@ivinco.com { 9310Syaroslav@ivinco.com $this->_groupby = ""; 9320Syaroslav@ivinco.com $this->_groupfunc = SPH_GROUPBY_DAY; 9330Syaroslav@ivinco.com $this->_groupsort = "@group desc"; 9340Syaroslav@ivinco.com $this->_groupdistinct= ""; 9350Syaroslav@ivinco.com } 9360Syaroslav@ivinco.com 9370Syaroslav@ivinco.com /// clear all attribute value overrides (for multi-queries) 9380Syaroslav@ivinco.com function ResetOverrides () 9390Syaroslav@ivinco.com { 9400Syaroslav@ivinco.com $this->_overrides = array (); 9410Syaroslav@ivinco.com } 9420Syaroslav@ivinco.com 9430Syaroslav@ivinco.com ////////////////////////////////////////////////////////////////////////////// 9440Syaroslav@ivinco.com 9450Syaroslav@ivinco.com /// connect to searchd server, run given search query through given indexes, 9460Syaroslav@ivinco.com /// and return the search results 9470Syaroslav@ivinco.com function Query ( $query, $index="*", $comment="" ) 9480Syaroslav@ivinco.com { 9490Syaroslav@ivinco.com assert ( empty($this->_reqs) ); 9500Syaroslav@ivinco.com 9510Syaroslav@ivinco.com $this->AddQuery ( $query, $index, $comment ); 9520Syaroslav@ivinco.com $results = $this->RunQueries (); 9530Syaroslav@ivinco.com $this->_reqs = array (); // just in case it failed too early 9540Syaroslav@ivinco.com 9550Syaroslav@ivinco.com if ( !is_array($results) ) 9560Syaroslav@ivinco.com return false; // probably network error; error message should be already filled 9570Syaroslav@ivinco.com 9580Syaroslav@ivinco.com $this->_error = $results[0]["error"]; 9590Syaroslav@ivinco.com $this->_warning = $results[0]["warning"]; 9600Syaroslav@ivinco.com if ( $results[0]["status"]==SEARCHD_ERROR ) 9610Syaroslav@ivinco.com return false; 9620Syaroslav@ivinco.com else 9630Syaroslav@ivinco.com return $results[0]; 9640Syaroslav@ivinco.com } 9650Syaroslav@ivinco.com 9660Syaroslav@ivinco.com /// helper to pack floats in network byte order 9670Syaroslav@ivinco.com function _PackFloat ( $f ) 9680Syaroslav@ivinco.com { 9690Syaroslav@ivinco.com $t1 = pack ( "f", $f ); // machine order 9700Syaroslav@ivinco.com list(,$t2) = unpack ( "L*", $t1 ); // int in machine order 9710Syaroslav@ivinco.com return pack ( "N", $t2 ); 9720Syaroslav@ivinco.com } 9730Syaroslav@ivinco.com 9740Syaroslav@ivinco.com /// add query to multi-query batch 9750Syaroslav@ivinco.com /// returns index into results array from RunQueries() call 9760Syaroslav@ivinco.com function AddQuery ( $query, $index="*", $comment="" ) 9770Syaroslav@ivinco.com { 9780Syaroslav@ivinco.com // mbstring workaround 9790Syaroslav@ivinco.com $this->_MBPush (); 9800Syaroslav@ivinco.com 9810Syaroslav@ivinco.com // build request 9820Syaroslav@ivinco.com $req = pack ( "NNNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker, $this->_sort ); // mode and limits 9830Syaroslav@ivinco.com $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby; 9840Syaroslav@ivinco.com $req .= pack ( "N", strlen($query) ) . $query; // query itself 9850Syaroslav@ivinco.com $req .= pack ( "N", count($this->_weights) ); // weights 9860Syaroslav@ivinco.com foreach ( $this->_weights as $weight ) 9870Syaroslav@ivinco.com $req .= pack ( "N", (int)$weight ); 9880Syaroslav@ivinco.com $req .= pack ( "N", strlen($index) ) . $index; // indexes 9890Syaroslav@ivinco.com $req .= pack ( "N", 1 ); // id64 range marker 9900Syaroslav@ivinco.com $req .= sphPackU64 ( $this->_min_id ) . sphPackU64 ( $this->_max_id ); // id64 range 9910Syaroslav@ivinco.com 9920Syaroslav@ivinco.com // filters 9930Syaroslav@ivinco.com $req .= pack ( "N", count($this->_filters) ); 9940Syaroslav@ivinco.com foreach ( $this->_filters as $filter ) 9950Syaroslav@ivinco.com { 9960Syaroslav@ivinco.com $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"]; 9970Syaroslav@ivinco.com $req .= pack ( "N", $filter["type"] ); 9980Syaroslav@ivinco.com switch ( $filter["type"] ) 9990Syaroslav@ivinco.com { 10000Syaroslav@ivinco.com case SPH_FILTER_VALUES: 10010Syaroslav@ivinco.com $req .= pack ( "N", count($filter["values"]) ); 10020Syaroslav@ivinco.com foreach ( $filter["values"] as $value ) 10030Syaroslav@ivinco.com $req .= sphPackI64 ( $value ); 10040Syaroslav@ivinco.com break; 10050Syaroslav@ivinco.com 10060Syaroslav@ivinco.com case SPH_FILTER_RANGE: 10070Syaroslav@ivinco.com $req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] ); 10080Syaroslav@ivinco.com break; 10090Syaroslav@ivinco.com 10100Syaroslav@ivinco.com case SPH_FILTER_FLOATRANGE: 10110Syaroslav@ivinco.com $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] ); 10120Syaroslav@ivinco.com break; 10130Syaroslav@ivinco.com 10140Syaroslav@ivinco.com default: 10150Syaroslav@ivinco.com assert ( 0 && "internal error: unhandled filter type" ); 10160Syaroslav@ivinco.com } 10170Syaroslav@ivinco.com $req .= pack ( "N", $filter["exclude"] ); 10180Syaroslav@ivinco.com } 10190Syaroslav@ivinco.com 10200Syaroslav@ivinco.com // group-by clause, max-matches count, group-sort clause, cutoff count 10210Syaroslav@ivinco.com $req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby; 10220Syaroslav@ivinco.com $req .= pack ( "N", $this->_maxmatches ); 10230Syaroslav@ivinco.com $req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort; 10240Syaroslav@ivinco.com $req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay ); 10250Syaroslav@ivinco.com $req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct; 10260Syaroslav@ivinco.com 10270Syaroslav@ivinco.com // anchor point 10280Syaroslav@ivinco.com if ( empty($this->_anchor) ) 10290Syaroslav@ivinco.com { 10300Syaroslav@ivinco.com $req .= pack ( "N", 0 ); 10310Syaroslav@ivinco.com } else 10320Syaroslav@ivinco.com { 10330Syaroslav@ivinco.com $a =& $this->_anchor; 10340Syaroslav@ivinco.com $req .= pack ( "N", 1 ); 10350Syaroslav@ivinco.com $req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"]; 10360Syaroslav@ivinco.com $req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"]; 10370Syaroslav@ivinco.com $req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] ); 10380Syaroslav@ivinco.com } 10390Syaroslav@ivinco.com 10400Syaroslav@ivinco.com // per-index weights 10410Syaroslav@ivinco.com $req .= pack ( "N", count($this->_indexweights) ); 10420Syaroslav@ivinco.com foreach ( $this->_indexweights as $idx=>$weight ) 10430Syaroslav@ivinco.com $req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight ); 10440Syaroslav@ivinco.com 10450Syaroslav@ivinco.com // max query time 10460Syaroslav@ivinco.com $req .= pack ( "N", $this->_maxquerytime ); 10470Syaroslav@ivinco.com 10480Syaroslav@ivinco.com // per-field weights 10490Syaroslav@ivinco.com $req .= pack ( "N", count($this->_fieldweights) ); 10500Syaroslav@ivinco.com foreach ( $this->_fieldweights as $field=>$weight ) 10510Syaroslav@ivinco.com $req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight ); 10520Syaroslav@ivinco.com 10530Syaroslav@ivinco.com // comment 10540Syaroslav@ivinco.com $req .= pack ( "N", strlen($comment) ) . $comment; 10550Syaroslav@ivinco.com 10560Syaroslav@ivinco.com // attribute overrides 10570Syaroslav@ivinco.com $req .= pack ( "N", count($this->_overrides) ); 10580Syaroslav@ivinco.com foreach ( $this->_overrides as $key => $entry ) 10590Syaroslav@ivinco.com { 10600Syaroslav@ivinco.com $req .= pack ( "N", strlen($entry["attr"]) ) . $entry["attr"]; 10610Syaroslav@ivinco.com $req .= pack ( "NN", $entry["type"], count($entry["values"]) ); 10620Syaroslav@ivinco.com foreach ( $entry["values"] as $id=>$val ) 10630Syaroslav@ivinco.com { 10640Syaroslav@ivinco.com assert ( is_numeric($id) ); 10650Syaroslav@ivinco.com assert ( is_numeric($val) ); 10660Syaroslav@ivinco.com 10670Syaroslav@ivinco.com $req .= sphPackU64 ( $id ); 10680Syaroslav@ivinco.com switch ( $entry["type"] ) 10690Syaroslav@ivinco.com { 10700Syaroslav@ivinco.com case SPH_ATTR_FLOAT: $req .= $this->_PackFloat ( $val ); break; 10710Syaroslav@ivinco.com case SPH_ATTR_BIGINT: $req .= sphPackI64 ( $val ); break; 10720Syaroslav@ivinco.com default: $req .= pack ( "N", $val ); break; 10730Syaroslav@ivinco.com } 10740Syaroslav@ivinco.com } 10750Syaroslav@ivinco.com } 10760Syaroslav@ivinco.com 10770Syaroslav@ivinco.com // select-list 10780Syaroslav@ivinco.com $req .= pack ( "N", strlen($this->_select) ) . $this->_select; 10790Syaroslav@ivinco.com 10800Syaroslav@ivinco.com // mbstring workaround 10810Syaroslav@ivinco.com $this->_MBPop (); 10820Syaroslav@ivinco.com 10830Syaroslav@ivinco.com // store request to requests array 10840Syaroslav@ivinco.com $this->_reqs[] = $req; 10850Syaroslav@ivinco.com return count($this->_reqs)-1; 10860Syaroslav@ivinco.com } 10870Syaroslav@ivinco.com 10880Syaroslav@ivinco.com /// connect to searchd, run queries batch, and return an array of result sets 10890Syaroslav@ivinco.com function RunQueries () 10900Syaroslav@ivinco.com { 10910Syaroslav@ivinco.com if ( empty($this->_reqs) ) 10920Syaroslav@ivinco.com { 10930Syaroslav@ivinco.com $this->_error = "no queries defined, issue AddQuery() first"; 10940Syaroslav@ivinco.com return false; 10950Syaroslav@ivinco.com } 10960Syaroslav@ivinco.com 10970Syaroslav@ivinco.com // mbstring workaround 10980Syaroslav@ivinco.com $this->_MBPush (); 10990Syaroslav@ivinco.com 11000Syaroslav@ivinco.com if (!( $fp = $this->_Connect() )) 11010Syaroslav@ivinco.com { 11020Syaroslav@ivinco.com $this->_MBPop (); 11030Syaroslav@ivinco.com return false; 11040Syaroslav@ivinco.com } 11050Syaroslav@ivinco.com 11060Syaroslav@ivinco.com // send query, get response 11070Syaroslav@ivinco.com $nreqs = count($this->_reqs); 11080Syaroslav@ivinco.com $req = join ( "", $this->_reqs ); 11090Syaroslav@ivinco.com $len = 4+strlen($req); 11100Syaroslav@ivinco.com $req = pack ( "nnNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, $nreqs ) . $req; // add header 11110Syaroslav@ivinco.com 11120Syaroslav@ivinco.com if ( !( $this->_Send ( $fp, $req, $len+8 ) ) || 11130Syaroslav@ivinco.com !( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) ) 11140Syaroslav@ivinco.com { 11150Syaroslav@ivinco.com $this->_MBPop (); 11160Syaroslav@ivinco.com return false; 11170Syaroslav@ivinco.com } 11180Syaroslav@ivinco.com 11190Syaroslav@ivinco.com // query sent ok; we can reset reqs now 11200Syaroslav@ivinco.com $this->_reqs = array (); 11210Syaroslav@ivinco.com 11220Syaroslav@ivinco.com // parse and return response 11230Syaroslav@ivinco.com return $this->_ParseSearchResponse ( $response, $nreqs ); 11240Syaroslav@ivinco.com } 11250Syaroslav@ivinco.com 11260Syaroslav@ivinco.com /// parse and return search query (or queries) response 11270Syaroslav@ivinco.com function _ParseSearchResponse ( $response, $nreqs ) 11280Syaroslav@ivinco.com { 11290Syaroslav@ivinco.com $p = 0; // current position 11300Syaroslav@ivinco.com $max = strlen($response); // max position for checks, to protect against broken responses 11310Syaroslav@ivinco.com 11320Syaroslav@ivinco.com $results = array (); 11330Syaroslav@ivinco.com for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ ) 11340Syaroslav@ivinco.com { 11350Syaroslav@ivinco.com $results[] = array(); 11360Syaroslav@ivinco.com $result =& $results[$ires]; 11370Syaroslav@ivinco.com 11380Syaroslav@ivinco.com $result["error"] = ""; 11390Syaroslav@ivinco.com $result["warning"] = ""; 11400Syaroslav@ivinco.com 11410Syaroslav@ivinco.com // extract status 11420Syaroslav@ivinco.com list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11430Syaroslav@ivinco.com $result["status"] = $status; 11440Syaroslav@ivinco.com if ( $status!=SEARCHD_OK ) 11450Syaroslav@ivinco.com { 11460Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11470Syaroslav@ivinco.com $message = substr ( $response, $p, $len ); $p += $len; 11480Syaroslav@ivinco.com 11490Syaroslav@ivinco.com if ( $status==SEARCHD_WARNING ) 11500Syaroslav@ivinco.com { 11510Syaroslav@ivinco.com $result["warning"] = $message; 11520Syaroslav@ivinco.com } else 11530Syaroslav@ivinco.com { 11540Syaroslav@ivinco.com $result["error"] = $message; 11550Syaroslav@ivinco.com continue; 11560Syaroslav@ivinco.com } 11570Syaroslav@ivinco.com } 11580Syaroslav@ivinco.com 11590Syaroslav@ivinco.com // read schema 11600Syaroslav@ivinco.com $fields = array (); 11610Syaroslav@ivinco.com $attrs = array (); 11620Syaroslav@ivinco.com 11630Syaroslav@ivinco.com list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11640Syaroslav@ivinco.com while ( $nfields-->0 && $p<$max ) 11650Syaroslav@ivinco.com { 11660Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11670Syaroslav@ivinco.com $fields[] = substr ( $response, $p, $len ); $p += $len; 11680Syaroslav@ivinco.com } 11690Syaroslav@ivinco.com $result["fields"] = $fields; 11700Syaroslav@ivinco.com 11710Syaroslav@ivinco.com list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11720Syaroslav@ivinco.com while ( $nattrs-->0 && $p<$max ) 11730Syaroslav@ivinco.com { 11740Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11750Syaroslav@ivinco.com $attr = substr ( $response, $p, $len ); $p += $len; 11760Syaroslav@ivinco.com list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11770Syaroslav@ivinco.com $attrs[$attr] = $type; 11780Syaroslav@ivinco.com } 11790Syaroslav@ivinco.com $result["attrs"] = $attrs; 11800Syaroslav@ivinco.com 11810Syaroslav@ivinco.com // read match count 11820Syaroslav@ivinco.com list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11830Syaroslav@ivinco.com list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11840Syaroslav@ivinco.com 11850Syaroslav@ivinco.com // read matches 11860Syaroslav@ivinco.com $idx = -1; 11870Syaroslav@ivinco.com while ( $count-->0 && $p<$max ) 11880Syaroslav@ivinco.com { 11890Syaroslav@ivinco.com // index into result array 11900Syaroslav@ivinco.com $idx++; 11910Syaroslav@ivinco.com 11920Syaroslav@ivinco.com // parse document id and weight 11930Syaroslav@ivinco.com if ( $id64 ) 11940Syaroslav@ivinco.com { 11950Syaroslav@ivinco.com $doc = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8; 11960Syaroslav@ivinco.com list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 11970Syaroslav@ivinco.com } 11980Syaroslav@ivinco.com else 11990Syaroslav@ivinco.com { 12000Syaroslav@ivinco.com list ( $doc, $weight ) = array_values ( unpack ( "N*N*", 12010Syaroslav@ivinco.com substr ( $response, $p, 8 ) ) ); 12020Syaroslav@ivinco.com $p += 8; 12030Syaroslav@ivinco.com $doc = sphFixUint($doc); 12040Syaroslav@ivinco.com } 12050Syaroslav@ivinco.com $weight = sprintf ( "%u", $weight ); 12060Syaroslav@ivinco.com 12070Syaroslav@ivinco.com // create match entry 12080Syaroslav@ivinco.com if ( $this->_arrayresult ) 12090Syaroslav@ivinco.com $result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight ); 12100Syaroslav@ivinco.com else 12110Syaroslav@ivinco.com $result["matches"][$doc]["weight"] = $weight; 12120Syaroslav@ivinco.com 12130Syaroslav@ivinco.com // parse and create attributes 12140Syaroslav@ivinco.com $attrvals = array (); 12150Syaroslav@ivinco.com foreach ( $attrs as $attr=>$type ) 12160Syaroslav@ivinco.com { 12170Syaroslav@ivinco.com // handle 64bit ints 12180Syaroslav@ivinco.com if ( $type==SPH_ATTR_BIGINT ) 12190Syaroslav@ivinco.com { 12200Syaroslav@ivinco.com $attrvals[$attr] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8; 12210Syaroslav@ivinco.com continue; 12220Syaroslav@ivinco.com } 12230Syaroslav@ivinco.com 12240Syaroslav@ivinco.com // handle floats 12250Syaroslav@ivinco.com if ( $type==SPH_ATTR_FLOAT ) 12260Syaroslav@ivinco.com { 12270Syaroslav@ivinco.com list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 12280Syaroslav@ivinco.com list(,$fval) = unpack ( "f*", pack ( "L", $uval ) ); 12290Syaroslav@ivinco.com $attrvals[$attr] = $fval; 12300Syaroslav@ivinco.com continue; 12310Syaroslav@ivinco.com } 12320Syaroslav@ivinco.com 12330Syaroslav@ivinco.com // handle everything else as unsigned ints 12340Syaroslav@ivinco.com list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 12350Syaroslav@ivinco.com if ( $type & SPH_ATTR_MULTI ) 12360Syaroslav@ivinco.com { 12370Syaroslav@ivinco.com $attrvals[$attr] = array (); 12380Syaroslav@ivinco.com $nvalues = $val; 12390Syaroslav@ivinco.com while ( $nvalues-->0 && $p<$max ) 12400Syaroslav@ivinco.com { 12410Syaroslav@ivinco.com list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 12420Syaroslav@ivinco.com $attrvals[$attr][] = sphFixUint($val); 12430Syaroslav@ivinco.com } 12440Syaroslav@ivinco.com } else 12450Syaroslav@ivinco.com { 12460Syaroslav@ivinco.com $attrvals[$attr] = sphFixUint($val); 12470Syaroslav@ivinco.com } 12480Syaroslav@ivinco.com } 12490Syaroslav@ivinco.com 12500Syaroslav@ivinco.com if ( $this->_arrayresult ) 12510Syaroslav@ivinco.com $result["matches"][$idx]["attrs"] = $attrvals; 12520Syaroslav@ivinco.com else 12530Syaroslav@ivinco.com $result["matches"][$doc]["attrs"] = $attrvals; 12540Syaroslav@ivinco.com } 12550Syaroslav@ivinco.com 12560Syaroslav@ivinco.com list ( $total, $total_found, $msecs, $words ) = 12570Syaroslav@ivinco.com array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) ); 12580Syaroslav@ivinco.com $result["total"] = sprintf ( "%u", $total ); 12590Syaroslav@ivinco.com $result["total_found"] = sprintf ( "%u", $total_found ); 12600Syaroslav@ivinco.com $result["time"] = sprintf ( "%.3f", $msecs/1000 ); 12610Syaroslav@ivinco.com $p += 16; 12620Syaroslav@ivinco.com 12630Syaroslav@ivinco.com while ( $words-->0 && $p<$max ) 12640Syaroslav@ivinco.com { 12650Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 12660Syaroslav@ivinco.com $word = substr ( $response, $p, $len ); $p += $len; 12670Syaroslav@ivinco.com list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8; 12680Syaroslav@ivinco.com $result["words"][$word] = array ( 12690Syaroslav@ivinco.com "docs"=>sprintf ( "%u", $docs ), 12700Syaroslav@ivinco.com "hits"=>sprintf ( "%u", $hits ) ); 12710Syaroslav@ivinco.com } 12720Syaroslav@ivinco.com } 12730Syaroslav@ivinco.com 12740Syaroslav@ivinco.com $this->_MBPop (); 12750Syaroslav@ivinco.com return $results; 12760Syaroslav@ivinco.com } 12770Syaroslav@ivinco.com 12780Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 12790Syaroslav@ivinco.com // excerpts generation 12800Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 12810Syaroslav@ivinco.com 12820Syaroslav@ivinco.com /// connect to searchd server, and generate exceprts (snippets) 12830Syaroslav@ivinco.com /// of given documents for given query. returns false on failure, 12840Syaroslav@ivinco.com /// an array of snippets on success 12850Syaroslav@ivinco.com function BuildExcerpts ( $docs, $index, $words, $opts=array() ) 12860Syaroslav@ivinco.com { 12870Syaroslav@ivinco.com assert ( is_array($docs) ); 12880Syaroslav@ivinco.com assert ( is_string($index) ); 12890Syaroslav@ivinco.com assert ( is_string($words) ); 12900Syaroslav@ivinco.com assert ( is_array($opts) ); 12910Syaroslav@ivinco.com 12920Syaroslav@ivinco.com $this->_MBPush (); 12930Syaroslav@ivinco.com 12940Syaroslav@ivinco.com if (!( $fp = $this->_Connect() )) 12950Syaroslav@ivinco.com { 12960Syaroslav@ivinco.com $this->_MBPop(); 12970Syaroslav@ivinco.com return false; 12980Syaroslav@ivinco.com } 12990Syaroslav@ivinco.com 13000Syaroslav@ivinco.com ///////////////// 13010Syaroslav@ivinco.com // fixup options 13020Syaroslav@ivinco.com ///////////////// 13030Syaroslav@ivinco.com 13040Syaroslav@ivinco.com if ( !isset($opts["before_match"]) ) $opts["before_match"] = "<b>"; 13050Syaroslav@ivinco.com if ( !isset($opts["after_match"]) ) $opts["after_match"] = "</b>"; 13060Syaroslav@ivinco.com if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... "; 13070Syaroslav@ivinco.com if ( !isset($opts["limit"]) ) $opts["limit"] = 256; 13080Syaroslav@ivinco.com if ( !isset($opts["around"]) ) $opts["around"] = 5; 13090Syaroslav@ivinco.com if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false; 13100Syaroslav@ivinco.com if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false; 13110Syaroslav@ivinco.com if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false; 13120Syaroslav@ivinco.com if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false; 13130Syaroslav@ivinco.com 13140Syaroslav@ivinco.com ///////////////// 13150Syaroslav@ivinco.com // build request 13160Syaroslav@ivinco.com ///////////////// 13170Syaroslav@ivinco.com 13180Syaroslav@ivinco.com // v.1.0 req 13190Syaroslav@ivinco.com $flags = 1; // remove spaces 13200Syaroslav@ivinco.com if ( $opts["exact_phrase"] ) $flags |= 2; 13210Syaroslav@ivinco.com if ( $opts["single_passage"] ) $flags |= 4; 13220Syaroslav@ivinco.com if ( $opts["use_boundaries"] ) $flags |= 8; 13230Syaroslav@ivinco.com if ( $opts["weight_order"] ) $flags |= 16; 13240Syaroslav@ivinco.com $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags 13250Syaroslav@ivinco.com $req .= pack ( "N", strlen($index) ) . $index; // req index 13260Syaroslav@ivinco.com $req .= pack ( "N", strlen($words) ) . $words; // req words 13270Syaroslav@ivinco.com 13280Syaroslav@ivinco.com // options 13290Syaroslav@ivinco.com $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"]; 13300Syaroslav@ivinco.com $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"]; 13310Syaroslav@ivinco.com $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"]; 13320Syaroslav@ivinco.com $req .= pack ( "N", (int)$opts["limit"] ); 13330Syaroslav@ivinco.com $req .= pack ( "N", (int)$opts["around"] ); 13340Syaroslav@ivinco.com 13350Syaroslav@ivinco.com // documents 13360Syaroslav@ivinco.com $req .= pack ( "N", count($docs) ); 13370Syaroslav@ivinco.com foreach ( $docs as $doc ) 13380Syaroslav@ivinco.com { 13390Syaroslav@ivinco.com assert ( is_string($doc) ); 13400Syaroslav@ivinco.com $req .= pack ( "N", strlen($doc) ) . $doc; 13410Syaroslav@ivinco.com } 13420Syaroslav@ivinco.com 13430Syaroslav@ivinco.com //////////////////////////// 13440Syaroslav@ivinco.com // send query, get response 13450Syaroslav@ivinco.com //////////////////////////// 13460Syaroslav@ivinco.com 13470Syaroslav@ivinco.com $len = strlen($req); 13480Syaroslav@ivinco.com $req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header 13490Syaroslav@ivinco.com if ( !( $this->_Send ( $fp, $req, $len+8 ) ) || 13500Syaroslav@ivinco.com !( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ) ) 13510Syaroslav@ivinco.com { 13520Syaroslav@ivinco.com $this->_MBPop (); 13530Syaroslav@ivinco.com return false; 13540Syaroslav@ivinco.com } 13550Syaroslav@ivinco.com 13560Syaroslav@ivinco.com ////////////////// 13570Syaroslav@ivinco.com // parse response 13580Syaroslav@ivinco.com ////////////////// 13590Syaroslav@ivinco.com 13600Syaroslav@ivinco.com $pos = 0; 13610Syaroslav@ivinco.com $res = array (); 13620Syaroslav@ivinco.com $rlen = strlen($response); 13630Syaroslav@ivinco.com for ( $i=0; $i<count($docs); $i++ ) 13640Syaroslav@ivinco.com { 13650Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); 13660Syaroslav@ivinco.com $pos += 4; 13670Syaroslav@ivinco.com 13680Syaroslav@ivinco.com if ( $pos+$len > $rlen ) 13690Syaroslav@ivinco.com { 13700Syaroslav@ivinco.com $this->_error = "incomplete reply"; 13710Syaroslav@ivinco.com $this->_MBPop (); 13720Syaroslav@ivinco.com return false; 13730Syaroslav@ivinco.com } 13740Syaroslav@ivinco.com $res[] = $len ? substr ( $response, $pos, $len ) : ""; 13750Syaroslav@ivinco.com $pos += $len; 13760Syaroslav@ivinco.com } 13770Syaroslav@ivinco.com 13780Syaroslav@ivinco.com $this->_MBPop (); 13790Syaroslav@ivinco.com return $res; 13800Syaroslav@ivinco.com } 13810Syaroslav@ivinco.com 13820Syaroslav@ivinco.com 13830Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 13840Syaroslav@ivinco.com // keyword generation 13850Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 13860Syaroslav@ivinco.com 13870Syaroslav@ivinco.com /// connect to searchd server, and generate keyword list for a given query 13880Syaroslav@ivinco.com /// returns false on failure, 13890Syaroslav@ivinco.com /// an array of words on success 13900Syaroslav@ivinco.com function BuildKeywords ( $query, $index, $hits ) 13910Syaroslav@ivinco.com { 13920Syaroslav@ivinco.com assert ( is_string($query) ); 13930Syaroslav@ivinco.com assert ( is_string($index) ); 13940Syaroslav@ivinco.com assert ( is_bool($hits) ); 13950Syaroslav@ivinco.com 13960Syaroslav@ivinco.com $this->_MBPush (); 13970Syaroslav@ivinco.com 13980Syaroslav@ivinco.com if (!( $fp = $this->_Connect() )) 13990Syaroslav@ivinco.com { 14000Syaroslav@ivinco.com $this->_MBPop(); 14010Syaroslav@ivinco.com return false; 14020Syaroslav@ivinco.com } 14030Syaroslav@ivinco.com 14040Syaroslav@ivinco.com ///////////////// 14050Syaroslav@ivinco.com // build request 14060Syaroslav@ivinco.com ///////////////// 14070Syaroslav@ivinco.com 14080Syaroslav@ivinco.com // v.1.0 req 14090Syaroslav@ivinco.com $req = pack ( "N", strlen($query) ) . $query; // req query 14100Syaroslav@ivinco.com $req .= pack ( "N", strlen($index) ) . $index; // req index 14110Syaroslav@ivinco.com $req .= pack ( "N", (int)$hits ); 14120Syaroslav@ivinco.com 14130Syaroslav@ivinco.com //////////////////////////// 14140Syaroslav@ivinco.com // send query, get response 14150Syaroslav@ivinco.com //////////////////////////// 14160Syaroslav@ivinco.com 14170Syaroslav@ivinco.com $len = strlen($req); 14180Syaroslav@ivinco.com $req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header 14190Syaroslav@ivinco.com if ( !( $this->_Send ( $fp, $req, $len+8 ) ) || 14200Syaroslav@ivinco.com !( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ) ) 14210Syaroslav@ivinco.com { 14220Syaroslav@ivinco.com $this->_MBPop (); 14230Syaroslav@ivinco.com return false; 14240Syaroslav@ivinco.com } 14250Syaroslav@ivinco.com 14260Syaroslav@ivinco.com ////////////////// 14270Syaroslav@ivinco.com // parse response 14280Syaroslav@ivinco.com ////////////////// 14290Syaroslav@ivinco.com 14300Syaroslav@ivinco.com $pos = 0; 14310Syaroslav@ivinco.com $res = array (); 14320Syaroslav@ivinco.com $rlen = strlen($response); 14330Syaroslav@ivinco.com list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) ); 14340Syaroslav@ivinco.com $pos += 4; 14350Syaroslav@ivinco.com for ( $i=0; $i<$nwords; $i++ ) 14360Syaroslav@ivinco.com { 14370Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4; 14380Syaroslav@ivinco.com $tokenized = $len ? substr ( $response, $pos, $len ) : ""; 14390Syaroslav@ivinco.com $pos += $len; 14400Syaroslav@ivinco.com 14410Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4; 14420Syaroslav@ivinco.com $normalized = $len ? substr ( $response, $pos, $len ) : ""; 14430Syaroslav@ivinco.com $pos += $len; 14440Syaroslav@ivinco.com 14450Syaroslav@ivinco.com $res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized ); 14460Syaroslav@ivinco.com 14470Syaroslav@ivinco.com if ( $hits ) 14480Syaroslav@ivinco.com { 14490Syaroslav@ivinco.com list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) ); 14500Syaroslav@ivinco.com $pos += 8; 14510Syaroslav@ivinco.com $res [$i]["docs"] = $ndocs; 14520Syaroslav@ivinco.com $res [$i]["hits"] = $nhits; 14530Syaroslav@ivinco.com } 14540Syaroslav@ivinco.com 14550Syaroslav@ivinco.com if ( $pos > $rlen ) 14560Syaroslav@ivinco.com { 14570Syaroslav@ivinco.com $this->_error = "incomplete reply"; 14580Syaroslav@ivinco.com $this->_MBPop (); 14590Syaroslav@ivinco.com return false; 14600Syaroslav@ivinco.com } 14610Syaroslav@ivinco.com } 14620Syaroslav@ivinco.com 14630Syaroslav@ivinco.com $this->_MBPop (); 14640Syaroslav@ivinco.com return $res; 14650Syaroslav@ivinco.com } 14660Syaroslav@ivinco.com 14670Syaroslav@ivinco.com function EscapeString ( $string ) 14680Syaroslav@ivinco.com { 14690Syaroslav@ivinco.com $from = array ( '\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=' ); 14700Syaroslav@ivinco.com $to = array ( '\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=' ); 14710Syaroslav@ivinco.com 14720Syaroslav@ivinco.com return str_replace ( $from, $to, $string ); 14730Syaroslav@ivinco.com } 14740Syaroslav@ivinco.com 14750Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 14760Syaroslav@ivinco.com // attribute updates 14770Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 14780Syaroslav@ivinco.com 14790Syaroslav@ivinco.com /// batch update given attributes in given rows in given indexes 14800Syaroslav@ivinco.com /// returns amount of updated documents (0 or more) on success, or -1 on failure 14810Syaroslav@ivinco.com function UpdateAttributes ( $index, $attrs, $values, $mva=false ) 14820Syaroslav@ivinco.com { 14830Syaroslav@ivinco.com // verify everything 14840Syaroslav@ivinco.com assert ( is_string($index) ); 14850Syaroslav@ivinco.com assert ( is_bool($mva) ); 14860Syaroslav@ivinco.com 14870Syaroslav@ivinco.com assert ( is_array($attrs) ); 14880Syaroslav@ivinco.com foreach ( $attrs as $attr ) 14890Syaroslav@ivinco.com assert ( is_string($attr) ); 14900Syaroslav@ivinco.com 14910Syaroslav@ivinco.com assert ( is_array($values) ); 14920Syaroslav@ivinco.com foreach ( $values as $id=>$entry ) 14930Syaroslav@ivinco.com { 14940Syaroslav@ivinco.com assert ( is_numeric($id) ); 14950Syaroslav@ivinco.com assert ( is_array($entry) ); 14960Syaroslav@ivinco.com assert ( count($entry)==count($attrs) ); 14970Syaroslav@ivinco.com foreach ( $entry as $v ) 14980Syaroslav@ivinco.com { 14990Syaroslav@ivinco.com if ( $mva ) 15000Syaroslav@ivinco.com { 15010Syaroslav@ivinco.com assert ( is_array($v) ); 15020Syaroslav@ivinco.com foreach ( $v as $vv ) 15030Syaroslav@ivinco.com assert ( is_int($vv) ); 15040Syaroslav@ivinco.com } else 15050Syaroslav@ivinco.com assert ( is_int($v) ); 15060Syaroslav@ivinco.com } 15070Syaroslav@ivinco.com } 15080Syaroslav@ivinco.com 15090Syaroslav@ivinco.com // build request 15100Syaroslav@ivinco.com $req = pack ( "N", strlen($index) ) . $index; 15110Syaroslav@ivinco.com 15120Syaroslav@ivinco.com $req .= pack ( "N", count($attrs) ); 15130Syaroslav@ivinco.com foreach ( $attrs as $attr ) 15140Syaroslav@ivinco.com { 15150Syaroslav@ivinco.com $req .= pack ( "N", strlen($attr) ) . $attr; 15160Syaroslav@ivinco.com $req .= pack ( "N", $mva ? 1 : 0 ); 15170Syaroslav@ivinco.com } 15180Syaroslav@ivinco.com 15190Syaroslav@ivinco.com $req .= pack ( "N", count($values) ); 15200Syaroslav@ivinco.com foreach ( $values as $id=>$entry ) 15210Syaroslav@ivinco.com { 15220Syaroslav@ivinco.com $req .= sphPackU64 ( $id ); 15230Syaroslav@ivinco.com foreach ( $entry as $v ) 15240Syaroslav@ivinco.com { 15250Syaroslav@ivinco.com $req .= pack ( "N", $mva ? count($v) : $v ); 15260Syaroslav@ivinco.com if ( $mva ) 15270Syaroslav@ivinco.com foreach ( $v as $vv ) 15280Syaroslav@ivinco.com $req .= pack ( "N", $vv ); 15290Syaroslav@ivinco.com } 15300Syaroslav@ivinco.com } 15310Syaroslav@ivinco.com 15320Syaroslav@ivinco.com // connect, send query, get response 15330Syaroslav@ivinco.com if (!( $fp = $this->_Connect() )) 15340Syaroslav@ivinco.com return -1; 15350Syaroslav@ivinco.com 15360Syaroslav@ivinco.com $len = strlen($req); 15370Syaroslav@ivinco.com $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header 15380Syaroslav@ivinco.com if ( !$this->_Send ( $fp, $req, $len+8 ) ) 15390Syaroslav@ivinco.com return -1; 15400Syaroslav@ivinco.com 15410Syaroslav@ivinco.com if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) )) 15420Syaroslav@ivinco.com return -1; 15430Syaroslav@ivinco.com 15440Syaroslav@ivinco.com // parse response 15450Syaroslav@ivinco.com list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) ); 15460Syaroslav@ivinco.com return $updated; 15470Syaroslav@ivinco.com } 15480Syaroslav@ivinco.com 15490Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 15500Syaroslav@ivinco.com // persistent connections 15510Syaroslav@ivinco.com ///////////////////////////////////////////////////////////////////////////// 15520Syaroslav@ivinco.com 15530Syaroslav@ivinco.com function Open() 15540Syaroslav@ivinco.com { 15550Syaroslav@ivinco.com if ( $this->_socket !== false ) 15560Syaroslav@ivinco.com { 15570Syaroslav@ivinco.com $this->_error = 'already connected'; 15580Syaroslav@ivinco.com return false; 15590Syaroslav@ivinco.com } 15600Syaroslav@ivinco.com if ( !$fp = $this->_Connect() ) 15610Syaroslav@ivinco.com return false; 15620Syaroslav@ivinco.com 15630Syaroslav@ivinco.com // command, command version = 0, body length = 4, body = 1 15640Syaroslav@ivinco.com $req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 ); 15650Syaroslav@ivinco.com if ( !$this->_Send ( $fp, $req, 12 ) ) 15660Syaroslav@ivinco.com return false; 15670Syaroslav@ivinco.com 15680Syaroslav@ivinco.com $this->_socket = $fp; 15690Syaroslav@ivinco.com return true; 15700Syaroslav@ivinco.com } 15710Syaroslav@ivinco.com 15720Syaroslav@ivinco.com function Close() 15730Syaroslav@ivinco.com { 15740Syaroslav@ivinco.com if ( $this->_socket === false ) 15750Syaroslav@ivinco.com { 15760Syaroslav@ivinco.com $this->_error = 'not connected'; 15770Syaroslav@ivinco.com return false; 15780Syaroslav@ivinco.com } 15790Syaroslav@ivinco.com 15800Syaroslav@ivinco.com fclose ( $this->_socket ); 15810Syaroslav@ivinco.com $this->_socket = false; 15820Syaroslav@ivinco.com 15830Syaroslav@ivinco.com return true; 15840Syaroslav@ivinco.com } 15850Syaroslav@ivinco.com 15860Syaroslav@ivinco.com ////////////////////////////////////////////////////////////////////////// 15870Syaroslav@ivinco.com // status 15880Syaroslav@ivinco.com ////////////////////////////////////////////////////////////////////////// 15890Syaroslav@ivinco.com 15900Syaroslav@ivinco.com function Status () 15910Syaroslav@ivinco.com { 15920Syaroslav@ivinco.com $this->_MBPush (); 15930Syaroslav@ivinco.com if (!( $fp = $this->_Connect() )) 15940Syaroslav@ivinco.com { 15950Syaroslav@ivinco.com $this->_MBPop(); 15960Syaroslav@ivinco.com return false; 15970Syaroslav@ivinco.com } 15980Syaroslav@ivinco.com 15990Syaroslav@ivinco.com $req = pack ( "nnNN", SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1 16000Syaroslav@ivinco.com if ( !( $this->_Send ( $fp, $req, 12 ) ) || 16010Syaroslav@ivinco.com !( $response = $this->_GetResponse ( $fp, VER_COMMAND_STATUS ) ) ) 16020Syaroslav@ivinco.com { 16030Syaroslav@ivinco.com $this->_MBPop (); 16040Syaroslav@ivinco.com return false; 16050Syaroslav@ivinco.com } 16060Syaroslav@ivinco.com 16070Syaroslav@ivinco.com $res = substr ( $response, 4 ); // just ignore length, error handling, etc 16080Syaroslav@ivinco.com $p = 0; 16090Syaroslav@ivinco.com list ( $rows, $cols ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8; 16100Syaroslav@ivinco.com 16110Syaroslav@ivinco.com $res = array(); 16120Syaroslav@ivinco.com for ( $i=0; $i<$rows; $i++ ) 16130Syaroslav@ivinco.com for ( $j=0; $j<$cols; $j++ ) 16140Syaroslav@ivinco.com { 16150Syaroslav@ivinco.com list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 16160Syaroslav@ivinco.com $res[$i][] = substr ( $response, $p, $len ); $p += $len; 16170Syaroslav@ivinco.com } 16180Syaroslav@ivinco.com 16190Syaroslav@ivinco.com $this->_MBPop (); 16200Syaroslav@ivinco.com return $res; 16210Syaroslav@ivinco.com } 16220Syaroslav@ivinco.com} 16230Syaroslav@ivinco.com 1624*133Sandrey} 1625*133Sandrey 16260Syaroslav@ivinco.com// 16270Syaroslav@ivinco.com// $Id: sphinxapi.php 2055 2009-11-06 23:09:58Z shodan $ 16280Syaroslav@ivinco.com// 1629