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