Development

Changeset 22670

You must first sign up to be able to contribute.

Changeset 22670

Show
Ignore:
Timestamp:
10/01/09 08:26:18 (4 years ago)
Author:
garak
Message:

[sfSphinxPluginsfSphinxPlugin] updated main lib class to Sphinx 0.9.9-rc2

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/sfSphinxPlugin/trunk/lib/sfSphinxClient.class.php

    r17698 r22670  
    2626  const SEARCHD_COMMAND_UPDATE   = 2; 
    2727  const SEARCHD_COMMAND_KEYWORDS = 3; 
     28  const SEARCHD_COMMAND_PERSIST  = 4; 
     29  const SEARCHD_COMMAND_STATUS   = 5; 
     30  const SEARCHD_COMMAND_QUERY    = 6; 
    2831 
    2932  // current client-side command implementation versions 
    30   const VER_COMMAND_SEARCH   = 0x113
     33  const VER_COMMAND_SEARCH   = 0x116
    3134  const VER_COMMAND_EXCERPT  = 0x100; 
    32   const VER_COMMAND_UPDATE   = 0x101
     35  const VER_COMMAND_UPDATE   = 0x102
    3336  const VER_COMMAND_KEYWORDS = 0x100; 
     37  const VER_COMMAND_STATUS   = 0x100; 
     38  const VER_COMMAND_QUERY    = 0x100; 
    3439 
    3540  // known searchd status codes 
     
    5459  const SPH_RANK_WORDCOUNT      = 3; // simple word-count weighting, rank is a weighted sum of 
    5560                                     //  per-field keyword occurence counts 
     61  const SPH_RANK_PROXIMITY      = 4; 
     62  const SPH_RANK_MATCHANY       = 5; 
     63  const SPH_RANK_FIELDMASK      = 6; 
     64 
    5665  // known sort modes 
    5766  const SPH_SORT_RELEVANCE     = 0; 
     
    7382  const SPH_ATTR_BOOL      = 4; 
    7483  const SPH_ATTR_FLOAT     = 5; 
     84  const SPH_ATTR_BIGINT    = 6; 
    7585  const SPH_ATTR_MULTI     = 0x40000000; 
    7686 
     
    110120  protected $maxquerytime;  // max query time, milliseconds (default is 0, do not limit) 
    111121  protected $fieldweights;  // per-field-name weights 
     122  protected $overrides;     // per-query attribute values overrides 
     123  protected $select;        // select-list (attributes or expressions, with optional aliases) 
    112124  protected $mbenc;         // stored mbstring encoding 
    113125  protected $arrayresult;   // whether $result['matches'] should be a hash or an array 
     
    116128  private   $error;         // last error message 
    117129  private   $warning;       // last warning message 
     130  private   $conerror;      // connection error vs remote error flag 
    118131  private   $res;           // result from RunQueries() 
    119132 
     
    127140      'host'          => 'localhost', 
    128141      'port'          => 3312, 
     142      'path'          => false, 
     143      'socket'        => false, 
    129144      'offset'        => 0, 
    130145      'limit'         => 20, 
     
    149164      'maxquerytime'  => 0, 
    150165      'fieldweights'  => array(), 
     166      'overrides'     => array(), 
     167      'select'        => '*', 
    151168      'mbenc'         => '', 
    152169      'arrayresult'   => true, 
    153170      'timeout'       => 0, 
    154     ); 
     171 ); 
    155172    $available_options = array_keys($default_options); 
    156173    $new_options = array_merge($default_options, $options); 
     
    162179      } 
    163180    } 
    164     $this->reqs    = array(); 
    165     $this->error   = ''; 
    166     $this->warning = ''; 
    167     $this->res     = false; 
    168   } 
    169  
    170   /** 
    171    * portably pack numeric to 64 unsigned bits, network order 
    172    * @param  integer $v 
    173    * @return integer 
    174    */ 
    175   private function sphPack64($v) 
    176   { 
    177     // x64 route 
     181    $this->reqs     = array(); 
     182    $this->error    = ''; 
     183    $this->warning  = ''; 
     184    $this->conerror = false; 
     185    $this->res      = false; 
     186  } 
     187 
     188  /** 
     189   * destructor 
     190   */ 
     191  public function __destruct() 
     192  { 
     193    if ($this->socket !== false) 
     194    { 
     195      fclose($this->socket); 
     196    } 
     197  } 
     198 
     199  /** 
     200   * pack 64-bit signed 
     201   * @param  mixed  $v integer/float 
     202   * @return string 
     203   */ 
     204  private function sphPackI64($v) 
     205  { 
     206    // x64 
    178207    if (PHP_INT_SIZE >= 8) 
    179208    { 
    180       $i = (int)$v; 
    181       return pack('NN', $i >> 32, $i & ((1 << 32) - 1)); 
    182     } 
    183  
    184     // x32 route, bcmath 
    185     $x = '4294967296'; 
     209      $v = (int) $v; 
     210      return pack('NN', $v >> 32, $v&0xFFFFFFFF); 
     211    } 
     212 
     213    // x32, int 
     214    if (is_int($v)) 
     215    { 
     216      return pack('NN', $v < 0 ? -1 : 0, $v); 
     217    } 
     218 
     219    // x32, bcmath 
    186220    if (function_exists('bcmul')) 
    187221    { 
    188       $h = bcdiv($v, $x, 0); 
    189       $l = bcmod($v, $x); 
     222      if (bccomp($v, 0) == -1) 
     223      { 
     224        $v = bcadd('18446744073709551616', $v); 
     225      } 
     226      $h = bcdiv($v, '4294967296', 0); 
     227      $l = bcmod($v, '4294967296'); 
    190228      // conversion to float is intentional; int would lose 31st bit 
    191       return pack('NN', (float)$h, (float)$l); 
    192     } 
    193  
    194     // x32 route, 15 or less decimal digits 
    195     // we can use float, because its actually double and has 52 precision bits 
    196     if (strlen($v) <= 15) 
    197     { 
    198       $f = (float)$v; 
    199       $h = (int)($f / $x); 
    200       $l = (int)($f - $x * $h); 
    201       return pack('NN', $h, $l); 
    202     } 
    203   } 
    204  
    205   /** 
    206    * portably unpack 64 unsigned bits, network order to numeric 
    207    * @param  integer $v 
    208    * @return integer 
    209    */ 
    210   private function sphUnpack64($v) 
    211   { 
    212     list($h, $l) = array_values(unpack('N*N*', $v)); 
    213     // x64 route 
     229      return pack('NN', (float) $h, (float) $l); 
     230    } 
     231 
     232    // x32, no-bcmath 
     233    $p = max(0, strlen($v) - 13); 
     234    $lo = abs((float) substr($v, $p)); 
     235    $hi = abs((float) substr($v, 0, $p)); 
     236 
     237    $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912 
     238    $q = floor($m / 4294967296.0); 
     239    $l = $m - ($q * 4294967296.0); 
     240    $h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328 
     241 
     242    if ($v < 0) 
     243    { 
     244      if ($l == 0) 
     245      { 
     246        $h = 4294967296.0 - $h; 
     247      } 
     248      else 
     249      { 
     250        $h = 4294967295.0 - $h; 
     251        $l = 4294967296.0 - $l; 
     252      } 
     253    } 
     254    return pack('NN', $h, $l); 
     255  } 
     256 
     257  /** 
     258   * pack 64-bit unsigned 
     259   * @param  mixed  $v integer/float 
     260   * @return string 
     261   */ 
     262  public function sphPackU64($v) 
     263  { 
     264    // x64 
    214265    if (PHP_INT_SIZE >= 8) 
    215266    { 
    216       if ($h < 0) 
    217       { 
    218         $h += (1 << 32); // because php 5.2.2 to 5.2.5 is totally messed up again 
    219       } 
    220       if ($l < 0) 
    221       { 
    222         $l += (1 << 32); 
    223       } 
    224       return ($h << 32) + $l; 
    225     } 
    226  
    227     // x32 route 
    228     $h = sprintf('%u', $h); 
    229     $l = sprintf('%u', $l); 
    230     $x = '4294967296'; 
    231  
    232     // bcmath 
     267      // x64, int 
     268      if (is_int($v)) 
     269      { 
     270        return pack('NN', $v>>32, $v&0xFFFFFFFF); 
     271      } 
     272 
     273      // x64, bcmath 
     274      if ( function_exists('bcmul')) 
     275      { 
     276        $h = bcdiv($v, 4294967296, 0); 
     277        $l = bcmod($v, 4294967296); 
     278        return pack('NN', $h, $l); 
     279      } 
     280 
     281      // x64, no-bcmath 
     282      $p = max(0, strlen($v) - 13); 
     283      $lo = (int) substr($v, $p); 
     284      $hi = (int) substr($v, 0, $p); 
     285 
     286      $m = $lo + $hi * 1316134912; 
     287      $l = $m % 4294967296; 
     288      $h = $hi * 2328 + (int) ($m / 4294967296); 
     289 
     290      return pack('NN', $h, $l ); 
     291    } 
     292 
     293    // x32, int 
     294    if (is_int($v)) 
     295    { 
     296      return pack('NN', 0, $v); 
     297    } 
     298 
     299    // x32, bcmath 
    233300    if (function_exists('bcmul')) 
    234301    { 
    235       return bcadd($l, bcmul($x, $h)); 
    236     } 
    237  
    238     // no bcmath, 15 or less decimal digits 
    239     // we can use float, because its actually double and has 52 precision bits 
    240     if ($h < 1048576) 
    241     { 
    242       $f = ((float)$h) * $x + (float)$l; 
    243       return sprintf('%.0f', $f); // builtin conversion is only about 39-40 bits precise! 
    244     } 
     302      $h = bcdiv($v, '4294967296', 0); 
     303      $l = bcmod($v, '4294967296'); 
     304      // conversion to float is intentional; int would lose 31st bit 
     305      return pack('NN', (float) $h, (float) $l); 
     306    } 
     307 
     308    // x32, no-bcmath 
     309    $p = max(0, strlen($v) - 13); 
     310    $lo = (float) substr($v, $p); 
     311    $hi = (float) substr($v, 0, $p); 
     312 
     313    $m = $lo + $hi * 1316134912.0; 
     314    $q = floor($m / 4294967296.0); 
     315    $l = $m - ($q * 4294967296.0); 
     316    $h = $hi * 2328.0 + $q; 
     317 
     318    return pack('NN', $h, $l); 
     319  } 
     320 
     321  /** 
     322   * unpack 64-bit unsigned 
     323   * @param  mixed  $v integer/float 
     324   * @return string 
     325   */ 
     326  private function sphUnpackU64($v) 
     327  { 
     328    list($hi, $lo) = array_values(unpack('N*N*', $v)); 
     329 
     330    if (PHP_INT_SIZE >= 8) 
     331    { 
     332      if ( $hi < 0 ) 
     333      { 
     334        $hi += (1 << 32); // because php 5.2.2 to 5.2.5 is totally fucked up again 
     335      } 
     336      if ( $lo < 0 ) 
     337      { 
     338        $lo += (1 << 32); 
     339      } 
     340 
     341      // x64, int 
     342      if ($hi <= 2147483647) 
     343      { 
     344        return ($hi << 32) + $lo; 
     345      } 
     346 
     347      // x64, bcmath 
     348      if (function_exists('bcmul')) 
     349      { 
     350        return bcadd($lo, bcmul($hi, '4294967296')); 
     351      } 
     352 
     353      // x64, no-bcmath 
     354      $C = 100000; 
     355      $h = ((int) ($hi / $C) << 32) + (int)($lo / $C); 
     356      $l = (($hi % $C) << 32) + ($lo % $C); 
     357      if ($l > $C) 
     358      { 
     359        $h += (int) ($l / $C); 
     360        $l  = $l % $C; 
     361      } 
     362 
     363      if ($h == 0) 
     364      { 
     365        return $l; 
     366      } 
     367      return sprintf('%d%05d', $h, $l); 
     368    } 
     369 
     370    // x32, int 
     371    if ($hi == 0) 
     372    { 
     373      if ($lo > 0) 
     374      { 
     375        return $lo; 
     376      } 
     377      return sprintf('%u', $lo ); 
     378    } 
     379 
     380    $hi = sprintf('%u', $hi ); 
     381    $lo = sprintf('%u', $lo ); 
     382 
     383    // x32, bcmath 
     384    if (function_exists('bcmul')) 
     385    { 
     386      return bcadd($lo, bcmul($hi, '4294967296')); 
     387    } 
     388 
     389    // x32, no-bcmath 
     390    $hi = (float) $hi; 
     391    $lo = (float) $lo; 
     392 
     393    $q = floor($hi / 10000000.0); 
     394    $r = $hi - $q * 10000000.0; 
     395    $m = $lo + $r * 4967296.0; 
     396    $mq = floor($m / 10000000.0); 
     397    $l = $m - $mq * 10000000.0; 
     398    $h = $q * 4294967296.0 + $r * 429.0 + $mq; 
     399 
     400    $h = sprintf('%.0f', $h ); 
     401    $l = sprintf('%07.0f', $l ); 
     402    if ($h == '0') 
     403    { 
     404      return sprintf('%.0f', (float) $l); 
     405    } 
     406    return $h . $l; 
     407  } 
     408 
     409  /** 
     410   * unpack 64-bit signed 
     411   */ 
     412  private function sphUnpackI64($v) 
     413  { 
     414    list($hi, $lo) = array_values(unpack('N*N*', $v)); 
     415 
     416    // x64 
     417    if ( PHP_INT_SIZE>=8 ) 
     418    { 
     419      if ( $hi<0 ) 
     420      { 
     421        $hi += (1 << 32); // because php 5.2.2 to 5.2.5 is totally fucked up again 
     422      } 
     423      if ( $lo<0 ) 
     424      { 
     425        $lo += (1 << 32); 
     426      } 
     427 
     428      return ($hi << 32) + $lo; 
     429    } 
     430 
     431    // x32, int 
     432    if ($hi == 0) 
     433    { 
     434      if ($lo > 0) 
     435      { 
     436        return $lo; 
     437      } 
     438      return sprintf('%u', $lo); 
     439    } 
     440    // x32, int 
     441    elseif ($hi == -1) 
     442    { 
     443      if ($lo < 0) 
     444      { 
     445        return $lo; 
     446      } 
     447      return sprintf('%.0f', $lo - 4294967296.0); 
     448    } 
     449 
     450    $neg = ''; 
     451    $c = 0; 
     452    if ($hi < 0) 
     453    { 
     454      $hi = ~$hi; 
     455      $lo = ~$lo; 
     456      $c = 1; 
     457      $neg = '-'; 
     458    } 
     459 
     460    $hi = sprintf('%u', $hi ); 
     461    $lo = sprintf('%u', $lo ); 
     462 
     463    // x32, bcmath 
     464    if ( function_exists('bcmul') ) 
     465    { 
     466      return $neg . bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c); 
     467    } 
     468 
     469    // x32, no-bcmath 
     470    $hi = (float) $hi; 
     471    $lo = (float) $lo; 
     472 
     473    $q = floor($hi / 10000000.0); 
     474    $r = $hi - $q * 10000000.0; 
     475    $m = $lo + $r * 4967296.0; 
     476    $mq = floor($m / 10000000.0); 
     477    $l = $m - $mq * 10000000.0 + $c; 
     478    $h = $q * 4294967296.0 + $r * 429.0 + $mq; 
     479 
     480    $h = sprintf('%.0f', $h ); 
     481    $l = sprintf('%07.0f', $l ); 
     482    if ($h == '0') 
     483    { 
     484      return $neg . sprintf('%.0f', (float) $l); 
     485    } 
     486    return $neg . $h . $l; 
    245487  } 
    246488 
     
    288530  private function EscapeString($string) 
    289531  { 
    290     $from = array('(', ')', '|', '-', '!', '@', '~', '"', '&', '/' ); 
    291     $to   = array('\(', '\)', '\|', '\-', '\!', '\@', '\~', '\"', '\&', '\/'); 
     532    $from = array('\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '='); 
     533    $to   = array('\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\='); 
    292534    return str_replace($from, $to, $string); 
    293535  } 
     
    318560  { 
    319561    return $this->warning; 
     562  } 
     563 
     564  /** 
     565   * get last error flag 
     566   * (to tell network connection errors from searchd errors or broken responses) 
     567   * @return mixed 
     568   */ 
     569  public function isConnectError() 
     570  { 
     571    return $this->conerror; 
    320572  } 
    321573 
     
    328580  { 
    329581    $this->host = $host; 
     582    if ($host[0] == '/') 
     583    { 
     584      $this->path = 'unix://' . $host; 
     585      return; 
     586    } 
     587    if (substr($host, 0, 7) == 'unix://') 
     588    { 
     589      $this->path = $host; 
     590      return; 
     591    } 
    330592    $this->port = $port; 
     593    $this->path = ''; 
     594  } 
     595 
     596  /** 
     597   * set server connection timeout (0 to remove) 
     598   * @param integer $timeout 
     599   */ 
     600  public function SetConnectTimeout($timeout) 
     601  { 
     602    $this->timeout = $timeout; 
     603  } 
     604 
     605  protected function Send($handle, $data, $length) 
     606  { 
     607    if (feof($handle) || fwrite($handle, $data, $length) !== $length) 
     608    { 
     609      $this->error = 'connection unexpectedly closed (timed out?)'; 
     610      $this->connerror = true; 
     611      return false; 
     612    } 
     613    return true; 
    331614  } 
    332615 
     
    338621  protected function Connect() 
    339622  { 
     623    if ($this->socket !== false) 
     624    { 
     625      return $this->socket; 
     626    } 
     627 
    340628    $errno = 0; 
    341629    $errstr = ''; 
     630    $this->connerror = false; 
     631 
     632    if ($this->path) 
     633    { 
     634      $host = $this->path; 
     635      $port = 0; 
     636    } 
     637    else 
     638    { 
     639      $host = $this->host; 
     640      $port = $this->port; 
     641    } 
     642 
    342643    if ($this->timeout <= 0) 
    343644    { 
    344       $fp = @fsockopen($this->host, $this->port, $errno, $errstr); 
     645      $fp = @fsock($host, $port, $errno, $errstr); 
    345646    } 
    346647    else 
    347648    { 
    348       $fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout); 
    349     } 
     649      $fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout); 
     650    } 
     651 
    350652    if (!$fp) 
    351653    { 
     654      if($this->path) 
     655      { 
     656        $location = $this->path; 
     657      } 
     658      else 
     659      { 
     660        $location = "{$this->host}:{$this->port}"; 
     661      } 
     662 
    352663      $errstr = trim($errstr); 
    353       $this->error = "Sphinx connection to {$this->host}:{$this->port} failed (errno=$errno, msg=$errstr)"; 
    354       throw new Exception($this->error); 
     664      $this->error = "connection to $location failed (errno=$errno, msg=$errstr)"; 
     665      $this->connerror = true; 
     666      return false; 
     667    } 
     668 
     669    // send my version 
     670    // this is a subtle part. we must do it before (!) reading back from searchd. 
     671    // because otherwise under some conditions (reported on FreeBSD for instance) 
     672    // TCP stack could throttle write-write-read pattern because of Nagle. 
     673    if (!$this->Send($fp, pack('N', 1), 4)) 
     674    { 
     675      fclose($fp); 
     676      $this->error = 'failed to send client protocol version'; 
     677      return false; 
    355678    } 
    356679 
    357680    // check version 
    358     list(, $v) = unpack("N*", fread($fp, 4)); 
     681    list(, $v) = unpack('N*', fread($fp, 4)); 
    359682    $v = (int) $v; 
    360683    if ($v < 1) 
     
    362685      fclose($fp); 
    363686      $this->error = "expected searchd protocol version 1+, got version '$v'"; 
    364       throw new Exception($this->error); 
    365     } 
    366  
    367     // all ok, send my version 
    368     fwrite($fp, pack("N", 1)); 
     687      return false; 
     688    } 
     689 
    369690    return $fp; 
    370691  } 
     
    392713      } 
    393714    } 
    394     fclose($fp); 
     715    if ($this->socket === false) 
     716    { 
     717      fclose($fp); 
     718    } 
    395719 
    396720    // check response 
     
    449773 
    450774  /** 
     775   * set maximum query time, in milliseconds, per-index 
     776   * integer, 0 means "do not limit" 
     777   * @param integer $max 
     778   */ 
     779  public function SetMaxQueryTime($max) 
     780  { 
     781    $this->maxquerytime = $max; 
     782  } 
     783 
     784  /** 
    451785   * set match mode 
    452786   * @param integer $mode 
     
    455789  { 
    456790    $this->mode = $mode; 
     791  } 
     792 
     793  /** 
     794   * set ranking mode 
     795   * @param integer $ranker 
     796   */ 
     797  public function SetRankingMode($ranker) 
     798  { 
     799    $this->ranker = $ranker; 
    457800  } 
    458801 
     
    470813  /** 
    471814   * set per-field weights 
     815   * @deprecated use SetFieldWeights() instead 
    472816   * @param array $weights 
    473817   */ 
     
    478822      $this->weights = $weights; 
    479823    } 
     824  } 
     825 
     826  /** 
     827   * bind per-field weights by name 
     828   * @param array $weights 
     829   */ 
     830  public function SetFieldWeights(array $weights) 
     831  { 
     832    $this->fieldweights = $weights; 
     833  } 
     834 
     835  /** 
     836   * bind per-index weights by name 
     837   * @param array $weights 
     838   */ 
     839  public function SetIndexWeights(array $weights) 
     840  { 
     841    $this->indexweights = $weights; 
    480842  } 
    481843 
     
    500862   * @param boolean $exclude 
    501863   */ 
    502   public function SetFilter($attribute, $values, $exclude = false) 
     864  public function SetFilter($attribute, array $values, $exclude = false) 
    503865  { 
    504866    if (is_array($values) && !empty($values)) 
     
    530892      'min'    => $min, 
    531893      'max'    => $max 
     894    ); 
     895  } 
     896 
     897  /** 
     898   * set float range filter 
     899   * only match records if $attribute value is beetwen $min and $max (inclusive) 
     900   * @param string  $attribute 
     901   * @param integer $max 
     902   * @param integer $min 
     903   * @param boolean $exclude 
     904   */ 
     905  public function SetFilterFloatRange($attribute, $min, $max, $exclude = false) 
     906  { 
     907    $this->filters[] = array( 
     908      'type'    => self::SPH_FILTER_FLOATRANGE, 
     909      'attr'    => $attribute, 
     910      'exclude' => $exclude, 
     911      'min'     => $min, 
     912      'max'     => $max 
     913    ); 
     914  } 
     915 
     916  /** 
     917   * setup anchor point for geosphere distance calculations 
     918   * required to use @geodist in filters and sorting 
     919   * latitude and longitude must be in radians 
     920   * @param string $attrlat 
     921   * @param string $attrlong 
     922   * @param float  $lat 
     923   * @param float  $long 
     924   */ 
     925  public function SetGeoAnchor($attrlat, $attrlong, $lat, $long) 
     926  { 
     927    $this->anchor = array( 
     928      'attrlat'  => $attrlat, 
     929      'attrlong' => $attrlong, 
     930      'lat'      => $lat, 
     931      'long'     => $long 
    532932    ); 
    533933  } 
     
    553953   * @param string  $attribute 
    554954   * @param integer $func 
    555    */ 
    556   public function SetGroupBy($attribute, $func) 
     955   * @apram string  $groupsort 
     956   */ 
     957  public function SetGroupBy($attribute, $func, $groupsort = '@group desc') 
    557958  { 
    558959    $this->groupby = $attribute; 
    559960    $this->groupfunc = $func; 
     961    $this->groupsort = $groupsort; 
     962  } 
     963 
     964  /** 
     965   * set count-distinct attribute for group-by queries 
     966   * @param string $attribute 
     967   */ 
     968  public function SetGroupDistinct($attribute) 
     969  { 
     970    $this->groupdistinct = $attribute; 
     971  } 
     972 
     973  /** 
     974   * set distributed retries count and delay 
     975   * @param integer $count 
     976   * @param integer $delay 
     977   */ 
     978  public function SetRetries($count, $delay = 0) 
     979  { 
     980    $this->retrycount = $count; 
     981    $this->retrydelay = $delay; 
     982  } 
     983 
     984  /** 
     985   * set result set format (hash or array; hash by default) 
     986   * PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs 
     987   * @param boolean $arrayresult 
     988   */ 
     989  public function SetArrayResult($arrayresult) 
     990  { 
     991    $this->arrayresult = $arrayresult; 
     992  } 
     993 
     994  /** 
     995   * set attribute values override 
     996   * there can be only one override per attribute 
     997   * $values must be a hash that maps document IDs to attribute values 
     998   * @param string  $attrname 
     999   * @param integer $attrtype 
     1000   * @param array   $values 
     1001   */ 
     1002  public function SetOverride($attrname, $attrtype, array $values) 
     1003  { 
     1004    $this->overrides[$attrname] = array( 
     1005      'attr'   => $attrname, 
     1006      'type'   => $attrtype, 
     1007      'values' => $values 
     1008    ); 
     1009  } 
     1010 
     1011  /** 
     1012   * set select-list (attributes or expressions), SQL-like syntax 
     1013   * @param string $select 
     1014   */ 
     1015  public function SetSelect($select) 
     1016  { 
     1017    $this->select = $select; 
     1018  } 
     1019 
     1020  /** 
     1021   * clear all filters (for multi-queries) 
     1022   */ 
     1023  public function ResetFilters() 
     1024  { 
     1025    $this->filters = array(); 
     1026    $this->anchor = array(); 
     1027  } 
     1028 
     1029  /** 
     1030   * clear groupby settings (for multi-queries) 
     1031   */ 
     1032  public function ResetGroupBy() 
     1033  { 
     1034    $this->groupby = ''; 
     1035    $this->groupfunc  = self::SPH_GROUPBY_DAY; 
     1036    $this->groupsort  = '@group desc'; 
     1037    $this->groupdistinct = ''; 
     1038  } 
     1039 
     1040  /** 
     1041   * clear all attribute value overrides (for multi-queries) 
     1042   */ 
     1043  public function ResetOverrides() 
     1044  { 
     1045    $this->overrides = array(); 
    5601046  } 
    5611047 
     
    5841070    $req .= pack('N', strlen($index)) . $index; // indexes 
    5851071    $req .= pack('N', 1); // id64 range marker 
    586     $req .= $this->sphPack64($this->min_id) . $this->sphPack64($this->max_id); // id64 range 
     1072    $req .= $this->sphPackU64($this->min_id) . $this->sphPackU64($this->max_id); // id64 range 
    5871073 
    5881074    // filters 
     
    5981084          foreach ($filter['values'] as $value) 
    5991085          { 
    600             $req .= pack('N', floatval($value)); // this uberhack is to workaround 32bit signed int limit on x32 platforms 
     1086            $req .= sphPackI64($value); 
    6011087          } 
    6021088          break; 
    6031089        case self::SPH_FILTER_RANGE: 
    604           $req .= pack('NN', $filter['min'], $filter['max']); 
     1090          $req .= sphPackI64($filter['min']) . sphPackI64($filter['max']); 
    6051091          break; 
    6061092        case self::SPH_FILTER_FLOATRANGE: 
    607           $req .= $this->packFloat( $filter['min']) . $this->packFloat( $filter['max']); 
     1093          $req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']); 
    6081094      } 
    6091095      $req .= pack('N', $filter['exclude']); 
     
    6431129    // per-field weights 
    6441130    $req .= pack('N', count($this->fieldweights)); 
    645     foreach ( $this->fieldweights as $field=>$weight) 
     1131    foreach ($this->fieldweights as $field=>$weight) 
    6461132    $req .= pack('N', strlen($field)) . $field . pack('N', $weight); 
    6471133 
    6481134    // comment 
    6491135    $req .= pack('N', strlen($comment)) . $comment; 
     1136 
     1137    // attribute overrides 
     1138    $req .= pack('N', count($this->overrides)); 
     1139    foreach ($this->overrides as $key => $entry) 
     1140    { 
     1141      $req .= pack('N', strlen($entry['attr'])) . $entry['attr']; 
     1142      $req .= pack('NN', $entry['type'], count($entry['values'])); 
     1143      foreach ($entry['values'] as $id => $val) 
     1144      { 
     1145        $req .= sphPackU64($id); 
     1146        switch ($entry['type']) 
     1147        { 
     1148          case self::SPH_ATTR_FLOAT: 
     1149            $req .= $this->PackFloat($val); 
     1150            break; 
     1151          case self::SPH_ATTR_BIGINT: 
     1152            $req .= sphPackI64($val); 
     1153            break; 
     1154          default: 
     1155            $req .= pack('N', $val); 
     1156        } 
     1157      } 
     1158    } 
     1159 
     1160    // select-list 
     1161    $req .= pack('N', strlen($this->select)) . $this->select; 
    6501162 
    6511163    // mbstring workaround 
     
    7311243 
    7321244    $nreqs = count($this->reqs); 
    733     $req = join ( '', $this->reqs ); 
     1245    $req = join('', $this->reqs); 
    7341246    $len = 4 + strlen($req); 
    7351247    // add header 
    7361248    $req = pack('nnNN', self::SEARCHD_COMMAND_SEARCH, self::VER_COMMAND_SEARCH, $len, $nreqs) . $req; 
    7371249 
    738     fwrite ($fp, $req, $len + 8); 
    739     if (!($response = $this->GetResponse($fp, self::VER_COMMAND_SEARCH))) 
     1250    if (!($this->Send($fp, $req, $len + 8)) || !($response = $this->GetResponse($fp, VER_COMMAND_SEARCH))) 
    7401251    { 
    7411252      $this->MBPop(); 
     
    7441255 
    7451256    $this->reqs = array(); 
     1257 
     1258    return $this->ParseSearchResponse($response, $nreqs); 
     1259  } 
     1260 
     1261  /** 
     1262   * parse and return search query (or queries) response 
     1263   * @param string  $response 
     1264   * @param integer $nreqs 
     1265   */ 
     1266  protected function ParseSearchResponse($response, $nreqs) 
     1267  { 
    7461268 
    7471269    //// parse response 
     
    8251347        if ($id64) 
    8261348        { 
    827           $doc = $this->sphUnpack64(substr($response, $p, 8)); 
     1349          $doc = $this->sphUnpackU64(substr($response, $p, 8)); 
    8281350          $p += 8; 
    8291351          list(, $weight) = unpack('N*', substr($response, $p, 4)); 
     
    8651387        foreach ($attrs as $attr => $type) 
    8661388        { 
     1389          // handle 64bit ints 
     1390          if ($type == self::SPH_ATTR_BIGINT) 
     1391          { 
     1392            $attrvals[$attr] = sphUnpackI64(substr($response, $p, 8)); 
     1393            $p += 8; 
     1394            continue; 
     1395          } 
     1396 
    8671397          // handle floats 
    8681398          if ($type == self::SPH_ATTR_FLOAT) 
     
    8701400            list(, $uval) = unpack('N*', substr($response, $p, 4)); 
    8711401            $p += 4; 
    872             list(, $fval) = unpack('f*', pack('L', $uval )); 
     1402            list(, $fval) = unpack('f*', pack('L', $uval)); 
    8731403            $attrvals[$attr] = $fval; 
    8741404            continue; 
     
    9051435      } 
    9061436 
    907       list($total, $total_found, $msecs, $words ) = 
    908        array_values(unpack('N*N*N*N*', substr($response, $p, 16))); 
     1437      list($total, $total_found, $msecs, $words) = array_values(unpack('N*N*N*N*', substr($response, $p, 16))); 
    9091438      $result['total'] = sprintf('%u', $total); 
    9101439      $result['total_found'] = sprintf('%u', $total_found); 
     
    10041533    // add header 
    10051534    $req = pack('nnN', self::SEARCHD_COMMAND_EXCERPT, self::VER_COMMAND_EXCERPT, $len) . $req; 
    1006     $wrote = fwrite($fp, $req, $len+8); 
    1007     if (!($response = $this->GetResponse($fp, self::VER_COMMAND_EXCERPT))) 
     1535    if (!($this->Send($fp, $req, $len + 8)) || !($response = $this->GetResponse($fp, self::VER_COMMAND_EXCERPT))) 
    10081536    { 
    10091537      $this->MBPop(); 
     
    10581586    $req  = pack('N', strlen($query)) . $query; // req query 
    10591587    $req .= pack('N', strlen($index)) . $index; // req index 
    1060     $req .= pack('N', (int)$hits ); 
     1588    $req .= pack('N', (int)$hits); 
    10611589 
    10621590    //// send query, get response 
     
    10641592    $len = strlen($req); 
    10651593    $req = pack('nnN', self::SEARCHD_COMMAND_KEYWORDS, self::VER_COMMAND_KEYWORDS, $len) . $req; // add header 
    1066     $wrote = fwrite ($fp, $req, $len + 8); 
    1067     if (!($response = $this->_GetResponse($fp, self::VER_COMMAND_KEYWORDS))) 
     1594    if (!($this->Send($fp, $req, $len + 8)) || !($response = $this->GetResponse($fp, self::VER_COMMAND_KEYWORDS))) 
    10681595    { 
    10691596      $this->MBPop(); 
     
    10781605    list(, $nwords) = unpack('N*', substr($response, $pos, 4)); 
    10791606    $pos += 4; 
    1080     for ( $i=0; $i<$nwords; $i++
     1607    for($i = 0; $i < $nwords; $i ++
    10811608    { 
    10821609      list(, $len) = unpack('N*', substr($response, $pos, 4)); 
    10831610      $pos += 4; 
    1084       $tokenized = $len ? substr ( $response, $pos, $len ) : ''; 
     1611      $tokenized = $len ? substr($response, $pos, $len) : ''; 
    10851612      $pos += $len; 
    10861613 
    10871614      list(, $len) = unpack('N*', substr($response, $pos, 4)); 
    10881615      $pos += 4; 
    1089       $normalized = $len ? substr ( $response, $pos, $len ) : ''; 
     1616      $normalized = $len ? substr($response, $pos, $len) : ''; 
    10901617      $pos += $len; 
    10911618 
     
    11171644   * @param  array   $attrs 
    11181645   * @param  array   $values 
     1646   * @param  boolean $mva 
    11191647   * @return integer         amount of updated documents (0 or more) on success, or -1 on failure 
    11201648   */ 
    1121   public function UpdateAttributes($index, $attrs, $values
     1649  public function UpdateAttributes($index, array $attrs, array $values, $mva = false
    11221650  { 
    11231651    // build request 
     
    11251653 
    11261654    $req .= pack('N', count($attrs)); 
    1127     foreach ( $attrs as $attr
     1655    foreach ($attrs as $attr
    11281656    { 
    11291657      $req .= pack('N', strlen($attr)) . $attr; 
     1658      $req .= pack('N', $mva ? 1 : 0); 
    11301659    } 
    11311660 
     
    11331662    foreach ($values as $id => $entry) 
    11341663    { 
    1135       $req .= $this->sphPack64($id); 
     1664      $req .= $this->sphPackU64($id); 
    11361665      foreach ($entry as $v) 
    11371666      { 
    1138         $req .= pack('N', $v); 
     1667        $req .= pack('N', $mva ? count($v) : $v); 
     1668        if ($mva) 
     1669        { 
     1670          foreach ($v as $vv) 
     1671          { 
     1672            $req .= pack('N', $vv); 
     1673          } 
     1674        } 
    11391675      } 
    11401676    } 
     
    11441680 
    11451681    // connect, send query, get response 
    1146     if (!($fp = $this->_Connect())) 
     1682    if (!($fp = $this->Connect())) 
    11471683    { 
    11481684      $this->MBPop(); 
     
    11521688    $len = strlen($req); 
    11531689    // add header 
    1154     $req = pack('nnN', self::SEARCHD_COMMAND_UPDATE, self::VER_COMMAND_UPDATE, $len ) . $req; 
    1155     fwrite ( $fp, $req, $len+8 ); 
    1156  
    1157     if (!($response = $this->_GetResponse ( $fp, self::VER_COMMAND_UPDATE ))) 
    1158     { 
    1159       $this->MBPop(); 
     1690    $req = pack('nnN', self::SEARCHD_COMMAND_UPDATE, self::VER_COMMAND_UPDATE, $len) . $req; 
     1691    if (!$this->Send($fp, $req, $len + 8)) 
     1692    { 
     1693      return -1; 
     1694    } 
     1695 
     1696    if (!($response = $this->GetResponse($fp, self::VER_COMMAND_UPDATE))) 
     1697    { 
    11601698      return -1; 
    11611699    } 
     
    11631701    // parse response 
    11641702    list(, $updated) = unpack('N*', substr($response, 0, 4)); 
     1703 
     1704    return $updated; 
     1705  } 
     1706 
     1707  /** 
     1708   * open a persistent connection 
     1709   * @return boolean 
     1710   */ 
     1711  public function Open() 
     1712  { 
     1713    if ($this->socket !== false) 
     1714    { 
     1715      $this->error = 'already connected'; 
     1716      return false; 
     1717    } 
     1718    if (!$fp = $this->Connect()) 
     1719    { 
     1720      return false; 
     1721    } 
     1722 
     1723    // command, command version = 0, body length = 4, body = 1 
     1724    $req = pack('nnNN', SEARCHD_COMMAND_PERSIST, 0, 4, 1); 
     1725    if (!$this->Send ($fp, $req, 12)) 
     1726    { 
     1727      return false; 
     1728    } 
     1729 
     1730    $this->socket = $fp; 
     1731    return true; 
     1732  } 
     1733 
     1734  /** 
     1735   * close a persistent connection 
     1736   * @return boolean 
     1737   */ 
     1738  public function Close() 
     1739  { 
     1740    if ($this->socket === false) 
     1741    { 
     1742      $this->error = 'not connected'; 
     1743      return false; 
     1744    } 
     1745 
     1746    fclose($this->socket); 
     1747    $this->socket = false; 
     1748 
     1749    return true; 
     1750  } 
     1751 
     1752  /** 
     1753   * status 
     1754   * @return array 
     1755   */ 
     1756  public function Status() 
     1757  { 
     1758    $this->MBPush(); 
     1759    if (!($fp = $this->Connect())) 
     1760    { 
     1761      $this->MBPop(); 
     1762      return false; 
     1763    } 
     1764 
     1765    $req = pack('nnNN', SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1); // len=4, body=1 
     1766    if (!($this->Send ($fp, $req, 12)) || !($response = $this->GetResponse ($fp, VER_COMMAND_STATUS))) 
     1767    { 
     1768      $this->MBPop(); 
     1769      return false; 
     1770    } 
     1771 
     1772    $res = substr($response, 4); // just ignore length, error handling, etc 
     1773    $p = 0; 
     1774    list($rows, $cols) = array_values (unpack ('N*N*', substr ($response, $p, 8))); $p += 8; 
     1775 
     1776    $res = array(); 
     1777    for ($i = 0; $i < $rows; $i ++) 
     1778    { 
     1779      for ($j = 0; $j < $cols; $j ++) 
     1780      { 
     1781        list(,$len) = unpack('N*', substr($response, $p, 4)); 
     1782        $p += 4; 
     1783        $res[$i][] = substr($response, $p, $len); 
     1784        $p += $len; 
     1785      } 
     1786    } 
     1787 
    11651788    $this->MBPop(); 
    1166     return $updated
     1789    return $res
    11671790  } 
    11681791