Completed
Push — master ( 127ba1...126d50 )
by Peter
02:32
created
src/SphinxClient.php 4 patches
Braces   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -1854,8 +1854,9 @@
 block discarded – undo
1854 1854
             $this->error = 'already connected';
1855 1855
             return false;
1856 1856
         }
1857
-        if (!($fp = $this->connect()))
1858
-            return false;
1857
+        if (!($fp = $this->connect())) {
1858
+                    return false;
1859
+        }
1859 1860
 
1860 1861
         // command, command version = 0, body length = 4, body = 1
1861 1862
         $req = pack('nnNN', SEARCHD_COMMAND_PERSIST, 0, 4, 1);
Please login to merge, or discard this patch.
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -936,7 +936,7 @@
 block discarded – undo
936 936
      * @param resource $fp
937 937
      * @param int $client_ver
938 938
      *
939
-     * @return bool|string
939
+     * @return false|string
940 940
      */
941 941
     protected function getResponse($fp, $client_ver)
942 942
     {
Please login to merge, or discard this patch.
Indentation   +2166 added lines, -2166 removed lines patch added patch discarded remove patch
@@ -146,48 +146,48 @@  discard block
 block discarded – undo
146 146
  */
147 147
 function sphPackI64($value)
148 148
 {
149
-    assert(is_numeric($value));
150
-
151
-    // x64
152
-    if (PHP_INT_SIZE >= 8) {
153
-        $value = (int)$value;
154
-        return pack('NN', $value >> 32, $value & 0xFFFFFFFF);
155
-    }
156
-
157
-    // x32, int
158
-    if (is_int($value)) {
159
-        return pack('NN', $value < 0 ? -1 : 0, $value);
160
-    }
161
-
162
-    // x32, bcmath
163
-    if (function_exists('bcmul')) {
164
-        if (bccomp($value, 0) == -1) {
165
-            $value = bcadd('18446744073709551616', $value);
166
-        }
167
-        $h = bcdiv($value, '4294967296', 0);
168
-        $l = bcmod($value, '4294967296');
169
-        return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
170
-    }
171
-
172
-    // x32, no-bcmath
173
-    $p = max(0, strlen($value) - 13);
174
-    $lo = abs((float)substr($value, $p));
175
-    $hi = abs((float)substr($value, 0, $p));
176
-
177
-    $m = $lo + $hi * 1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
178
-    $q = floor($m / 4294967296.0);
179
-    $l = $m - ($q * 4294967296.0);
180
-    $h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
181
-
182
-    if ($value < 0) {
183
-        if ($l == 0) {
184
-            $h = 4294967296.0 - $h;
185
-        } else {
186
-            $h = 4294967295.0 - $h;
187
-            $l = 4294967296.0 - $l;
188
-        }
189
-    }
190
-    return pack('NN', $h, $l);
149
+	assert(is_numeric($value));
150
+
151
+	// x64
152
+	if (PHP_INT_SIZE >= 8) {
153
+		$value = (int)$value;
154
+		return pack('NN', $value >> 32, $value & 0xFFFFFFFF);
155
+	}
156
+
157
+	// x32, int
158
+	if (is_int($value)) {
159
+		return pack('NN', $value < 0 ? -1 : 0, $value);
160
+	}
161
+
162
+	// x32, bcmath
163
+	if (function_exists('bcmul')) {
164
+		if (bccomp($value, 0) == -1) {
165
+			$value = bcadd('18446744073709551616', $value);
166
+		}
167
+		$h = bcdiv($value, '4294967296', 0);
168
+		$l = bcmod($value, '4294967296');
169
+		return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
170
+	}
171
+
172
+	// x32, no-bcmath
173
+	$p = max(0, strlen($value) - 13);
174
+	$lo = abs((float)substr($value, $p));
175
+	$hi = abs((float)substr($value, 0, $p));
176
+
177
+	$m = $lo + $hi * 1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
178
+	$q = floor($m / 4294967296.0);
179
+	$l = $m - ($q * 4294967296.0);
180
+	$h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
181
+
182
+	if ($value < 0) {
183
+		if ($l == 0) {
184
+			$h = 4294967296.0 - $h;
185
+		} else {
186
+			$h = 4294967295.0 - $h;
187
+			$l = 4294967296.0 - $l;
188
+		}
189
+	}
190
+	return pack('NN', $h, $l);
191 191
 }
192 192
 
193 193
 /**
@@ -199,59 +199,59 @@  discard block
 block discarded – undo
199 199
  */
200 200
 function sphPackU64($value)
201 201
 {
202
-    assert(is_numeric($value));
203
-
204
-    // x64
205
-    if (PHP_INT_SIZE >= 8) {
206
-        assert($value >= 0);
207
-
208
-        // x64, int
209
-        if (is_int($value)) {
210
-            return pack('NN', $value >> 32, $value & 0xFFFFFFFF);
211
-        }
212
-
213
-        // x64, bcmath
214
-        if (function_exists('bcmul')) {
215
-            $h = bcdiv($value, 4294967296, 0);
216
-            $l = bcmod($value, 4294967296);
217
-            return pack('NN', $h, $l);
218
-        }
219
-
220
-        // x64, no-bcmath
221
-        $p = max(0, strlen($value) - 13);
222
-        $lo = (int)substr($value, $p);
223
-        $hi = (int)substr($value, 0, $p);
224
-
225
-        $m = $lo + $hi * 1316134912;
226
-        $l = $m % 4294967296;
227
-        $h = $hi * 2328 + (int)($m / 4294967296);
228
-
229
-        return pack('NN', $h, $l);
230
-    }
231
-
232
-    // x32, int
233
-    if (is_int($value)) {
234
-        return pack('NN', 0, $value);
235
-    }
236
-
237
-    // x32, bcmath
238
-    if (function_exists('bcmul')) {
239
-        $h = bcdiv($value, '4294967296', 0);
240
-        $l = bcmod($value, '4294967296');
241
-        return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
242
-    }
243
-
244
-    // x32, no-bcmath
245
-    $p = max(0, strlen($value) - 13);
246
-    $lo = (float)substr($value, $p);
247
-    $hi = (float)substr($value, 0, $p);
248
-
249
-    $m = $lo + $hi * 1316134912.0;
250
-    $q = floor($m / 4294967296.0);
251
-    $l = $m - ($q * 4294967296.0);
252
-    $h = $hi * 2328.0 + $q;
253
-
254
-    return pack('NN', $h, $l);
202
+	assert(is_numeric($value));
203
+
204
+	// x64
205
+	if (PHP_INT_SIZE >= 8) {
206
+		assert($value >= 0);
207
+
208
+		// x64, int
209
+		if (is_int($value)) {
210
+			return pack('NN', $value >> 32, $value & 0xFFFFFFFF);
211
+		}
212
+
213
+		// x64, bcmath
214
+		if (function_exists('bcmul')) {
215
+			$h = bcdiv($value, 4294967296, 0);
216
+			$l = bcmod($value, 4294967296);
217
+			return pack('NN', $h, $l);
218
+		}
219
+
220
+		// x64, no-bcmath
221
+		$p = max(0, strlen($value) - 13);
222
+		$lo = (int)substr($value, $p);
223
+		$hi = (int)substr($value, 0, $p);
224
+
225
+		$m = $lo + $hi * 1316134912;
226
+		$l = $m % 4294967296;
227
+		$h = $hi * 2328 + (int)($m / 4294967296);
228
+
229
+		return pack('NN', $h, $l);
230
+	}
231
+
232
+	// x32, int
233
+	if (is_int($value)) {
234
+		return pack('NN', 0, $value);
235
+	}
236
+
237
+	// x32, bcmath
238
+	if (function_exists('bcmul')) {
239
+		$h = bcdiv($value, '4294967296', 0);
240
+		$l = bcmod($value, '4294967296');
241
+		return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
242
+	}
243
+
244
+	// x32, no-bcmath
245
+	$p = max(0, strlen($value) - 13);
246
+	$lo = (float)substr($value, $p);
247
+	$hi = (float)substr($value, 0, $p);
248
+
249
+	$m = $lo + $hi * 1316134912.0;
250
+	$q = floor($m / 4294967296.0);
251
+	$l = $m - ($q * 4294967296.0);
252
+	$h = $hi * 2328.0 + $q;
253
+
254
+	return pack('NN', $h, $l);
255 255
 }
256 256
 
257 257
 /**
@@ -263,74 +263,74 @@  discard block
 block discarded – undo
263 263
  */
264 264
 function sphUnpackU64($value)
265 265
 {
266
-    list($hi, $lo) = array_values(unpack('N*N*', $value));
267
-
268
-    if (PHP_INT_SIZE >= 8) {
269
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
270
-            $hi += 1 << 32;
271
-        }
272
-        if ($lo < 0) {
273
-            $lo += 1 << 32;
274
-        }
275
-
276
-        // x64, int
277
-        if ($hi <= 2147483647) {
278
-            return ($hi << 32) + $lo;
279
-        }
280
-
281
-        // x64, bcmath
282
-        if (function_exists('bcmul')) {
283
-            return bcadd($lo, bcmul($hi, '4294967296'));
284
-        }
285
-
286
-        // x64, no-bcmath
287
-        $C = 100000;
288
-        $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
289
-        $l = (($hi % $C) << 32) + ($lo % $C);
290
-        if ($l > $C) {
291
-            $h += (int)($l / $C);
292
-            $l  = $l % $C;
293
-        }
294
-
295
-        if ($h == 0) {
296
-            return $l;
297
-        }
298
-        return sprintf('%d%05d', $h, $l);
299
-    }
300
-
301
-    // x32, int
302
-    if ($hi == 0) {
303
-        if ($lo > 0) {
304
-            return $lo;
305
-        }
306
-        return sprintf('%u', $lo);
307
-    }
308
-
309
-    $hi = sprintf('%u', $hi);
310
-    $lo = sprintf('%u', $lo);
311
-
312
-    // x32, bcmath
313
-    if (function_exists('bcmul')) {
314
-        return bcadd($lo, bcmul($hi, '4294967296'));
315
-    }
316
-
317
-    // x32, no-bcmath
318
-    $hi = (float)$hi;
319
-    $lo = (float)$lo;
320
-
321
-    $q = floor($hi / 10000000.0);
322
-    $r = $hi - $q * 10000000.0;
323
-    $m = $lo + $r * 4967296.0;
324
-    $mq = floor($m / 10000000.0);
325
-    $l = $m - $mq * 10000000.0;
326
-    $h = $q * 4294967296.0 + $r * 429.0 + $mq;
327
-
328
-    $h = sprintf('%.0f', $h);
329
-    $l = sprintf('%07.0f', $l);
330
-    if ($h == '0') {
331
-        return sprintf('%.0f', (float)$l);
332
-    }
333
-    return $h . $l;
266
+	list($hi, $lo) = array_values(unpack('N*N*', $value));
267
+
268
+	if (PHP_INT_SIZE >= 8) {
269
+		if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
270
+			$hi += 1 << 32;
271
+		}
272
+		if ($lo < 0) {
273
+			$lo += 1 << 32;
274
+		}
275
+
276
+		// x64, int
277
+		if ($hi <= 2147483647) {
278
+			return ($hi << 32) + $lo;
279
+		}
280
+
281
+		// x64, bcmath
282
+		if (function_exists('bcmul')) {
283
+			return bcadd($lo, bcmul($hi, '4294967296'));
284
+		}
285
+
286
+		// x64, no-bcmath
287
+		$C = 100000;
288
+		$h = ((int)($hi / $C) << 32) + (int)($lo / $C);
289
+		$l = (($hi % $C) << 32) + ($lo % $C);
290
+		if ($l > $C) {
291
+			$h += (int)($l / $C);
292
+			$l  = $l % $C;
293
+		}
294
+
295
+		if ($h == 0) {
296
+			return $l;
297
+		}
298
+		return sprintf('%d%05d', $h, $l);
299
+	}
300
+
301
+	// x32, int
302
+	if ($hi == 0) {
303
+		if ($lo > 0) {
304
+			return $lo;
305
+		}
306
+		return sprintf('%u', $lo);
307
+	}
308
+
309
+	$hi = sprintf('%u', $hi);
310
+	$lo = sprintf('%u', $lo);
311
+
312
+	// x32, bcmath
313
+	if (function_exists('bcmul')) {
314
+		return bcadd($lo, bcmul($hi, '4294967296'));
315
+	}
316
+
317
+	// x32, no-bcmath
318
+	$hi = (float)$hi;
319
+	$lo = (float)$lo;
320
+
321
+	$q = floor($hi / 10000000.0);
322
+	$r = $hi - $q * 10000000.0;
323
+	$m = $lo + $r * 4967296.0;
324
+	$mq = floor($m / 10000000.0);
325
+	$l = $m - $mq * 10000000.0;
326
+	$h = $q * 4294967296.0 + $r * 429.0 + $mq;
327
+
328
+	$h = sprintf('%.0f', $h);
329
+	$l = sprintf('%07.0f', $l);
330
+	if ($h == '0') {
331
+		return sprintf('%.0f', (float)$l);
332
+	}
333
+	return $h . $l;
334 334
 }
335 335
 
336 336
 /**
@@ -342,70 +342,70 @@  discard block
 block discarded – undo
342 342
  */
343 343
 function sphUnpackI64($value)
344 344
 {
345
-    list($hi, $lo) = array_values(unpack('N*N*', $value));
346
-
347
-    // x64
348
-    if (PHP_INT_SIZE >= 8) {
349
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
350
-            $hi += 1 << 32;
351
-        }
352
-        if ($lo < 0) {
353
-            $lo += 1 << 32;
354
-        }
355
-
356
-        return ($hi << 32) + $lo;
357
-    }
358
-
359
-    if ($hi == 0) { // x32, int
360
-        if ($lo > 0) {
361
-            return $lo;
362
-        }
363
-        return sprintf('%u', $lo);
364
-    } elseif ($hi == -1) { // x32, int
365
-        if ($lo < 0) {
366
-            return $lo;
367
-        }
368
-        return sprintf('%.0f', $lo - 4294967296.0);
369
-    }
370
-
371
-    $neg = '';
372
-    $c = 0;
373
-    if ($hi < 0) {
374
-        $hi = ~$hi;
375
-        $lo = ~$lo;
376
-        $c = 1;
377
-        $neg = '-';
378
-    }
379
-
380
-    $hi = sprintf('%u', $hi);
381
-    $lo = sprintf('%u', $lo);
382
-
383
-    // x32, bcmath
384
-    if (function_exists('bcmul')) {
385
-        return $neg . bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
386
-    }
387
-
388
-    // x32, no-bcmath
389
-    $hi = (float)$hi;
390
-    $lo = (float)$lo;
391
-
392
-    $q = floor($hi / 10000000.0);
393
-    $r = $hi - $q * 10000000.0;
394
-    $m = $lo + $r * 4967296.0;
395
-    $mq = floor($m / 10000000.0);
396
-    $l = $m - $mq * 10000000.0 + $c;
397
-    $h = $q * 4294967296.0 + $r * 429.0 + $mq;
398
-    if ($l == 10000000) {
399
-        $l = 0;
400
-        $h += 1;
401
-    }
402
-
403
-    $h = sprintf('%.0f', $h);
404
-    $l = sprintf('%07.0f', $l);
405
-    if ($h == '0') {
406
-        return $neg . sprintf('%.0f', (float)$l);
407
-    }
408
-    return $neg . $h . $l;
345
+	list($hi, $lo) = array_values(unpack('N*N*', $value));
346
+
347
+	// x64
348
+	if (PHP_INT_SIZE >= 8) {
349
+		if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
350
+			$hi += 1 << 32;
351
+		}
352
+		if ($lo < 0) {
353
+			$lo += 1 << 32;
354
+		}
355
+
356
+		return ($hi << 32) + $lo;
357
+	}
358
+
359
+	if ($hi == 0) { // x32, int
360
+		if ($lo > 0) {
361
+			return $lo;
362
+		}
363
+		return sprintf('%u', $lo);
364
+	} elseif ($hi == -1) { // x32, int
365
+		if ($lo < 0) {
366
+			return $lo;
367
+		}
368
+		return sprintf('%.0f', $lo - 4294967296.0);
369
+	}
370
+
371
+	$neg = '';
372
+	$c = 0;
373
+	if ($hi < 0) {
374
+		$hi = ~$hi;
375
+		$lo = ~$lo;
376
+		$c = 1;
377
+		$neg = '-';
378
+	}
379
+
380
+	$hi = sprintf('%u', $hi);
381
+	$lo = sprintf('%u', $lo);
382
+
383
+	// x32, bcmath
384
+	if (function_exists('bcmul')) {
385
+		return $neg . bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
386
+	}
387
+
388
+	// x32, no-bcmath
389
+	$hi = (float)$hi;
390
+	$lo = (float)$lo;
391
+
392
+	$q = floor($hi / 10000000.0);
393
+	$r = $hi - $q * 10000000.0;
394
+	$m = $lo + $r * 4967296.0;
395
+	$mq = floor($m / 10000000.0);
396
+	$l = $m - $mq * 10000000.0 + $c;
397
+	$h = $q * 4294967296.0 + $r * 429.0 + $mq;
398
+	if ($l == 10000000) {
399
+		$l = 0;
400
+		$h += 1;
401
+	}
402
+
403
+	$h = sprintf('%.0f', $h);
404
+	$l = sprintf('%07.0f', $l);
405
+	if ($h == '0') {
406
+		return $neg . sprintf('%.0f', (float)$l);
407
+	}
408
+	return $neg . $h . $l;
409 409
 }
410 410
 
411 411
 /**
@@ -415,16 +415,16 @@  discard block
 block discarded – undo
415 415
  */
416 416
 function sphFixUint($value)
417 417
 {
418
-    if (PHP_INT_SIZE >= 8) {
419
-        // x64 route, workaround broken unpack() in 5.2.2+
420
-        if ($value < 0) {
421
-            $value += 1 << 32;
422
-        }
423
-        return $value;
424
-    } else {
425
-        // x32 route, workaround php signed/unsigned braindamage
426
-        return sprintf('%u', $value);
427
-    }
418
+	if (PHP_INT_SIZE >= 8) {
419
+		// x64 route, workaround broken unpack() in 5.2.2+
420
+		if ($value < 0) {
421
+			$value += 1 << 32;
422
+		}
423
+		return $value;
424
+	} else {
425
+		// x32 route, workaround php signed/unsigned braindamage
426
+		return sprintf('%u', $value);
427
+	}
428 428
 }
429 429
 
430 430
 /**
@@ -436,13 +436,13 @@  discard block
 block discarded – undo
436 436
  */
437 437
 function sphSetBit($flag, $bit, $on)
438 438
 {
439
-    if ($on) {
440
-        $flag |= 1 << $bit;
441
-    } else {
442
-        $reset = 16777215 ^ (1 << $bit);
443
-        $flag = $flag & $reset;
444
-    }
445
-    return $flag;
439
+	if ($on) {
440
+		$flag |= 1 << $bit;
441
+	} else {
442
+		$reset = 16777215 ^ (1 << $bit);
443
+		$flag = $flag & $reset;
444
+	}
445
+	return $flag;
446 446
 }
447 447
 
448 448
 
@@ -451,1926 +451,1926 @@  discard block
 block discarded – undo
451 451
  */
452 452
 class SphinxClient
453 453
 {
454
-    /**
455
-     * Searchd host
456
-     *
457
-     * @var string
458
-     */
459
-    protected $host = 'localhost';
460
-
461
-    /**
462
-     * Searchd port
463
-     *
464
-     * @var int
465
-     */
466
-    protected $port = 9312;
467
-
468
-    /**
469
-     * How many records to seek from result-set start
470
-     *
471
-     * @var int
472
-     */
473
-    protected $offset = 0;
474
-
475
-    /**
476
-     * How many records to return from result-set starting at offset
477
-     *
478
-     * @var int
479
-     */
480
-    protected $limit = 20;
481
-
482
-    /**
483
-     * Query matching mode
484
-     *
485
-     * @var int
486
-     */
487
-    protected $mode = SPH_MATCH_EXTENDED2;
488
-
489
-    /**
490
-     * Per-field weights (default is 1 for all fields)
491
-     *
492
-     * @var array
493
-     */
494
-    protected $weights = array();
495
-
496
-    /**
497
-     * Match sorting mode
498
-     *
499
-     * @var int
500
-     */
501
-    protected $sort = SPH_SORT_RELEVANCE;
502
-
503
-    /**
504
-     * Attribute to sort by
505
-     *
506
-     * @var string
507
-     */
508
-    protected $sort_by = '';
509
-
510
-    /**
511
-     * Min ID to match (0 means no limit)
512
-     *
513
-     * @var int
514
-     */
515
-    protected $min_id = 0;
516
-
517
-    /**
518
-     * Max ID to match (0 means no limit)
519
-     *
520
-     * @var int
521
-     */
522
-    protected $max_id = 0;
523
-
524
-    /**
525
-     * Search filters
526
-     *
527
-     * @var array
528
-     */
529
-    protected $filters = array();
530
-
531
-    /**
532
-     * Group-by attribute name
533
-     *
534
-     * @var string
535
-     */
536
-    protected $group_by = '';
537
-
538
-    /**
539
-     * Group-by function (to pre-process group-by attribute value with)
540
-     *
541
-     * @var int
542
-     */
543
-    protected $group_func = SPH_GROUPBY_DAY;
544
-
545
-    /**
546
-     * Group-by sorting clause (to sort groups in result set with)
547
-     *
548
-     * @var string
549
-     */
550
-    protected $group_sort = '@group desc';
551
-
552
-    /**
553
-     * Group-by count-distinct attribute
554
-     *
555
-     * @var string
556
-     */
557
-    protected $group_distinct = '';
558
-
559
-    /**
560
-     * Max matches to retrieve
561
-     *
562
-     * @var int
563
-     */
564
-    protected $max_matches = 1000;
565
-
566
-    /**
567
-     * Cutoff to stop searching at
568
-     *
569
-     * @var int
570
-     */
571
-    protected $cutoff = 0;
572
-
573
-    /**
574
-     * Distributed retries count
575
-     *
576
-     * @var int
577
-     */
578
-    protected $retry_count = 0;
579
-
580
-    /**
581
-     * Distributed retries delay
582
-     *
583
-     * @var int
584
-     */
585
-    protected $retry_delay = 0;
586
-
587
-    /**
588
-     * Geographical anchor point
589
-     *
590
-     * @var array
591
-     */
592
-    protected $anchor = array();
593
-
594
-    /**
595
-     * Per-index weights
596
-     *
597
-     * @var array
598
-     */
599
-    protected $index_weights = array();
600
-
601
-    /**
602
-     * Ranking mode
603
-     *
604
-     * @var int
605
-     */
606
-    protected $ranker = SPH_RANK_PROXIMITY_BM25;
607
-
608
-    /**
609
-     * Ranking mode expression (for SPH_RANK_EXPR)
610
-     *
611
-     * @var string
612
-     */
613
-    protected $rank_expr = '';
614
-
615
-    /**
616
-     * Max query time, milliseconds (0 means no limit)
617
-     *
618
-     * @var int
619
-     */
620
-    protected $max_query_time = 0;
621
-
622
-    /**
623
-     * Per-field-name weights
624
-     *
625
-     * @var array
626
-     */
627
-    protected $field_weights = array();
628
-
629
-    /**
630
-     * Per-query attribute values overrides
631
-     *
632
-     * @var array
633
-     */
634
-    protected $overrides = array();
635
-
636
-    /**
637
-     * Select-list (attributes or expressions, with optional aliases)
638
-     *
639
-     * @var string
640
-     */
641
-    protected $select = '*';
642
-
643
-    /**
644
-     * Per-query various flags
645
-     *
646
-     * @var int
647
-     */
648
-    protected $query_flags = 0;
649
-
650
-    /**
651
-     * Per-query max_predicted_time
652
-     *
653
-     * @var int
654
-     */
655
-    protected $predicted_time = 0;
656
-
657
-    /**
658
-     * Outer match sort by
659
-     *
660
-     * @var string
661
-     */
662
-    protected $outer_order_by = '';
663
-
664
-    /**
665
-     * Outer offset
666
-     *
667
-     * @var int
668
-     */
669
-    protected $outer_offset = 0;
670
-
671
-    /**
672
-     * Outer limit
673
-     *
674
-     * @var int
675
-     */
676
-    protected $outer_limit = 0;
677
-
678
-    /**
679
-     * @var bool
680
-     */
681
-    protected $has_outer = false;
682
-
683
-    /**
684
-     * Last error message
685
-     *
686
-     * @var string
687
-     */
688
-    protected $error = '';
689
-
690
-    /**
691
-     * Last warning message
692
-     *
693
-     * @var string
694
-     */
695
-    protected $warning = '';
696
-
697
-    /**
698
-     * Connection error vs remote error flag
699
-     *
700
-     * @var bool
701
-     */
702
-    protected $conn_error = false;
703
-
704
-    /**
705
-     * Requests array for multi-query
706
-     *
707
-     * @var array
708
-     */
709
-    protected $reqs = array();
710
-
711
-    /**
712
-     * Stored mbstring encoding
713
-     *
714
-     * @var string
715
-     */
716
-    protected $mbenc = '';
717
-
718
-    /**
719
-     * Whether $result['matches'] should be a hash or an array
720
-     *
721
-     * @var bool
722
-     */
723
-    protected $array_result = false;
724
-
725
-    /**
726
-     * Connect timeout
727
-     *
728
-     * @var int
729
-     */
730
-    protected $timeout = 0;
731
-
732
-    /**
733
-     * @var bool
734
-     */
735
-    protected $path = false;
736
-
737
-    /**
738
-     * @var resource|bool
739
-     */
740
-    protected $socket = false;
741
-
742
-    /////////////////////////////////////////////////////////////////////////////
743
-    // common stuff
744
-    /////////////////////////////////////////////////////////////////////////////
745
-
746
-    public function __construct()
747
-    {
748
-        // default idf=tfidf_normalized
749
-        $this->query_flags = sphSetBit(0, 6, true);
750
-    }
751
-
752
-    public function __destruct()
753
-    {
754
-        if ($this->socket !== false) {
755
-            fclose($this->socket);
756
-        }
757
-    }
758
-
759
-    /**
760
-     * @return string
761
-     */
762
-    public function getLastError()
763
-    {
764
-        return $this->error;
765
-    }
766
-
767
-    /**
768
-     * @return string
769
-     */
770
-    public function getLastWarning()
771
-    {
772
-        return $this->warning;
773
-    }
774
-
775
-    /**
776
-     * Get last error flag (to tell network connection errors from searchd errors or broken responses)
777
-     *
778
-     * @return bool
779
-     */
780
-    public function isConnectError()
781
-    {
782
-        return $this->conn_error;
783
-    }
784
-
785
-    /**
786
-     * Set searchd host name and port
787
-     *
788
-     * @param string $host
789
-     * @param int $port
790
-     */
791
-    public function setServer($host, $port = 0)
792
-    {
793
-        assert(is_string($host));
794
-        if ($host[0] == '/') {
795
-            $this->path = 'unix://' . $host;
796
-            return;
797
-        }
798
-        if (substr($host, 0, 7) == 'unix://') {
799
-            $this->path = $host;
800
-            return;
801
-        }
802
-
803
-        $this->host = $host;
804
-        $port = intval($port);
805
-        assert(0 <= $port && $port < 65536);
806
-        $this->port = $port == 0 ? 9312 : $port;
807
-        $this->path = '';
808
-    }
809
-
810
-    /**
811
-     * Set server connection timeout (0 to remove)
812
-     *
813
-     * @param int $timeout
814
-     */
815
-    public function setConnectTimeout($timeout)
816
-    {
817
-        assert(is_numeric($timeout));
818
-        $this->timeout = $timeout;
819
-    }
820
-
821
-    /**
822
-     * @param resource $handle
823
-     * @param string $data
824
-     * @param int $length
825
-     *
826
-     * @return bool
827
-     */
828
-    protected function send($handle, $data, $length)
829
-    {
830
-        if (feof($handle) || fwrite($handle, $data, $length) !== $length) {
831
-            $this->error = 'connection unexpectedly closed (timed out?)';
832
-            $this->conn_error = true;
833
-            return false;
834
-        }
835
-        return true;
836
-    }
837
-
838
-    /////////////////////////////////////////////////////////////////////////////
839
-
840
-    /**
841
-     * Enter mbstring workaround mode
842
-     */
843
-    protected function mbPush()
844
-    {
845
-        $this->mbenc = '';
846
-        if (ini_get('mbstring.func_overload') & 2) {
847
-            $this->mbenc = mb_internal_encoding();
848
-            mb_internal_encoding('latin1');
849
-        }
850
-    }
851
-
852
-    /**
853
-     * Leave mbstring workaround mode
854
-     */
855
-    protected function mbPop()
856
-    {
857
-        if ($this->mbenc) {
858
-            mb_internal_encoding($this->mbenc);
859
-        }
860
-    }
861
-
862
-    /**
863
-     * Connect to searchd server
864
-     *
865
-     * @return bool|resource
866
-     */
867
-    protected function connect()
868
-    {
869
-        if ($this->socket !== false) {
870
-            // we are in persistent connection mode, so we have a socket
871
-            // however, need to check whether it's still alive
872
-            if (!@feof($this->socket)) {
873
-                return $this->socket;
874
-            }
875
-
876
-            // force reopen
877
-            $this->socket = false;
878
-        }
879
-
880
-        $errno = 0;
881
-        $errstr = '';
882
-        $this->conn_error = false;
883
-
884
-        if ($this->path) {
885
-            $host = $this->path;
886
-            $port = 0;
887
-        } else {
888
-            $host = $this->host;
889
-            $port = $this->port;
890
-        }
891
-
892
-        if ($this->timeout <= 0) {
893
-            $fp = @fsockopen($host, $port, $errno, $errstr);
894
-        } else {
895
-            $fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout);
896
-        }
897
-
898
-        if (!$fp) {
899
-            if ($this->path) {
900
-                $location = $this->path;
901
-            } else {
902
-                $location = "{$this->host}:{$this->port}";
903
-            }
904
-
905
-            $errstr = trim($errstr);
906
-            $this->error = "connection to $location failed (errno=$errno, msg=$errstr)";
907
-            $this->conn_error = true;
908
-            return false;
909
-        }
910
-
911
-        // send my version
912
-        // this is a subtle part. we must do it before (!) reading back from searchd.
913
-        // because otherwise under some conditions (reported on FreeBSD for instance)
914
-        // TCP stack could throttle write-write-read pattern because of Nagle.
915
-        if (!$this->send($fp, pack('N', 1), 4)) {
916
-            fclose($fp);
917
-            $this->error = 'failed to send client protocol version';
918
-            return false;
919
-        }
920
-
921
-        // check version
922
-        list(, $v) = unpack('N*', fread($fp, 4));
923
-        $v = (int)$v;
924
-        if ($v < 1) {
925
-            fclose($fp);
926
-            $this->error = "expected searchd protocol version 1+, got version '$v'";
927
-            return false;
928
-        }
929
-
930
-        return $fp;
931
-    }
932
-
933
-    /**
934
-     * Get and check response packet from searchd server
935
-     *
936
-     * @param resource $fp
937
-     * @param int $client_ver
938
-     *
939
-     * @return bool|string
940
-     */
941
-    protected function getResponse($fp, $client_ver)
942
-    {
943
-        $response = '';
944
-        $len = 0;
945
-
946
-        $header = fread($fp, 8);
947
-        if (strlen($header) == 8) {
948
-            list($status, $ver, $len) = array_values(unpack('n2a/Nb', $header));
949
-            $left = $len;
950
-            while ($left > 0 && !feof($fp)) {
951
-                $chunk = fread($fp, min(8192, $left));
952
-                if ($chunk) {
953
-                    $response .= $chunk;
954
-                    $left -= strlen($chunk);
955
-                }
956
-            }
957
-        }
958
-
959
-        if ($this->socket === false) {
960
-            fclose($fp);
961
-        }
962
-
963
-        // check response
964
-        $read = strlen($response);
965
-        if (!$response || $read != $len) {
966
-            $this->error = $len
967
-                ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
968
-                : 'received zero-sized searchd response';
969
-            return false;
970
-        }
971
-
972
-        // check status
973
-        if ($status == SEARCHD_WARNING) {
974
-            list(, $wlen) = unpack('N*', substr($response, 0, 4));
975
-            $this->warning = substr($response, 4, $wlen);
976
-            return substr($response, 4 + $wlen);
977
-        }
978
-        if ($status == SEARCHD_ERROR) {
979
-            $this->error = 'searchd error: ' . substr($response, 4);
980
-            return false;
981
-        }
982
-        if ($status == SEARCHD_RETRY) {
983
-            $this->error = 'temporary searchd error: ' . substr($response, 4);
984
-            return false;
985
-        }
986
-        if ($status != SEARCHD_OK) {
987
-            $this->error = "unknown status code '$status'";
988
-            return false;
989
-        }
990
-
991
-        // check version
992
-        if ($ver < $client_ver) {
993
-            $this->warning = sprintf(
994
-                'searchd command v.%d.%d older than client\'s v.%d.%d, some options might not work',
995
-                $ver >> 8,
996
-                $ver & 0xff,
997
-                $client_ver >> 8,
998
-                $client_ver & 0xff
999
-            );
1000
-        }
1001
-
1002
-        return $response;
1003
-    }
1004
-
1005
-    /////////////////////////////////////////////////////////////////////////////
1006
-    // searching
1007
-    /////////////////////////////////////////////////////////////////////////////
1008
-
1009
-    /**
1010
-     * Set offset and count into result set, and optionally set max-matches and cutoff limits
1011
-     *
1012
-     * @param int $offset
1013
-     * @param int $limit
1014
-     * @param int $max
1015
-     * @param int $cutoff
1016
-     */
1017
-    public function setLimits($offset, $limit, $max = 0, $cutoff = 0)
1018
-    {
1019
-        assert(is_int($offset));
1020
-        assert(is_int($limit));
1021
-        assert($offset >= 0);
1022
-        assert($limit > 0);
1023
-        assert($max >= 0);
1024
-        $this->offset = $offset;
1025
-        $this->limit = $limit;
1026
-        if ($max > 0) {
1027
-            $this->max_matches = $max;
1028
-        }
1029
-        if ($cutoff > 0) {
1030
-            $this->cutoff = $cutoff;
1031
-        }
1032
-    }
1033
-
1034
-    /**
1035
-     * Set maximum query time, in milliseconds, per-index, 0 means 'do not limit'
1036
-     *
1037
-     * @param int $max
1038
-     */
1039
-    public function setMaxQueryTime($max)
1040
-    {
1041
-        assert(is_int($max));
1042
-        assert($max >= 0);
1043
-        $this->max_query_time = $max;
1044
-    }
1045
-
1046
-    /**
1047
-     * Set matching mode
1048
-     *
1049
-     * @param int $mode
1050
-     */
1051
-    public function setMatchMode($mode)
1052
-    {
1053
-        trigger_error(
1054
-            'DEPRECATED: Do not call this method or, even better, use SphinxQL instead of an API',
1055
-            E_USER_DEPRECATED
1056
-        );
1057
-        assert(in_array($mode, array(
1058
-            SPH_MATCH_ALL,
1059
-            SPH_MATCH_ANY,
1060
-            SPH_MATCH_PHRASE,
1061
-            SPH_MATCH_BOOLEAN,
1062
-            SPH_MATCH_EXTENDED,
1063
-            SPH_MATCH_FULLSCAN,
1064
-            SPH_MATCH_EXTENDED2
1065
-        )));
1066
-        $this->mode = $mode;
1067
-    }
1068
-
1069
-    /**
1070
-     * Set ranking mode
1071
-     *
1072
-     * @param int $ranker
1073
-     * @param string $rank_expr
1074
-     */
1075
-    public function setRankingMode($ranker, $rank_expr='')
1076
-    {
1077
-        assert($ranker === 0 || $ranker >= 1 && $ranker < SPH_RANK_TOTAL);
1078
-        assert(is_string($rank_expr));
1079
-        $this->ranker = $ranker;
1080
-        $this->rank_expr = $rank_expr;
1081
-    }
1082
-
1083
-    /**
1084
-     * Set matches sorting mode
1085
-     *
1086
-     * @param int $mode
1087
-     * @param string $sort_by
1088
-     */
1089
-    public function setSortMode($mode, $sort_by = '')
1090
-    {
1091
-        assert(in_array($mode, array(
1092
-            SPH_SORT_RELEVANCE,
1093
-            SPH_SORT_ATTR_DESC,
1094
-            SPH_SORT_ATTR_ASC,
1095
-            SPH_SORT_TIME_SEGMENTS,
1096
-            SPH_SORT_EXTENDED,
1097
-            SPH_SORT_EXPR
1098
-        )));
1099
-        assert(is_string($sort_by));
1100
-        assert($mode == SPH_SORT_RELEVANCE || strlen($sort_by) > 0);
1101
-
1102
-        $this->sort = $mode;
1103
-        $this->sort_by = $sort_by;
1104
-    }
1105
-
1106
-    /**
1107
-     * Bind per-field weights by order
1108
-     *
1109
-     * @deprecated use setFieldWeights() instead
1110
-     */
1111
-    public function setWeights()
1112
-    {
1113
-        throw new \RuntimeException('This method is now deprecated; please use setFieldWeights instead');
1114
-    }
1115
-
1116
-    /**
1117
-     * Bind per-field weights by name
1118
-     *
1119
-     * @param array $weights
1120
-     */
1121
-    public function setFieldWeights(array $weights)
1122
-    {
1123
-        foreach ($weights as $name => $weight) {
1124
-            assert(is_string($name));
1125
-            assert(is_int($weight));
1126
-        }
1127
-        $this->field_weights = $weights;
1128
-    }
1129
-
1130
-    /**
1131
-     * Bind per-index weights by name
1132
-     *
1133
-     * @param array $weights
1134
-     */
1135
-    public function setIndexWeights(array $weights)
1136
-    {
1137
-        foreach ($weights as $index => $weight) {
1138
-            assert(is_string($index));
1139
-            assert(is_int($weight));
1140
-        }
1141
-        $this->index_weights = $weights;
1142
-    }
1143
-
1144
-    /**
1145
-     * Set IDs range to match. Only match records if document ID is beetwen $min and $max (inclusive)
1146
-     *
1147
-     * @param int $min
1148
-     * @param int $max
1149
-     */
1150
-    public function setIDRange($min, $max)
1151
-    {
1152
-        assert(is_numeric($min));
1153
-        assert(is_numeric($max));
1154
-        assert($min <= $max);
1155
-
1156
-        $this->min_id = $min;
1157
-        $this->max_id = $max;
1158
-    }
1159
-
1160
-    /**
1161
-     * Set values set filter. Only match records where $attribute value is in given set
1162
-     *
1163
-     * @param string $attribute
1164
-     * @param array $values
1165
-     * @param bool $exclude
1166
-     */
1167
-    public function setFilter($attribute, array $values, $exclude = false)
1168
-    {
1169
-        assert(is_string($attribute));
1170
-        assert(count($values));
1171
-
1172
-        foreach ($values as $value) {
1173
-            assert(is_numeric($value));
1174
-        }
1175
-
1176
-        $this->filters[] = array(
1177
-            'type' => SPH_FILTER_VALUES,
1178
-            'attr' => $attribute,
1179
-            'exclude' => $exclude,
1180
-            'values' => $values
1181
-        );
1182
-    }
1183
-
1184
-    /**
1185
-     * Set string filter
1186
-     * Only match records where $attribute value is equal
1187
-     *
1188
-     * @param string $attribute
1189
-     * @param string $value
1190
-     * @param bool $exclude
1191
-     */
1192
-    public function setFilterString($attribute, $value, $exclude = false)
1193
-    {
1194
-        assert(is_string($attribute));
1195
-        assert(is_string($value));
1196
-        $this->filters[] = array(
1197
-            'type' => SPH_FILTER_STRING,
1198
-            'attr' => $attribute,
1199
-            'exclude' => $exclude,
1200
-            'value' => $value
1201
-        );
1202
-    }    
1203
-
1204
-    /**
1205
-     * Set range filter
1206
-     * Only match records if $attribute value is beetwen $min and $max (inclusive)
1207
-     *
1208
-     * @param string $attribute
1209
-     * @param int $min
1210
-     * @param int $max
1211
-     * @param bool $exclude
1212
-     */
1213
-    public function setFilterRange($attribute, $min, $max, $exclude = false)
1214
-    {
1215
-        assert(is_string($attribute));
1216
-        assert(is_numeric($min));
1217
-        assert(is_numeric($max));
1218
-        assert($min <= $max);
1219
-
1220
-        $this->filters[] = array(
1221
-            'type' => SPH_FILTER_RANGE,
1222
-            'attr' => $attribute,
1223
-            'exclude' => $exclude,
1224
-            'min' => $min,
1225
-            'max' => $max
1226
-        );
1227
-    }
1228
-
1229
-    /**
1230
-     * Set float range filter
1231
-     * Only match records if $attribute value is beetwen $min and $max (inclusive)
1232
-     *
1233
-     * @param string $attribute
1234
-     * @param int $min
1235
-     * @param int $max
1236
-     * @param bool $exclude
1237
-     */
1238
-    public function setFilterFloatRange($attribute, $min, $max, $exclude = false)
1239
-    {
1240
-        assert(is_string($attribute));
1241
-        assert(is_float($min));
1242
-        assert(is_float($max));
1243
-        assert($min <= $max);
1244
-
1245
-        $this->filters[] = array(
1246
-            'type' => SPH_FILTER_FLOATRANGE,
1247
-            'attr' => $attribute,
1248
-            'exclude' => $exclude,
1249
-            'min' => $min,
1250
-            'max' => $max
1251
-        );
1252
-    }
1253
-
1254
-    /**
1255
-     * Setup anchor point for geosphere distance calculations
1256
-     * Required to use @geodist in filters and sorting
1257
-     * Latitude and longitude must be in radians
1258
-     *
1259
-     * @param string $attr_lat
1260
-     * @param string $attr_long
1261
-     * @param float $lat
1262
-     * @param float $long
1263
-     */
1264
-    public function setGeoAnchor($attr_lat, $attr_long, $lat, $long)
1265
-    {
1266
-        assert(is_string($attr_lat));
1267
-        assert(is_string($attr_long));
1268
-        assert(is_float($lat));
1269
-        assert(is_float($long));
1270
-
1271
-        $this->anchor = array(
1272
-            'attrlat' => $attr_lat,
1273
-            'attrlong' => $attr_long,
1274
-            'lat' => $lat,
1275
-            'long' => $long
1276
-        );
1277
-    }
1278
-
1279
-    /**
1280
-     * Set grouping attribute and function
1281
-     *
1282
-     * @param string $attribute
1283
-     * @param string $func
1284
-     * @param string $group_sort
1285
-     */
1286
-    public function setGroupBy($attribute, $func, $group_sort = '@group desc')
1287
-    {
1288
-        assert(is_string($attribute));
1289
-        assert(is_string($group_sort));
1290
-        assert(in_array($func, array(
1291
-            SPH_GROUPBY_DAY,
1292
-            SPH_GROUPBY_WEEK,
1293
-            SPH_GROUPBY_MONTH,
1294
-            SPH_GROUPBY_YEAR,
1295
-            SPH_GROUPBY_ATTR,
1296
-            SPH_GROUPBY_ATTRPAIR
1297
-        )));
1298
-
1299
-        $this->group_by = $attribute;
1300
-        $this->group_func = $func;
1301
-        $this->group_sort = $group_sort;
1302
-    }
1303
-
1304
-    /**
1305
-     * Set count-distinct attribute for group-by queries
1306
-     *
1307
-     * @param string $attribute
1308
-     */
1309
-    public function setGroupDistinct($attribute)
1310
-    {
1311
-        assert(is_string($attribute));
1312
-        $this->group_distinct = $attribute;
1313
-    }
1314
-
1315
-    /**
1316
-     * Set distributed retries count and delay
1317
-     *
1318
-     * @param int $count
1319
-     * @param int $delay
1320
-     */
1321
-    public function setRetries($count, $delay = 0)
1322
-    {
1323
-        assert(is_int($count) && $count >= 0);
1324
-        assert(is_int($delay) && $delay >= 0);
1325
-        $this->retry_count = $count;
1326
-        $this->retry_delay = $delay;
1327
-    }
1328
-
1329
-    /**
1330
-     * Set result set format (hash or array; hash by default)
1331
-     * PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
1332
-     *
1333
-     * @param bool $array_result
1334
-     */
1335
-    public function setArrayResult($array_result)
1336
-    {
1337
-        assert(is_bool($array_result));
1338
-        $this->array_result = $array_result;
1339
-    }
1340
-
1341
-    /**
1342
-     * Set attribute values override
1343
-     * There can be only one override per attribute
1344
-     * $values must be a hash that maps document IDs to attribute values
1345
-     *
1346
-     * @deprecated Do not call this method. Use SphinxQL REMAP() function instead.
1347
-     *
1348
-     * @param string $attr_name
1349
-     * @param string $attr_type
1350
-     * @param array $values
1351
-     */
1352
-    public function setOverride($attr_name, $attr_type, array $values)
1353
-    {
1354
-        trigger_error(
1355
-            'DEPRECATED: Do not call this method. Use SphinxQL REMAP() function instead.',
1356
-            E_USER_DEPRECATED
1357
-        );
1358
-        assert(is_string($attr_name));
1359
-        assert(in_array($attr_type, array(
1360
-            SPH_ATTR_INTEGER,
1361
-            SPH_ATTR_TIMESTAMP,
1362
-            SPH_ATTR_BOOL,
1363
-            SPH_ATTR_FLOAT,
1364
-            SPH_ATTR_BIGINT
1365
-        )));
1366
-
1367
-        $this->overrides[$attr_name] = array(
1368
-            'attr' => $attr_name,
1369
-            'type' => $attr_type,
1370
-            'values' => $values
1371
-        );
1372
-    }
1373
-
1374
-    /**
1375
-     * Set select-list (attributes or expressions), SQL-like syntax
1376
-     *
1377
-     * @param string $select
1378
-     */
1379
-    public function setSelect($select)
1380
-    {
1381
-        assert(is_string($select));
1382
-        $this->select = $select;
1383
-    }
1384
-
1385
-    /**
1386
-     * @param string $flag_name
1387
-     * @param string|int $flag_value
1388
-     */
1389
-    public function setQueryFlag($flag_name, $flag_value)
1390
-    {
1391
-        $known_names = array(
1392
-            'reverse_scan',
1393
-            'sort_method',
1394
-            'max_predicted_time',
1395
-            'boolean_simplify',
1396
-            'idf',
1397
-            'global_idf',
1398
-            'low_priority'
1399
-        );
1400
-        $flags = array (
1401
-            'reverse_scan' => array(0, 1),
1402
-            'sort_method' => array('pq', 'kbuffer'),
1403
-            'max_predicted_time' => array(0),
1404
-            'boolean_simplify' => array(true, false),
1405
-            'idf' => array ('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
1406
-            'global_idf' => array(true, false),
1407
-            'low_priority' => array(true, false)
1408
-        );
1409
-
1410
-        assert(isset($flag_name, $known_names));
1411
-        assert(
1412
-            in_array($flag_value, $flags[$flag_name], true) ||
1413
-            ($flag_name == 'max_predicted_time' && is_int($flag_value) && $flag_value >= 0)
1414
-        );
1415
-
1416
-        switch ($flag_name) {
1417
-            case 'reverse_scan':
1418
-                $this->query_flags = sphSetBit($this->query_flags, 0, $flag_value == 1);
1419
-                break;
1420
-            case 'sort_method':
1421
-                $this->query_flags = sphSetBit($this->query_flags, 1, $flag_value == 'kbuffer');
1422
-                break;
1423
-            case 'max_predicted_time':
1424
-                $this->query_flags = sphSetBit($this->query_flags, 2, $flag_value > 0);
1425
-                $this->predicted_time = (int)$flag_value;
1426
-                break;
1427
-            case 'boolean_simplify':
1428
-                $this->query_flags = sphSetBit($this->query_flags, 3, $flag_value);
1429
-                break;
1430
-            case 'idf':
1431
-                if ($flag_value == 'normalized' || $flag_value == 'plain') {
1432
-                    $this->query_flags = sphSetBit($this->query_flags, 4, $flag_value == 'plain');
1433
-                }
1434
-                if ($flag_value == 'tfidf_normalized' || $flag_value == 'tfidf_unnormalized') {
1435
-                    $this->query_flags = sphSetBit($this->query_flags, 6, $flag_value == 'tfidf_normalized');
1436
-                }
1437
-                break;
1438
-            case 'global_idf':
1439
-                $this->query_flags = sphSetBit($this->query_flags, 5, $flag_value);
1440
-                break;
1441
-            case 'low_priority':
1442
-                $this->query_flags = sphSetBit($this->query_flags, 8, $flag_value);
1443
-                break;
1444
-        }
1445
-    }
1446
-
1447
-    /**
1448
-     * Set outer order by parameters
1449
-     *
1450
-     * @param string $order_by
1451
-     * @param int $offset
1452
-     * @param int $limit
1453
-     */
1454
-    public function setOuterSelect($order_by, $offset, $limit)
1455
-    {
1456
-        assert(is_string($order_by));
1457
-        assert(is_int($offset));
1458
-        assert(is_int($limit));
1459
-        assert($offset >= 0);
1460
-        assert($limit > 0);
1461
-
1462
-        $this->outer_order_by = $order_by;
1463
-        $this->outer_offset = $offset;
1464
-        $this->outer_limit = $limit;
1465
-        $this->has_outer = true;
1466
-    }
1467
-
1468
-
1469
-    //////////////////////////////////////////////////////////////////////////////
1470
-
1471
-    /**
1472
-     * Clear all filters (for multi-queries)
1473
-     */
1474
-    public function resetFilters()
1475
-    {
1476
-        $this->filters = array();
1477
-        $this->anchor = array();
1478
-    }
1479
-
1480
-    /**
1481
-     * Clear groupby settings (for multi-queries)
1482
-     */
1483
-    public function resetGroupBy()
1484
-    {
1485
-        $this->group_by = '';
1486
-        $this->group_func = SPH_GROUPBY_DAY;
1487
-        $this->group_sort = '@group desc';
1488
-        $this->group_distinct = '';
1489
-    }
1490
-
1491
-    /**
1492
-     * Clear all attribute value overrides (for multi-queries)
1493
-     */
1494
-    public function resetOverrides()
1495
-    {
1496
-        $this->overrides = array();
1497
-    }
1498
-
1499
-    public function resetQueryFlag()
1500
-    {
1501
-        $this->query_flags = sphSetBit(0, 6, true); // default idf=tfidf_normalized
1502
-        $this->predicted_time = 0;
1503
-    }
1504
-
1505
-    public function resetOuterSelect()
1506
-    {
1507
-        $this->outer_order_by = '';
1508
-        $this->outer_offset = 0;
1509
-        $this->outer_limit = 0;
1510
-        $this->has_outer = false;
1511
-    }
1512
-
1513
-    //////////////////////////////////////////////////////////////////////////////
1514
-
1515
-    /**
1516
-     * Connect to searchd server, run given search query through given indexes, and return the search results
1517
-     *
1518
-     * @param string  $query
1519
-     * @param string $index
1520
-     * @param string $comment
1521
-     *
1522
-     * @return bool
1523
-     */
1524
-    public function query($query, $index = '*', $comment = '')
1525
-    {
1526
-        assert(empty($this->reqs));
1527
-
1528
-        $this->addQuery($query, $index, $comment);
1529
-        $results = $this->runQueries();
1530
-        $this->reqs = array(); // just in case it failed too early
1531
-
1532
-        if (!is_array($results)) {
1533
-            return false; // probably network error; error message should be already filled
1534
-        }
1535
-
1536
-        $this->error = $results[0]['error'];
1537
-        $this->warning = $results[0]['warning'];
1538
-
1539
-        if ($results[0]['status'] == SEARCHD_ERROR) {
1540
-            return false;
1541
-        } else {
1542
-            return $results[0];
1543
-        }
1544
-    }
1545
-
1546
-    /**
1547
-     * Helper to pack floats in network byte order
1548
-     *
1549
-     * @param float $float
1550
-     *
1551
-     * @return string
1552
-     */
1553
-    protected function packFloat($float)
1554
-    {
1555
-        $t1 = pack('f', $float); // machine order
1556
-        list(, $t2) = unpack('L*', $t1); // int in machine order
1557
-        return pack('N', $t2);
1558
-    }
1559
-
1560
-    /**
1561
-     * Add query to multi-query batch
1562
-     * Returns index into results array from RunQueries() call
1563
-     *
1564
-     * @param string $query
1565
-     * @param string $index
1566
-     * @param string $comment
1567
-     *
1568
-     * @return int
1569
-     */
1570
-    public function addQuery($query, $index = '*', $comment = '')
1571
-    {
1572
-        // mbstring workaround
1573
-        $this->mbPush();
1574
-
1575
-        // build request
1576
-        $req = pack('NNNNN', $this->query_flags, $this->offset, $this->limit, $this->mode, $this->ranker);
1577
-        if ($this->ranker == SPH_RANK_EXPR) {
1578
-            $req .= pack('N', strlen($this->rank_expr)) . $this->rank_expr;
1579
-        }
1580
-        $req .= pack('N', $this->sort); // (deprecated) sort mode
1581
-        $req .= pack('N', strlen($this->sort_by)) . $this->sort_by;
1582
-        $req .= pack('N', strlen($query)) . $query; // query itself
1583
-        $req .= pack('N', count($this->weights)); // weights
1584
-        foreach ($this->weights as $weight) {
1585
-            $req .= pack('N', (int)$weight);
1586
-        }
1587
-        $req .= pack('N', strlen($index)) . $index; // indexes
1588
-        $req .= pack('N', 1); // id64 range marker
1589
-        $req .= sphPackU64($this->min_id) . sphPackU64($this->max_id); // id64 range
1590
-
1591
-        // filters
1592
-        $req .= pack('N', count($this->filters));
1593
-        foreach ($this->filters as $filter) {
1594
-            $req .= pack('N', strlen($filter['attr'])) . $filter['attr'];
1595
-            $req .= pack('N', $filter['type']);
1596
-            switch ($filter['type']) {
1597
-                case SPH_FILTER_VALUES:
1598
-                    $req .= pack('N', count($filter['values']));
1599
-                    foreach ($filter['values'] as $value) {
1600
-                        $req .= sphPackI64($value);
1601
-                    }
1602
-                    break;
1603
-                case SPH_FILTER_RANGE:
1604
-                    $req .= sphPackI64($filter['min']) . sphPackI64($filter['max']);
1605
-                    break;
1606
-                case SPH_FILTER_FLOATRANGE:
1607
-                    $req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']);
1608
-                    break;
1609
-                case SPH_FILTER_STRING:
1610
-                    $req .= pack('N', strlen($filter['value'])) . $filter['value'];
1611
-                    break;
1612
-                default:
1613
-                    assert(0 && 'internal error: unhandled filter type');
1614
-            }
1615
-            $req .= pack('N', $filter['exclude']);
1616
-        }
1617
-
1618
-        // group-by clause, max-matches count, group-sort clause, cutoff count
1619
-        $req .= pack('NN', $this->group_func, strlen($this->group_by)) . $this->group_by;
1620
-        $req .= pack('N', $this->max_matches);
1621
-        $req .= pack('N', strlen($this->group_sort)) . $this->group_sort;
1622
-        $req .= pack('NNN', $this->cutoff, $this->retry_count, $this->retry_delay);
1623
-        $req .= pack('N', strlen($this->group_distinct)) . $this->group_distinct;
1624
-
1625
-        // anchor point
1626
-        if (empty($this->anchor)) {
1627
-            $req .= pack('N', 0);
1628
-        } else {
1629
-            $a =& $this->anchor;
1630
-            $req .= pack('N', 1);
1631
-            $req .= pack('N', strlen($a['attrlat'])) . $a['attrlat'];
1632
-            $req .= pack('N', strlen($a['attrlong'])) . $a['attrlong'];
1633
-            $req .= $this->packFloat($a['lat']) . $this->packFloat($a['long']);
1634
-        }
1635
-
1636
-        // per-index weights
1637
-        $req .= pack('N', count($this->index_weights));
1638
-        foreach ($this->index_weights as $idx => $weight) {
1639
-            $req .= pack('N', strlen($idx)) . $idx . pack('N', $weight);
1640
-        }
1641
-
1642
-        // max query time
1643
-        $req .= pack('N', $this->max_query_time);
1644
-
1645
-        // per-field weights
1646
-        $req .= pack('N', count($this->field_weights));
1647
-        foreach ($this->field_weights as $field => $weight) {
1648
-            $req .= pack('N', strlen($field)) . $field . pack('N', $weight);
1649
-        }
1650
-
1651
-        // comment
1652
-        $req .= pack('N', strlen($comment)) . $comment;
1653
-
1654
-        // attribute overrides
1655
-        $req .= pack('N', count($this->overrides));
1656
-        foreach ($this->overrides as $key => $entry) {
1657
-            $req .= pack('N', strlen($entry['attr'])) . $entry['attr'];
1658
-            $req .= pack('NN', $entry['type'], count($entry['values']));
1659
-            foreach ($entry['values'] as $id => $val) {
1660
-                assert(is_numeric($id));
1661
-                assert(is_numeric($val));
1662
-
1663
-                $req .= sphPackU64($id);
1664
-                switch ($entry['type']) {
1665
-                    case SPH_ATTR_FLOAT:
1666
-                        $req .= $this->packFloat($val);
1667
-                        break;
1668
-                    case SPH_ATTR_BIGINT:
1669
-                        $req .= sphPackI64($val);
1670
-                        break;
1671
-                    default:
1672
-                        $req .= pack('N', $val);
1673
-                        break;
1674
-                }
1675
-            }
1676
-        }
1677
-
1678
-        // select-list
1679
-        $req .= pack('N', strlen($this->select)) . $this->select;
1680
-
1681
-        // max_predicted_time
1682
-        if ($this->predicted_time > 0) {
1683
-            $req .= pack('N', (int)$this->predicted_time);
1684
-        }
1685
-
1686
-        $req .= pack('N', strlen($this->outer_order_by)) . $this->outer_order_by;
1687
-        $req .= pack('NN', $this->outer_offset, $this->outer_limit);
1688
-        if ($this->has_outer) {
1689
-            $req .= pack('N', 1);
1690
-        } else {
1691
-            $req .= pack('N', 0);
1692
-        }
1693
-
1694
-        // mbstring workaround
1695
-        $this->mbPop();
1696
-
1697
-        // store request to requests array
1698
-        $this->reqs[] = $req;
1699
-        return count($this->reqs) - 1;
1700
-    }
1701
-
1702
-    /**
1703
-     * Connect to searchd, run queries batch, and return an array of result sets
1704
-     *
1705
-     * @return array|bool
1706
-     */
1707
-    public function runQueries()
1708
-    {
1709
-        if (empty($this->reqs)) {
1710
-            $this->error = 'no queries defined, issue AddQuery() first';
1711
-            return false;
1712
-        }
1713
-
1714
-        // mbstring workaround
1715
-        $this->mbPush();
1716
-
1717
-        if (!($fp = $this->connect())) {
1718
-            $this->mbPop();
1719
-            return false;
1720
-        }
1721
-
1722
-        // send query, get response
1723
-        $nreqs = count($this->reqs);
1724
-        $req = join('', $this->reqs);
1725
-        $len = 8 + strlen($req);
1726
-        $req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs) . $req; // add header
1727
-
1728
-        if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_SEARCH))) {
1729
-            $this->mbPop();
1730
-            return false;
1731
-        }
1732
-
1733
-        // query sent ok; we can reset reqs now
1734
-        $this->reqs = array();
1735
-
1736
-        // parse and return response
1737
-        return $this->parseSearchResponse($response, $nreqs);
1738
-    }
1739
-
1740
-    /**
1741
-     * Parse and return search query (or queries) response
1742
-     *
1743
-     * @param string $response
1744
-     * @param int $nreqs
1745
-     *
1746
-     * @return array
1747
-     */
1748
-    protected function parseSearchResponse($response, $nreqs)
1749
-    {
1750
-        $p = 0; // current position
1751
-        $max = strlen($response); // max position for checks, to protect against broken responses
1752
-
1753
-        $results = array();
1754
-        for ($ires = 0; $ires < $nreqs && $p < $max; $ires++) {
1755
-            $results[] = array();
1756
-            $result =& $results[$ires];
1757
-
1758
-            $result['error'] = '';
1759
-            $result['warning'] = '';
1760
-
1761
-            // extract status
1762
-            list(, $status) = unpack('N*', substr($response, $p, 4));
1763
-            $p += 4;
1764
-            $result['status'] = $status;
1765
-            if ($status != SEARCHD_OK) {
1766
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1767
-                $p += 4;
1768
-                $message = substr($response, $p, $len);
1769
-                $p += $len;
1770
-
1771
-                if ($status == SEARCHD_WARNING) {
1772
-                    $result['warning'] = $message;
1773
-                } else {
1774
-                    $result['error'] = $message;
1775
-                    continue;
1776
-                }
1777
-            }
1778
-
1779
-            // read schema
1780
-            $fields = array();
1781
-            $attrs = array();
1782
-
1783
-            list(, $nfields) = unpack('N*', substr($response, $p, 4));
1784
-            $p += 4;
1785
-            while ($nfields --> 0 && $p < $max) {
1786
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1787
-                $p += 4;
1788
-                $fields[] = substr($response, $p, $len);
1789
-                $p += $len;
1790
-            }
1791
-            $result['fields'] = $fields;
1792
-
1793
-            list(, $nattrs) = unpack('N*', substr($response, $p, 4));
1794
-            $p += 4;
1795
-            while ($nattrs --> 0 && $p < $max) {
1796
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1797
-                $p += 4;
1798
-                $attr = substr($response, $p, $len);
1799
-                $p += $len;
1800
-                list(, $type) = unpack('N*', substr($response, $p, 4));
1801
-                $p += 4;
1802
-                $attrs[$attr] = $type;
1803
-            }
1804
-            $result['attrs'] = $attrs;
1805
-
1806
-            // read match count
1807
-            list(, $count) = unpack('N*', substr($response, $p, 4));
1808
-            $p += 4;
1809
-            list(, $id64) = unpack('N*', substr($response, $p, 4));
1810
-            $p += 4;
1811
-
1812
-            // read matches
1813
-            $idx = -1;
1814
-            while ($count --> 0 && $p < $max) {
1815
-                // index into result array
1816
-                $idx++;
1817
-
1818
-                // parse document id and weight
1819
-                if ($id64) {
1820
-                    $doc = sphUnpackU64(substr($response, $p, 8));
1821
-                    $p += 8;
1822
-                    list(,$weight) = unpack('N*', substr($response, $p, 4));
1823
-                    $p += 4;
1824
-                } else {
1825
-                    list($doc, $weight) = array_values(unpack('N*N*', substr($response, $p, 8)));
1826
-                    $p += 8;
1827
-                    $doc = sphFixUint($doc);
1828
-                }
1829
-                $weight = sprintf('%u', $weight);
1830
-
1831
-                // create match entry
1832
-                if ($this->array_result) {
1833
-                    $result['matches'][$idx] = array('id' => $doc, 'weight' => $weight);
1834
-                } else {
1835
-                    $result['matches'][$doc]['weight'] = $weight;
1836
-                }
1837
-
1838
-                // parse and create attributes
1839
-                $attrvals = array();
1840
-                foreach ($attrs as $attr => $type) {
1841
-                    // handle 64bit ints
1842
-                    if ($type == SPH_ATTR_BIGINT) {
1843
-                        $attrvals[$attr] = sphUnpackI64(substr($response, $p, 8));
1844
-                        $p += 8;
1845
-                        continue;
1846
-                    }
1847
-
1848
-                    // handle floats
1849
-                    if ($type == SPH_ATTR_FLOAT) {
1850
-                        list(, $uval) = unpack('N*', substr($response, $p, 4));
1851
-                        $p += 4;
1852
-                        list(, $fval) = unpack('f*', pack('L', $uval));
1853
-                        $attrvals[$attr] = $fval;
1854
-                        continue;
1855
-                    }
1856
-
1857
-                    // handle everything else as unsigned ints
1858
-                    list(, $val) = unpack('N*', substr($response, $p, 4));
1859
-                    $p += 4;
1860
-                    if ($type == SPH_ATTR_MULTI) {
1861
-                        $attrvals[$attr] = array();
1862
-                        $nvalues = $val;
1863
-                        while ($nvalues --> 0 && $p < $max) {
1864
-                            list(, $val) = unpack('N*', substr($response, $p, 4));
1865
-                            $p += 4;
1866
-                            $attrvals[$attr][] = sphFixUint($val);
1867
-                        }
1868
-                    } elseif ($type == SPH_ATTR_MULTI64) {
1869
-                        $attrvals[$attr] = array();
1870
-                        $nvalues = $val;
1871
-                        while ($nvalues > 0 && $p < $max) {
1872
-                            $attrvals[$attr][] = sphUnpackI64(substr($response, $p, 8));
1873
-                            $p += 8;
1874
-                            $nvalues -= 2;
1875
-                        }
1876
-                    } elseif ($type == SPH_ATTR_STRING) {
1877
-                        $attrvals[$attr] = substr($response, $p, $val);
1878
-                        $p += $val;
1879
-                    } elseif ($type == SPH_ATTR_FACTORS) {
1880
-                        $attrvals[$attr] = substr($response, $p, $val - 4);
1881
-                        $p += $val-4;
1882
-                    } else {
1883
-                        $attrvals[$attr] = sphFixUint($val);
1884
-                    }
1885
-                }
1886
-
1887
-                if ($this->array_result) {
1888
-                    $result['matches'][$idx]['attrs'] = $attrvals;
1889
-                } else {
1890
-                    $result['matches'][$doc]['attrs'] = $attrvals;
1891
-                }
1892
-            }
1893
-
1894
-            list($total, $total_found, $msecs, $words) = array_values(unpack('N*N*N*N*', substr($response, $p, 16)));
1895
-            $result['total'] = sprintf('%u', $total);
1896
-            $result['total_found'] = sprintf('%u', $total_found);
1897
-            $result['time'] = sprintf('%.3f', $msecs / 1000);
1898
-            $p += 16;
1899
-
1900
-            while ($words --> 0 && $p < $max) {
1901
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1902
-                $p += 4;
1903
-                $word = substr($response, $p, $len);
1904
-                $p += $len;
1905
-                list($docs, $hits) = array_values(unpack('N*N*', substr($response, $p, 8)));
1906
-                $p += 8;
1907
-                $result['words'][$word] = array (
1908
-                    'docs' => sprintf('%u', $docs),
1909
-                    'hits' => sprintf('%u', $hits)
1910
-                );
1911
-            }
1912
-        }
1913
-
1914
-        $this->mbPop();
1915
-        return $results;
1916
-    }
1917
-
1918
-    /////////////////////////////////////////////////////////////////////////////
1919
-    // excerpts generation
1920
-    /////////////////////////////////////////////////////////////////////////////
1921
-
1922
-    /**
1923
-     * Connect to searchd server, and generate exceprts (snippets) of given documents for given query.
1924
-     * Returns false on failure, an array of snippets on success
1925
-     *
1926
-     * @param array $docs
1927
-     * @param string $index
1928
-     * @param string $words
1929
-     * @param array $opts
1930
-     *
1931
-     * @return array|bool
1932
-     */
1933
-    public function buildExcerpts(array $docs, $index, $words, array $opts = array())
1934
-    {
1935
-        assert(is_string($index));
1936
-        assert(is_string($words));
1937
-
1938
-        $this->mbPush();
1939
-
1940
-        if (!($fp = $this->connect())) {
1941
-            $this->mbPop();
1942
-            return false;
1943
-        }
1944
-
1945
-        /////////////////
1946
-        // fixup options
1947
-        /////////////////
1948
-
1949
-        $opts = array_merge(array(
1950
-            'before_match' => '<b>',
1951
-            'after_match' => '</b>',
1952
-            'chunk_separator' => ' ... ',
1953
-            'limit' => 256,
1954
-            'limit_passages' => 0,
1955
-            'limit_words' => 0,
1956
-            'around' => 5,
1957
-            'exact_phrase' => false,
1958
-            'single_passage' => false,
1959
-            'use_boundaries' => false,
1960
-            'weight_order' => false,
1961
-            'query_mode' => false,
1962
-            'force_all_words' => false,
1963
-            'start_passage_id' => 1,
1964
-            'load_files' => false,
1965
-            'html_strip_mode' => 'index',
1966
-            'allow_empty' => false,
1967
-            'passage_boundary' => 'none',
1968
-            'emit_zones' => false,
1969
-            'load_files_scattered' => false
1970
-        ), $opts);
1971
-
1972
-        /////////////////
1973
-        // build request
1974
-        /////////////////
1975
-
1976
-        // v.1.2 req
1977
-        $flags = 1; // remove spaces
1978
-        if ($opts['exact_phrase']) {
1979
-            $flags |= 2;
1980
-        }
1981
-        if ($opts['single_passage']) {
1982
-            $flags |= 4;
1983
-        }
1984
-        if ($opts['use_boundaries']) {
1985
-            $flags |= 8;
1986
-        }
1987
-        if ($opts['weight_order']) {
1988
-            $flags |= 16;
1989
-        }
1990
-        if ($opts['query_mode']) {
1991
-            $flags |= 32;
1992
-        }
1993
-        if ($opts['force_all_words']) {
1994
-            $flags |= 64;
1995
-        }
1996
-        if ($opts['load_files']) {
1997
-            $flags |= 128;
1998
-        }
1999
-        if ($opts['allow_empty']) {
2000
-            $flags |= 256;
2001
-        }
2002
-        if ($opts['emit_zones']) {
2003
-            $flags |= 512;
2004
-        }
2005
-        if ($opts['load_files_scattered']) {
2006
-            $flags |= 1024;
2007
-        }
2008
-        $req = pack('NN', 0, $flags); // mode=0, flags=$flags
2009
-        $req .= pack('N', strlen($index)) . $index; // req index
2010
-        $req .= pack('N', strlen($words)) . $words; // req words
2011
-
2012
-        // options
2013
-        $req .= pack('N', strlen($opts['before_match'])) . $opts['before_match'];
2014
-        $req .= pack('N', strlen($opts['after_match'])) . $opts['after_match'];
2015
-        $req .= pack('N', strlen($opts['chunk_separator'])) . $opts['chunk_separator'];
2016
-        $req .= pack('NN', (int)$opts['limit'], (int)$opts['around']);
2017
-        $req .= pack('NNN', (int)$opts['limit_passages'], (int)$opts['limit_words'], (int)$opts['start_passage_id']); // v.1.2
2018
-        $req .= pack('N', strlen($opts['html_strip_mode'])) . $opts['html_strip_mode'];
2019
-        $req .= pack('N', strlen($opts['passage_boundary'])) . $opts['passage_boundary'];
2020
-
2021
-        // documents
2022
-        $req .= pack('N', count($docs));
2023
-        foreach ($docs as $doc) {
2024
-            assert(is_string($doc));
2025
-            $req .= pack('N', strlen($doc)) . $doc;
2026
-        }
2027
-
2028
-        ////////////////////////////
2029
-        // send query, get response
2030
-        ////////////////////////////
2031
-
2032
-        $len = strlen($req);
2033
-        $req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len) . $req; // add header
2034
-        if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_EXCERPT))) {
2035
-            $this->mbPop();
2036
-            return false;
2037
-        }
2038
-
2039
-        //////////////////
2040
-        // parse response
2041
-        //////////////////
2042
-
2043
-        $pos = 0;
2044
-        $res = array();
2045
-        $rlen = strlen($response);
2046
-        $count = count($docs);
2047
-        while ($count--) {
2048
-            list(, $len) = unpack('N*', substr($response, $pos, 4));
2049
-            $pos += 4;
2050
-
2051
-            if ($pos + $len > $rlen) {
2052
-                $this->error = 'incomplete reply';
2053
-                $this->mbPop();
2054
-                return false;
2055
-            }
2056
-            $res[] = $len ? substr($response, $pos, $len) : '';
2057
-            $pos += $len;
2058
-        }
2059
-
2060
-        $this->mbPop();
2061
-        return $res;
2062
-    }
2063
-
2064
-
2065
-    /////////////////////////////////////////////////////////////////////////////
2066
-    // keyword generation
2067
-    /////////////////////////////////////////////////////////////////////////////
2068
-
2069
-    /**
2070
-     * Connect to searchd server, and generate keyword list for a given query returns false on failure,
2071
-     * an array of words on success
2072
-     *
2073
-     * @param string $query
2074
-     * @param string $index
2075
-     * @param bool $hits
2076
-     *
2077
-     * @return array|bool
2078
-     */
2079
-    public function buildKeywords($query, $index, $hits)
2080
-    {
2081
-        assert(is_string($query));
2082
-        assert(is_string($index));
2083
-        assert(is_bool($hits));
2084
-
2085
-        $this->mbPush();
2086
-
2087
-        if (!($fp = $this->connect())) {
2088
-            $this->mbPop();
2089
-            return false;
2090
-        }
2091
-
2092
-        /////////////////
2093
-        // build request
2094
-        /////////////////
2095
-
2096
-        // v.1.0 req
2097
-        $req  = pack('N', strlen($query)) . $query; // req query
2098
-        $req .= pack('N', strlen($index)) . $index; // req index
2099
-        $req .= pack('N', (int)$hits);
2100
-
2101
-        ////////////////////////////
2102
-        // send query, get response
2103
-        ////////////////////////////
2104
-
2105
-        $len = strlen($req);
2106
-        $req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len) . $req; // add header
2107
-        if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_KEYWORDS))) {
2108
-            $this->mbPop();
2109
-            return false;
2110
-        }
2111
-
2112
-        //////////////////
2113
-        // parse response
2114
-        //////////////////
2115
-
2116
-        $pos = 0;
2117
-        $res = array();
2118
-        $rlen = strlen($response);
2119
-        list(, $nwords) = unpack('N*', substr($response, $pos, 4));
2120
-        $pos += 4;
2121
-        for ($i = 0; $i < $nwords; $i++) {
2122
-            list(, $len) = unpack('N*', substr($response, $pos, 4));
2123
-            $pos += 4;
2124
-            $tokenized = $len ? substr($response, $pos, $len) : '';
2125
-            $pos += $len;
2126
-
2127
-            list(, $len) = unpack('N*', substr($response, $pos, 4));
2128
-            $pos += 4;
2129
-            $normalized = $len ? substr($response, $pos, $len) : '';
2130
-            $pos += $len;
2131
-
2132
-            $res[] = array(
2133
-                'tokenized' => $tokenized,
2134
-                'normalized' => $normalized
2135
-            );
2136
-
2137
-            if ($hits) {
2138
-                list($ndocs, $nhits) = array_values(unpack('N*N*', substr($response, $pos, 8)));
2139
-                $pos += 8;
2140
-                $res[$i]['docs'] = $ndocs;
2141
-                $res[$i]['hits'] = $nhits;
2142
-            }
2143
-
2144
-            if ($pos > $rlen) {
2145
-                $this->error = 'incomplete reply';
2146
-                $this->mbPop();
2147
-                return false;
2148
-            }
2149
-        }
2150
-
2151
-        $this->mbPop();
2152
-        return $res;
2153
-    }
2154
-
2155
-    /**
2156
-     * @param string $string
2157
-     *
2158
-     * @return string
2159
-     */
2160
-    public function escapeString($string)
2161
-    {
2162
-        $from = array('\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=', '<');
2163
-        $to   = array('\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=', '\<');
2164
-
2165
-        return str_replace($from, $to, $string);
2166
-    }
2167
-
2168
-    /////////////////////////////////////////////////////////////////////////////
2169
-    // attribute updates
2170
-    /////////////////////////////////////////////////////////////////////////////
2171
-
2172
-    /**
2173
-     * Batch update given attributes in given rows in given indexes
2174
-     * Returns amount of updated documents (0 or more) on success, or -1 on failure
2175
-     *
2176
-     * @param string $index
2177
-     * @param array $attrs
2178
-     * @param array $values
2179
-     * @param bool $mva
2180
-     * @param bool $ignore_non_existent
2181
-     *
2182
-     * @return int
2183
-     */
2184
-    public function updateAttributes($index, array $attrs, array $values, $mva = false, $ignore_non_existent = false)
2185
-    {
2186
-        // verify everything
2187
-        assert(is_string($index));
2188
-        assert(is_bool($mva));
2189
-        assert(is_bool($ignore_non_existent));
2190
-
2191
-        foreach ($attrs as $attr) {
2192
-            assert(is_string($attr));
2193
-        }
2194
-
2195
-        foreach ($values as $id => $entry) {
2196
-            assert(is_numeric($id));
2197
-            assert(is_array($entry));
2198
-            assert(count($entry) == count($attrs));
2199
-            foreach ($entry as $v) {
2200
-                if ($mva) {
2201
-                    assert(is_array($v));
2202
-                    foreach ($v as $vv) {
2203
-                        assert(is_int($vv));
2204
-                    }
2205
-                } else {
2206
-                    assert(is_int($v));
2207
-                }
2208
-            }
2209
-        }
2210
-
2211
-        // build request
2212
-        $this->mbPush();
2213
-        $req = pack('N', strlen($index)) . $index;
2214
-
2215
-        $req .= pack('N', count($attrs));
2216
-        $req .= pack('N', $ignore_non_existent ? 1 : 0);
2217
-        foreach ($attrs as $attr) {
2218
-            $req .= pack('N', strlen($attr)) . $attr;
2219
-            $req .= pack('N', $mva ? 1 : 0);
2220
-        }
2221
-
2222
-        $req .= pack('N', count($values));
2223
-        foreach ($values as $id => $entry) {
2224
-            $req .= sphPackU64($id);
2225
-            foreach ($entry as $v) {
2226
-                $req .= pack('N', $mva ? count($v) : $v);
2227
-                if ($mva) {
2228
-                    foreach ($v as $vv) {
2229
-                        $req .= pack('N', $vv);
2230
-                    }
2231
-                }
2232
-            }
2233
-        }
2234
-
2235
-        // connect, send query, get response
2236
-        if (!($fp = $this->connect())) {
2237
-            $this->mbPop();
2238
-            return -1;
2239
-        }
2240
-
2241
-        $len = strlen($req);
2242
-        $req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len) . $req; // add header
2243
-        if (!$this->send($fp, $req, $len + 8)) {
2244
-            $this->mbPop();
2245
-            return -1;
2246
-        }
2247
-
2248
-        if (!($response = $this->getResponse($fp, VER_COMMAND_UPDATE))) {
2249
-            $this->mbPop();
2250
-            return -1;
2251
-        }
2252
-
2253
-        // parse response
2254
-        list(, $updated) = unpack('N*', substr($response, 0, 4));
2255
-        $this->mbPop();
2256
-        return $updated;
2257
-    }
2258
-
2259
-    /////////////////////////////////////////////////////////////////////////////
2260
-    // persistent connections
2261
-    /////////////////////////////////////////////////////////////////////////////
2262
-
2263
-    /**
2264
-     * @return bool
2265
-     */
2266
-    public function open()
2267
-    {
2268
-        if ($this->socket !== false) {
2269
-            $this->error = 'already connected';
2270
-            return false;
2271
-        }
2272
-        if (!($fp = $this->connect()))
2273
-            return false;
2274
-
2275
-        // command, command version = 0, body length = 4, body = 1
2276
-        $req = pack('nnNN', SEARCHD_COMMAND_PERSIST, 0, 4, 1);
2277
-        if (!$this->send($fp, $req, 12)) {
2278
-            return false;
2279
-        }
2280
-
2281
-        $this->socket = $fp;
2282
-        return true;
2283
-    }
2284
-
2285
-    /**
2286
-     * @return bool
2287
-     */
2288
-    public function close()
2289
-    {
2290
-        if ($this->socket === false) {
2291
-            $this->error = 'not connected';
2292
-            return false;
2293
-        }
2294
-
2295
-        fclose($this->socket);
2296
-        $this->socket = false;
2297
-
2298
-        return true;
2299
-    }
2300
-
2301
-    //////////////////////////////////////////////////////////////////////////
2302
-    // status
2303
-    //////////////////////////////////////////////////////////////////////////
2304
-
2305
-    /**
2306
-     * @param bool $session
2307
-     *
2308
-     * @return array|bool
2309
-     */
2310
-    public function status($session = false)
2311
-    {
2312
-        assert(is_bool($session));
2313
-
2314
-        $this->mbPush();
2315
-        if (!($fp = $this->connect())) {
2316
-            $this->mbPop();
2317
-            return false;
2318
-        }
2319
-
2320
-        $req = pack('nnNN', SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, $session ? 0 : 1); // len=4, body=1
2321
-        if (!$this->send($fp, $req, 12) || !($response = $this->getResponse($fp, VER_COMMAND_STATUS))) {
2322
-            $this->mbPop();
2323
-            return false;
2324
-        }
2325
-
2326
-        $res = substr($response, 4); // just ignore length, error handling, etc
2327
-        $p = 0;
2328
-        list($rows, $cols) = array_values(unpack('N*N*', substr($response, $p, 8)));
2329
-        $p += 8;
2330
-
2331
-        $res = array();
2332
-        for ($i = 0; $i < $rows; $i++) {
2333
-            for ($j = 0; $j < $cols; $j++) {
2334
-                list(, $len) = unpack('N*', substr($response, $p, 4));
2335
-                $p += 4;
2336
-                $res[$i][] = substr($response, $p, $len);
2337
-                $p += $len;
2338
-            }
2339
-        }
2340
-
2341
-        $this->mbPop();
2342
-        return $res;
2343
-    }
2344
-
2345
-    //////////////////////////////////////////////////////////////////////////
2346
-    // flush
2347
-    //////////////////////////////////////////////////////////////////////////
2348
-
2349
-    /**
2350
-     * @return int
2351
-     */
2352
-    public function flushAttributes()
2353
-    {
2354
-        $this->mbPush();
2355
-        if (!($fp = $this->connect())) {
2356
-            $this->mbPop();
2357
-            return -1;
2358
-        }
2359
-
2360
-        $req = pack('nnN', SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0); // len=0
2361
-        if (!$this->send($fp, $req, 8) || !($response = $this->getResponse($fp, VER_COMMAND_FLUSHATTRS))) {
2362
-            $this->mbPop();
2363
-            return -1;
2364
-        }
2365
-
2366
-        $tag = -1;
2367
-        if (strlen($response) == 4) {
2368
-            list(, $tag) = unpack('N*', $response);
2369
-        } else {
2370
-            $this->error = 'unexpected response length';
2371
-        }
2372
-
2373
-        $this->mbPop();
2374
-        return $tag;
2375
-    }
454
+	/**
455
+	 * Searchd host
456
+	 *
457
+	 * @var string
458
+	 */
459
+	protected $host = 'localhost';
460
+
461
+	/**
462
+	 * Searchd port
463
+	 *
464
+	 * @var int
465
+	 */
466
+	protected $port = 9312;
467
+
468
+	/**
469
+	 * How many records to seek from result-set start
470
+	 *
471
+	 * @var int
472
+	 */
473
+	protected $offset = 0;
474
+
475
+	/**
476
+	 * How many records to return from result-set starting at offset
477
+	 *
478
+	 * @var int
479
+	 */
480
+	protected $limit = 20;
481
+
482
+	/**
483
+	 * Query matching mode
484
+	 *
485
+	 * @var int
486
+	 */
487
+	protected $mode = SPH_MATCH_EXTENDED2;
488
+
489
+	/**
490
+	 * Per-field weights (default is 1 for all fields)
491
+	 *
492
+	 * @var array
493
+	 */
494
+	protected $weights = array();
495
+
496
+	/**
497
+	 * Match sorting mode
498
+	 *
499
+	 * @var int
500
+	 */
501
+	protected $sort = SPH_SORT_RELEVANCE;
502
+
503
+	/**
504
+	 * Attribute to sort by
505
+	 *
506
+	 * @var string
507
+	 */
508
+	protected $sort_by = '';
509
+
510
+	/**
511
+	 * Min ID to match (0 means no limit)
512
+	 *
513
+	 * @var int
514
+	 */
515
+	protected $min_id = 0;
516
+
517
+	/**
518
+	 * Max ID to match (0 means no limit)
519
+	 *
520
+	 * @var int
521
+	 */
522
+	protected $max_id = 0;
523
+
524
+	/**
525
+	 * Search filters
526
+	 *
527
+	 * @var array
528
+	 */
529
+	protected $filters = array();
530
+
531
+	/**
532
+	 * Group-by attribute name
533
+	 *
534
+	 * @var string
535
+	 */
536
+	protected $group_by = '';
537
+
538
+	/**
539
+	 * Group-by function (to pre-process group-by attribute value with)
540
+	 *
541
+	 * @var int
542
+	 */
543
+	protected $group_func = SPH_GROUPBY_DAY;
544
+
545
+	/**
546
+	 * Group-by sorting clause (to sort groups in result set with)
547
+	 *
548
+	 * @var string
549
+	 */
550
+	protected $group_sort = '@group desc';
551
+
552
+	/**
553
+	 * Group-by count-distinct attribute
554
+	 *
555
+	 * @var string
556
+	 */
557
+	protected $group_distinct = '';
558
+
559
+	/**
560
+	 * Max matches to retrieve
561
+	 *
562
+	 * @var int
563
+	 */
564
+	protected $max_matches = 1000;
565
+
566
+	/**
567
+	 * Cutoff to stop searching at
568
+	 *
569
+	 * @var int
570
+	 */
571
+	protected $cutoff = 0;
572
+
573
+	/**
574
+	 * Distributed retries count
575
+	 *
576
+	 * @var int
577
+	 */
578
+	protected $retry_count = 0;
579
+
580
+	/**
581
+	 * Distributed retries delay
582
+	 *
583
+	 * @var int
584
+	 */
585
+	protected $retry_delay = 0;
586
+
587
+	/**
588
+	 * Geographical anchor point
589
+	 *
590
+	 * @var array
591
+	 */
592
+	protected $anchor = array();
593
+
594
+	/**
595
+	 * Per-index weights
596
+	 *
597
+	 * @var array
598
+	 */
599
+	protected $index_weights = array();
600
+
601
+	/**
602
+	 * Ranking mode
603
+	 *
604
+	 * @var int
605
+	 */
606
+	protected $ranker = SPH_RANK_PROXIMITY_BM25;
607
+
608
+	/**
609
+	 * Ranking mode expression (for SPH_RANK_EXPR)
610
+	 *
611
+	 * @var string
612
+	 */
613
+	protected $rank_expr = '';
614
+
615
+	/**
616
+	 * Max query time, milliseconds (0 means no limit)
617
+	 *
618
+	 * @var int
619
+	 */
620
+	protected $max_query_time = 0;
621
+
622
+	/**
623
+	 * Per-field-name weights
624
+	 *
625
+	 * @var array
626
+	 */
627
+	protected $field_weights = array();
628
+
629
+	/**
630
+	 * Per-query attribute values overrides
631
+	 *
632
+	 * @var array
633
+	 */
634
+	protected $overrides = array();
635
+
636
+	/**
637
+	 * Select-list (attributes or expressions, with optional aliases)
638
+	 *
639
+	 * @var string
640
+	 */
641
+	protected $select = '*';
642
+
643
+	/**
644
+	 * Per-query various flags
645
+	 *
646
+	 * @var int
647
+	 */
648
+	protected $query_flags = 0;
649
+
650
+	/**
651
+	 * Per-query max_predicted_time
652
+	 *
653
+	 * @var int
654
+	 */
655
+	protected $predicted_time = 0;
656
+
657
+	/**
658
+	 * Outer match sort by
659
+	 *
660
+	 * @var string
661
+	 */
662
+	protected $outer_order_by = '';
663
+
664
+	/**
665
+	 * Outer offset
666
+	 *
667
+	 * @var int
668
+	 */
669
+	protected $outer_offset = 0;
670
+
671
+	/**
672
+	 * Outer limit
673
+	 *
674
+	 * @var int
675
+	 */
676
+	protected $outer_limit = 0;
677
+
678
+	/**
679
+	 * @var bool
680
+	 */
681
+	protected $has_outer = false;
682
+
683
+	/**
684
+	 * Last error message
685
+	 *
686
+	 * @var string
687
+	 */
688
+	protected $error = '';
689
+
690
+	/**
691
+	 * Last warning message
692
+	 *
693
+	 * @var string
694
+	 */
695
+	protected $warning = '';
696
+
697
+	/**
698
+	 * Connection error vs remote error flag
699
+	 *
700
+	 * @var bool
701
+	 */
702
+	protected $conn_error = false;
703
+
704
+	/**
705
+	 * Requests array for multi-query
706
+	 *
707
+	 * @var array
708
+	 */
709
+	protected $reqs = array();
710
+
711
+	/**
712
+	 * Stored mbstring encoding
713
+	 *
714
+	 * @var string
715
+	 */
716
+	protected $mbenc = '';
717
+
718
+	/**
719
+	 * Whether $result['matches'] should be a hash or an array
720
+	 *
721
+	 * @var bool
722
+	 */
723
+	protected $array_result = false;
724
+
725
+	/**
726
+	 * Connect timeout
727
+	 *
728
+	 * @var int
729
+	 */
730
+	protected $timeout = 0;
731
+
732
+	/**
733
+	 * @var bool
734
+	 */
735
+	protected $path = false;
736
+
737
+	/**
738
+	 * @var resource|bool
739
+	 */
740
+	protected $socket = false;
741
+
742
+	/////////////////////////////////////////////////////////////////////////////
743
+	// common stuff
744
+	/////////////////////////////////////////////////////////////////////////////
745
+
746
+	public function __construct()
747
+	{
748
+		// default idf=tfidf_normalized
749
+		$this->query_flags = sphSetBit(0, 6, true);
750
+	}
751
+
752
+	public function __destruct()
753
+	{
754
+		if ($this->socket !== false) {
755
+			fclose($this->socket);
756
+		}
757
+	}
758
+
759
+	/**
760
+	 * @return string
761
+	 */
762
+	public function getLastError()
763
+	{
764
+		return $this->error;
765
+	}
766
+
767
+	/**
768
+	 * @return string
769
+	 */
770
+	public function getLastWarning()
771
+	{
772
+		return $this->warning;
773
+	}
774
+
775
+	/**
776
+	 * Get last error flag (to tell network connection errors from searchd errors or broken responses)
777
+	 *
778
+	 * @return bool
779
+	 */
780
+	public function isConnectError()
781
+	{
782
+		return $this->conn_error;
783
+	}
784
+
785
+	/**
786
+	 * Set searchd host name and port
787
+	 *
788
+	 * @param string $host
789
+	 * @param int $port
790
+	 */
791
+	public function setServer($host, $port = 0)
792
+	{
793
+		assert(is_string($host));
794
+		if ($host[0] == '/') {
795
+			$this->path = 'unix://' . $host;
796
+			return;
797
+		}
798
+		if (substr($host, 0, 7) == 'unix://') {
799
+			$this->path = $host;
800
+			return;
801
+		}
802
+
803
+		$this->host = $host;
804
+		$port = intval($port);
805
+		assert(0 <= $port && $port < 65536);
806
+		$this->port = $port == 0 ? 9312 : $port;
807
+		$this->path = '';
808
+	}
809
+
810
+	/**
811
+	 * Set server connection timeout (0 to remove)
812
+	 *
813
+	 * @param int $timeout
814
+	 */
815
+	public function setConnectTimeout($timeout)
816
+	{
817
+		assert(is_numeric($timeout));
818
+		$this->timeout = $timeout;
819
+	}
820
+
821
+	/**
822
+	 * @param resource $handle
823
+	 * @param string $data
824
+	 * @param int $length
825
+	 *
826
+	 * @return bool
827
+	 */
828
+	protected function send($handle, $data, $length)
829
+	{
830
+		if (feof($handle) || fwrite($handle, $data, $length) !== $length) {
831
+			$this->error = 'connection unexpectedly closed (timed out?)';
832
+			$this->conn_error = true;
833
+			return false;
834
+		}
835
+		return true;
836
+	}
837
+
838
+	/////////////////////////////////////////////////////////////////////////////
839
+
840
+	/**
841
+	 * Enter mbstring workaround mode
842
+	 */
843
+	protected function mbPush()
844
+	{
845
+		$this->mbenc = '';
846
+		if (ini_get('mbstring.func_overload') & 2) {
847
+			$this->mbenc = mb_internal_encoding();
848
+			mb_internal_encoding('latin1');
849
+		}
850
+	}
851
+
852
+	/**
853
+	 * Leave mbstring workaround mode
854
+	 */
855
+	protected function mbPop()
856
+	{
857
+		if ($this->mbenc) {
858
+			mb_internal_encoding($this->mbenc);
859
+		}
860
+	}
861
+
862
+	/**
863
+	 * Connect to searchd server
864
+	 *
865
+	 * @return bool|resource
866
+	 */
867
+	protected function connect()
868
+	{
869
+		if ($this->socket !== false) {
870
+			// we are in persistent connection mode, so we have a socket
871
+			// however, need to check whether it's still alive
872
+			if (!@feof($this->socket)) {
873
+				return $this->socket;
874
+			}
875
+
876
+			// force reopen
877
+			$this->socket = false;
878
+		}
879
+
880
+		$errno = 0;
881
+		$errstr = '';
882
+		$this->conn_error = false;
883
+
884
+		if ($this->path) {
885
+			$host = $this->path;
886
+			$port = 0;
887
+		} else {
888
+			$host = $this->host;
889
+			$port = $this->port;
890
+		}
891
+
892
+		if ($this->timeout <= 0) {
893
+			$fp = @fsockopen($host, $port, $errno, $errstr);
894
+		} else {
895
+			$fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout);
896
+		}
897
+
898
+		if (!$fp) {
899
+			if ($this->path) {
900
+				$location = $this->path;
901
+			} else {
902
+				$location = "{$this->host}:{$this->port}";
903
+			}
904
+
905
+			$errstr = trim($errstr);
906
+			$this->error = "connection to $location failed (errno=$errno, msg=$errstr)";
907
+			$this->conn_error = true;
908
+			return false;
909
+		}
910
+
911
+		// send my version
912
+		// this is a subtle part. we must do it before (!) reading back from searchd.
913
+		// because otherwise under some conditions (reported on FreeBSD for instance)
914
+		// TCP stack could throttle write-write-read pattern because of Nagle.
915
+		if (!$this->send($fp, pack('N', 1), 4)) {
916
+			fclose($fp);
917
+			$this->error = 'failed to send client protocol version';
918
+			return false;
919
+		}
920
+
921
+		// check version
922
+		list(, $v) = unpack('N*', fread($fp, 4));
923
+		$v = (int)$v;
924
+		if ($v < 1) {
925
+			fclose($fp);
926
+			$this->error = "expected searchd protocol version 1+, got version '$v'";
927
+			return false;
928
+		}
929
+
930
+		return $fp;
931
+	}
932
+
933
+	/**
934
+	 * Get and check response packet from searchd server
935
+	 *
936
+	 * @param resource $fp
937
+	 * @param int $client_ver
938
+	 *
939
+	 * @return bool|string
940
+	 */
941
+	protected function getResponse($fp, $client_ver)
942
+	{
943
+		$response = '';
944
+		$len = 0;
945
+
946
+		$header = fread($fp, 8);
947
+		if (strlen($header) == 8) {
948
+			list($status, $ver, $len) = array_values(unpack('n2a/Nb', $header));
949
+			$left = $len;
950
+			while ($left > 0 && !feof($fp)) {
951
+				$chunk = fread($fp, min(8192, $left));
952
+				if ($chunk) {
953
+					$response .= $chunk;
954
+					$left -= strlen($chunk);
955
+				}
956
+			}
957
+		}
958
+
959
+		if ($this->socket === false) {
960
+			fclose($fp);
961
+		}
962
+
963
+		// check response
964
+		$read = strlen($response);
965
+		if (!$response || $read != $len) {
966
+			$this->error = $len
967
+				? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
968
+				: 'received zero-sized searchd response';
969
+			return false;
970
+		}
971
+
972
+		// check status
973
+		if ($status == SEARCHD_WARNING) {
974
+			list(, $wlen) = unpack('N*', substr($response, 0, 4));
975
+			$this->warning = substr($response, 4, $wlen);
976
+			return substr($response, 4 + $wlen);
977
+		}
978
+		if ($status == SEARCHD_ERROR) {
979
+			$this->error = 'searchd error: ' . substr($response, 4);
980
+			return false;
981
+		}
982
+		if ($status == SEARCHD_RETRY) {
983
+			$this->error = 'temporary searchd error: ' . substr($response, 4);
984
+			return false;
985
+		}
986
+		if ($status != SEARCHD_OK) {
987
+			$this->error = "unknown status code '$status'";
988
+			return false;
989
+		}
990
+
991
+		// check version
992
+		if ($ver < $client_ver) {
993
+			$this->warning = sprintf(
994
+				'searchd command v.%d.%d older than client\'s v.%d.%d, some options might not work',
995
+				$ver >> 8,
996
+				$ver & 0xff,
997
+				$client_ver >> 8,
998
+				$client_ver & 0xff
999
+			);
1000
+		}
1001
+
1002
+		return $response;
1003
+	}
1004
+
1005
+	/////////////////////////////////////////////////////////////////////////////
1006
+	// searching
1007
+	/////////////////////////////////////////////////////////////////////////////
1008
+
1009
+	/**
1010
+	 * Set offset and count into result set, and optionally set max-matches and cutoff limits
1011
+	 *
1012
+	 * @param int $offset
1013
+	 * @param int $limit
1014
+	 * @param int $max
1015
+	 * @param int $cutoff
1016
+	 */
1017
+	public function setLimits($offset, $limit, $max = 0, $cutoff = 0)
1018
+	{
1019
+		assert(is_int($offset));
1020
+		assert(is_int($limit));
1021
+		assert($offset >= 0);
1022
+		assert($limit > 0);
1023
+		assert($max >= 0);
1024
+		$this->offset = $offset;
1025
+		$this->limit = $limit;
1026
+		if ($max > 0) {
1027
+			$this->max_matches = $max;
1028
+		}
1029
+		if ($cutoff > 0) {
1030
+			$this->cutoff = $cutoff;
1031
+		}
1032
+	}
1033
+
1034
+	/**
1035
+	 * Set maximum query time, in milliseconds, per-index, 0 means 'do not limit'
1036
+	 *
1037
+	 * @param int $max
1038
+	 */
1039
+	public function setMaxQueryTime($max)
1040
+	{
1041
+		assert(is_int($max));
1042
+		assert($max >= 0);
1043
+		$this->max_query_time = $max;
1044
+	}
1045
+
1046
+	/**
1047
+	 * Set matching mode
1048
+	 *
1049
+	 * @param int $mode
1050
+	 */
1051
+	public function setMatchMode($mode)
1052
+	{
1053
+		trigger_error(
1054
+			'DEPRECATED: Do not call this method or, even better, use SphinxQL instead of an API',
1055
+			E_USER_DEPRECATED
1056
+		);
1057
+		assert(in_array($mode, array(
1058
+			SPH_MATCH_ALL,
1059
+			SPH_MATCH_ANY,
1060
+			SPH_MATCH_PHRASE,
1061
+			SPH_MATCH_BOOLEAN,
1062
+			SPH_MATCH_EXTENDED,
1063
+			SPH_MATCH_FULLSCAN,
1064
+			SPH_MATCH_EXTENDED2
1065
+		)));
1066
+		$this->mode = $mode;
1067
+	}
1068
+
1069
+	/**
1070
+	 * Set ranking mode
1071
+	 *
1072
+	 * @param int $ranker
1073
+	 * @param string $rank_expr
1074
+	 */
1075
+	public function setRankingMode($ranker, $rank_expr='')
1076
+	{
1077
+		assert($ranker === 0 || $ranker >= 1 && $ranker < SPH_RANK_TOTAL);
1078
+		assert(is_string($rank_expr));
1079
+		$this->ranker = $ranker;
1080
+		$this->rank_expr = $rank_expr;
1081
+	}
1082
+
1083
+	/**
1084
+	 * Set matches sorting mode
1085
+	 *
1086
+	 * @param int $mode
1087
+	 * @param string $sort_by
1088
+	 */
1089
+	public function setSortMode($mode, $sort_by = '')
1090
+	{
1091
+		assert(in_array($mode, array(
1092
+			SPH_SORT_RELEVANCE,
1093
+			SPH_SORT_ATTR_DESC,
1094
+			SPH_SORT_ATTR_ASC,
1095
+			SPH_SORT_TIME_SEGMENTS,
1096
+			SPH_SORT_EXTENDED,
1097
+			SPH_SORT_EXPR
1098
+		)));
1099
+		assert(is_string($sort_by));
1100
+		assert($mode == SPH_SORT_RELEVANCE || strlen($sort_by) > 0);
1101
+
1102
+		$this->sort = $mode;
1103
+		$this->sort_by = $sort_by;
1104
+	}
1105
+
1106
+	/**
1107
+	 * Bind per-field weights by order
1108
+	 *
1109
+	 * @deprecated use setFieldWeights() instead
1110
+	 */
1111
+	public function setWeights()
1112
+	{
1113
+		throw new \RuntimeException('This method is now deprecated; please use setFieldWeights instead');
1114
+	}
1115
+
1116
+	/**
1117
+	 * Bind per-field weights by name
1118
+	 *
1119
+	 * @param array $weights
1120
+	 */
1121
+	public function setFieldWeights(array $weights)
1122
+	{
1123
+		foreach ($weights as $name => $weight) {
1124
+			assert(is_string($name));
1125
+			assert(is_int($weight));
1126
+		}
1127
+		$this->field_weights = $weights;
1128
+	}
1129
+
1130
+	/**
1131
+	 * Bind per-index weights by name
1132
+	 *
1133
+	 * @param array $weights
1134
+	 */
1135
+	public function setIndexWeights(array $weights)
1136
+	{
1137
+		foreach ($weights as $index => $weight) {
1138
+			assert(is_string($index));
1139
+			assert(is_int($weight));
1140
+		}
1141
+		$this->index_weights = $weights;
1142
+	}
1143
+
1144
+	/**
1145
+	 * Set IDs range to match. Only match records if document ID is beetwen $min and $max (inclusive)
1146
+	 *
1147
+	 * @param int $min
1148
+	 * @param int $max
1149
+	 */
1150
+	public function setIDRange($min, $max)
1151
+	{
1152
+		assert(is_numeric($min));
1153
+		assert(is_numeric($max));
1154
+		assert($min <= $max);
1155
+
1156
+		$this->min_id = $min;
1157
+		$this->max_id = $max;
1158
+	}
1159
+
1160
+	/**
1161
+	 * Set values set filter. Only match records where $attribute value is in given set
1162
+	 *
1163
+	 * @param string $attribute
1164
+	 * @param array $values
1165
+	 * @param bool $exclude
1166
+	 */
1167
+	public function setFilter($attribute, array $values, $exclude = false)
1168
+	{
1169
+		assert(is_string($attribute));
1170
+		assert(count($values));
1171
+
1172
+		foreach ($values as $value) {
1173
+			assert(is_numeric($value));
1174
+		}
1175
+
1176
+		$this->filters[] = array(
1177
+			'type' => SPH_FILTER_VALUES,
1178
+			'attr' => $attribute,
1179
+			'exclude' => $exclude,
1180
+			'values' => $values
1181
+		);
1182
+	}
1183
+
1184
+	/**
1185
+	 * Set string filter
1186
+	 * Only match records where $attribute value is equal
1187
+	 *
1188
+	 * @param string $attribute
1189
+	 * @param string $value
1190
+	 * @param bool $exclude
1191
+	 */
1192
+	public function setFilterString($attribute, $value, $exclude = false)
1193
+	{
1194
+		assert(is_string($attribute));
1195
+		assert(is_string($value));
1196
+		$this->filters[] = array(
1197
+			'type' => SPH_FILTER_STRING,
1198
+			'attr' => $attribute,
1199
+			'exclude' => $exclude,
1200
+			'value' => $value
1201
+		);
1202
+	}    
1203
+
1204
+	/**
1205
+	 * Set range filter
1206
+	 * Only match records if $attribute value is beetwen $min and $max (inclusive)
1207
+	 *
1208
+	 * @param string $attribute
1209
+	 * @param int $min
1210
+	 * @param int $max
1211
+	 * @param bool $exclude
1212
+	 */
1213
+	public function setFilterRange($attribute, $min, $max, $exclude = false)
1214
+	{
1215
+		assert(is_string($attribute));
1216
+		assert(is_numeric($min));
1217
+		assert(is_numeric($max));
1218
+		assert($min <= $max);
1219
+
1220
+		$this->filters[] = array(
1221
+			'type' => SPH_FILTER_RANGE,
1222
+			'attr' => $attribute,
1223
+			'exclude' => $exclude,
1224
+			'min' => $min,
1225
+			'max' => $max
1226
+		);
1227
+	}
1228
+
1229
+	/**
1230
+	 * Set float range filter
1231
+	 * Only match records if $attribute value is beetwen $min and $max (inclusive)
1232
+	 *
1233
+	 * @param string $attribute
1234
+	 * @param int $min
1235
+	 * @param int $max
1236
+	 * @param bool $exclude
1237
+	 */
1238
+	public function setFilterFloatRange($attribute, $min, $max, $exclude = false)
1239
+	{
1240
+		assert(is_string($attribute));
1241
+		assert(is_float($min));
1242
+		assert(is_float($max));
1243
+		assert($min <= $max);
1244
+
1245
+		$this->filters[] = array(
1246
+			'type' => SPH_FILTER_FLOATRANGE,
1247
+			'attr' => $attribute,
1248
+			'exclude' => $exclude,
1249
+			'min' => $min,
1250
+			'max' => $max
1251
+		);
1252
+	}
1253
+
1254
+	/**
1255
+	 * Setup anchor point for geosphere distance calculations
1256
+	 * Required to use @geodist in filters and sorting
1257
+	 * Latitude and longitude must be in radians
1258
+	 *
1259
+	 * @param string $attr_lat
1260
+	 * @param string $attr_long
1261
+	 * @param float $lat
1262
+	 * @param float $long
1263
+	 */
1264
+	public function setGeoAnchor($attr_lat, $attr_long, $lat, $long)
1265
+	{
1266
+		assert(is_string($attr_lat));
1267
+		assert(is_string($attr_long));
1268
+		assert(is_float($lat));
1269
+		assert(is_float($long));
1270
+
1271
+		$this->anchor = array(
1272
+			'attrlat' => $attr_lat,
1273
+			'attrlong' => $attr_long,
1274
+			'lat' => $lat,
1275
+			'long' => $long
1276
+		);
1277
+	}
1278
+
1279
+	/**
1280
+	 * Set grouping attribute and function
1281
+	 *
1282
+	 * @param string $attribute
1283
+	 * @param string $func
1284
+	 * @param string $group_sort
1285
+	 */
1286
+	public function setGroupBy($attribute, $func, $group_sort = '@group desc')
1287
+	{
1288
+		assert(is_string($attribute));
1289
+		assert(is_string($group_sort));
1290
+		assert(in_array($func, array(
1291
+			SPH_GROUPBY_DAY,
1292
+			SPH_GROUPBY_WEEK,
1293
+			SPH_GROUPBY_MONTH,
1294
+			SPH_GROUPBY_YEAR,
1295
+			SPH_GROUPBY_ATTR,
1296
+			SPH_GROUPBY_ATTRPAIR
1297
+		)));
1298
+
1299
+		$this->group_by = $attribute;
1300
+		$this->group_func = $func;
1301
+		$this->group_sort = $group_sort;
1302
+	}
1303
+
1304
+	/**
1305
+	 * Set count-distinct attribute for group-by queries
1306
+	 *
1307
+	 * @param string $attribute
1308
+	 */
1309
+	public function setGroupDistinct($attribute)
1310
+	{
1311
+		assert(is_string($attribute));
1312
+		$this->group_distinct = $attribute;
1313
+	}
1314
+
1315
+	/**
1316
+	 * Set distributed retries count and delay
1317
+	 *
1318
+	 * @param int $count
1319
+	 * @param int $delay
1320
+	 */
1321
+	public function setRetries($count, $delay = 0)
1322
+	{
1323
+		assert(is_int($count) && $count >= 0);
1324
+		assert(is_int($delay) && $delay >= 0);
1325
+		$this->retry_count = $count;
1326
+		$this->retry_delay = $delay;
1327
+	}
1328
+
1329
+	/**
1330
+	 * Set result set format (hash or array; hash by default)
1331
+	 * PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
1332
+	 *
1333
+	 * @param bool $array_result
1334
+	 */
1335
+	public function setArrayResult($array_result)
1336
+	{
1337
+		assert(is_bool($array_result));
1338
+		$this->array_result = $array_result;
1339
+	}
1340
+
1341
+	/**
1342
+	 * Set attribute values override
1343
+	 * There can be only one override per attribute
1344
+	 * $values must be a hash that maps document IDs to attribute values
1345
+	 *
1346
+	 * @deprecated Do not call this method. Use SphinxQL REMAP() function instead.
1347
+	 *
1348
+	 * @param string $attr_name
1349
+	 * @param string $attr_type
1350
+	 * @param array $values
1351
+	 */
1352
+	public function setOverride($attr_name, $attr_type, array $values)
1353
+	{
1354
+		trigger_error(
1355
+			'DEPRECATED: Do not call this method. Use SphinxQL REMAP() function instead.',
1356
+			E_USER_DEPRECATED
1357
+		);
1358
+		assert(is_string($attr_name));
1359
+		assert(in_array($attr_type, array(
1360
+			SPH_ATTR_INTEGER,
1361
+			SPH_ATTR_TIMESTAMP,
1362
+			SPH_ATTR_BOOL,
1363
+			SPH_ATTR_FLOAT,
1364
+			SPH_ATTR_BIGINT
1365
+		)));
1366
+
1367
+		$this->overrides[$attr_name] = array(
1368
+			'attr' => $attr_name,
1369
+			'type' => $attr_type,
1370
+			'values' => $values
1371
+		);
1372
+	}
1373
+
1374
+	/**
1375
+	 * Set select-list (attributes or expressions), SQL-like syntax
1376
+	 *
1377
+	 * @param string $select
1378
+	 */
1379
+	public function setSelect($select)
1380
+	{
1381
+		assert(is_string($select));
1382
+		$this->select = $select;
1383
+	}
1384
+
1385
+	/**
1386
+	 * @param string $flag_name
1387
+	 * @param string|int $flag_value
1388
+	 */
1389
+	public function setQueryFlag($flag_name, $flag_value)
1390
+	{
1391
+		$known_names = array(
1392
+			'reverse_scan',
1393
+			'sort_method',
1394
+			'max_predicted_time',
1395
+			'boolean_simplify',
1396
+			'idf',
1397
+			'global_idf',
1398
+			'low_priority'
1399
+		);
1400
+		$flags = array (
1401
+			'reverse_scan' => array(0, 1),
1402
+			'sort_method' => array('pq', 'kbuffer'),
1403
+			'max_predicted_time' => array(0),
1404
+			'boolean_simplify' => array(true, false),
1405
+			'idf' => array ('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
1406
+			'global_idf' => array(true, false),
1407
+			'low_priority' => array(true, false)
1408
+		);
1409
+
1410
+		assert(isset($flag_name, $known_names));
1411
+		assert(
1412
+			in_array($flag_value, $flags[$flag_name], true) ||
1413
+			($flag_name == 'max_predicted_time' && is_int($flag_value) && $flag_value >= 0)
1414
+		);
1415
+
1416
+		switch ($flag_name) {
1417
+			case 'reverse_scan':
1418
+				$this->query_flags = sphSetBit($this->query_flags, 0, $flag_value == 1);
1419
+				break;
1420
+			case 'sort_method':
1421
+				$this->query_flags = sphSetBit($this->query_flags, 1, $flag_value == 'kbuffer');
1422
+				break;
1423
+			case 'max_predicted_time':
1424
+				$this->query_flags = sphSetBit($this->query_flags, 2, $flag_value > 0);
1425
+				$this->predicted_time = (int)$flag_value;
1426
+				break;
1427
+			case 'boolean_simplify':
1428
+				$this->query_flags = sphSetBit($this->query_flags, 3, $flag_value);
1429
+				break;
1430
+			case 'idf':
1431
+				if ($flag_value == 'normalized' || $flag_value == 'plain') {
1432
+					$this->query_flags = sphSetBit($this->query_flags, 4, $flag_value == 'plain');
1433
+				}
1434
+				if ($flag_value == 'tfidf_normalized' || $flag_value == 'tfidf_unnormalized') {
1435
+					$this->query_flags = sphSetBit($this->query_flags, 6, $flag_value == 'tfidf_normalized');
1436
+				}
1437
+				break;
1438
+			case 'global_idf':
1439
+				$this->query_flags = sphSetBit($this->query_flags, 5, $flag_value);
1440
+				break;
1441
+			case 'low_priority':
1442
+				$this->query_flags = sphSetBit($this->query_flags, 8, $flag_value);
1443
+				break;
1444
+		}
1445
+	}
1446
+
1447
+	/**
1448
+	 * Set outer order by parameters
1449
+	 *
1450
+	 * @param string $order_by
1451
+	 * @param int $offset
1452
+	 * @param int $limit
1453
+	 */
1454
+	public function setOuterSelect($order_by, $offset, $limit)
1455
+	{
1456
+		assert(is_string($order_by));
1457
+		assert(is_int($offset));
1458
+		assert(is_int($limit));
1459
+		assert($offset >= 0);
1460
+		assert($limit > 0);
1461
+
1462
+		$this->outer_order_by = $order_by;
1463
+		$this->outer_offset = $offset;
1464
+		$this->outer_limit = $limit;
1465
+		$this->has_outer = true;
1466
+	}
1467
+
1468
+
1469
+	//////////////////////////////////////////////////////////////////////////////
1470
+
1471
+	/**
1472
+	 * Clear all filters (for multi-queries)
1473
+	 */
1474
+	public function resetFilters()
1475
+	{
1476
+		$this->filters = array();
1477
+		$this->anchor = array();
1478
+	}
1479
+
1480
+	/**
1481
+	 * Clear groupby settings (for multi-queries)
1482
+	 */
1483
+	public function resetGroupBy()
1484
+	{
1485
+		$this->group_by = '';
1486
+		$this->group_func = SPH_GROUPBY_DAY;
1487
+		$this->group_sort = '@group desc';
1488
+		$this->group_distinct = '';
1489
+	}
1490
+
1491
+	/**
1492
+	 * Clear all attribute value overrides (for multi-queries)
1493
+	 */
1494
+	public function resetOverrides()
1495
+	{
1496
+		$this->overrides = array();
1497
+	}
1498
+
1499
+	public function resetQueryFlag()
1500
+	{
1501
+		$this->query_flags = sphSetBit(0, 6, true); // default idf=tfidf_normalized
1502
+		$this->predicted_time = 0;
1503
+	}
1504
+
1505
+	public function resetOuterSelect()
1506
+	{
1507
+		$this->outer_order_by = '';
1508
+		$this->outer_offset = 0;
1509
+		$this->outer_limit = 0;
1510
+		$this->has_outer = false;
1511
+	}
1512
+
1513
+	//////////////////////////////////////////////////////////////////////////////
1514
+
1515
+	/**
1516
+	 * Connect to searchd server, run given search query through given indexes, and return the search results
1517
+	 *
1518
+	 * @param string  $query
1519
+	 * @param string $index
1520
+	 * @param string $comment
1521
+	 *
1522
+	 * @return bool
1523
+	 */
1524
+	public function query($query, $index = '*', $comment = '')
1525
+	{
1526
+		assert(empty($this->reqs));
1527
+
1528
+		$this->addQuery($query, $index, $comment);
1529
+		$results = $this->runQueries();
1530
+		$this->reqs = array(); // just in case it failed too early
1531
+
1532
+		if (!is_array($results)) {
1533
+			return false; // probably network error; error message should be already filled
1534
+		}
1535
+
1536
+		$this->error = $results[0]['error'];
1537
+		$this->warning = $results[0]['warning'];
1538
+
1539
+		if ($results[0]['status'] == SEARCHD_ERROR) {
1540
+			return false;
1541
+		} else {
1542
+			return $results[0];
1543
+		}
1544
+	}
1545
+
1546
+	/**
1547
+	 * Helper to pack floats in network byte order
1548
+	 *
1549
+	 * @param float $float
1550
+	 *
1551
+	 * @return string
1552
+	 */
1553
+	protected function packFloat($float)
1554
+	{
1555
+		$t1 = pack('f', $float); // machine order
1556
+		list(, $t2) = unpack('L*', $t1); // int in machine order
1557
+		return pack('N', $t2);
1558
+	}
1559
+
1560
+	/**
1561
+	 * Add query to multi-query batch
1562
+	 * Returns index into results array from RunQueries() call
1563
+	 *
1564
+	 * @param string $query
1565
+	 * @param string $index
1566
+	 * @param string $comment
1567
+	 *
1568
+	 * @return int
1569
+	 */
1570
+	public function addQuery($query, $index = '*', $comment = '')
1571
+	{
1572
+		// mbstring workaround
1573
+		$this->mbPush();
1574
+
1575
+		// build request
1576
+		$req = pack('NNNNN', $this->query_flags, $this->offset, $this->limit, $this->mode, $this->ranker);
1577
+		if ($this->ranker == SPH_RANK_EXPR) {
1578
+			$req .= pack('N', strlen($this->rank_expr)) . $this->rank_expr;
1579
+		}
1580
+		$req .= pack('N', $this->sort); // (deprecated) sort mode
1581
+		$req .= pack('N', strlen($this->sort_by)) . $this->sort_by;
1582
+		$req .= pack('N', strlen($query)) . $query; // query itself
1583
+		$req .= pack('N', count($this->weights)); // weights
1584
+		foreach ($this->weights as $weight) {
1585
+			$req .= pack('N', (int)$weight);
1586
+		}
1587
+		$req .= pack('N', strlen($index)) . $index; // indexes
1588
+		$req .= pack('N', 1); // id64 range marker
1589
+		$req .= sphPackU64($this->min_id) . sphPackU64($this->max_id); // id64 range
1590
+
1591
+		// filters
1592
+		$req .= pack('N', count($this->filters));
1593
+		foreach ($this->filters as $filter) {
1594
+			$req .= pack('N', strlen($filter['attr'])) . $filter['attr'];
1595
+			$req .= pack('N', $filter['type']);
1596
+			switch ($filter['type']) {
1597
+				case SPH_FILTER_VALUES:
1598
+					$req .= pack('N', count($filter['values']));
1599
+					foreach ($filter['values'] as $value) {
1600
+						$req .= sphPackI64($value);
1601
+					}
1602
+					break;
1603
+				case SPH_FILTER_RANGE:
1604
+					$req .= sphPackI64($filter['min']) . sphPackI64($filter['max']);
1605
+					break;
1606
+				case SPH_FILTER_FLOATRANGE:
1607
+					$req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']);
1608
+					break;
1609
+				case SPH_FILTER_STRING:
1610
+					$req .= pack('N', strlen($filter['value'])) . $filter['value'];
1611
+					break;
1612
+				default:
1613
+					assert(0 && 'internal error: unhandled filter type');
1614
+			}
1615
+			$req .= pack('N', $filter['exclude']);
1616
+		}
1617
+
1618
+		// group-by clause, max-matches count, group-sort clause, cutoff count
1619
+		$req .= pack('NN', $this->group_func, strlen($this->group_by)) . $this->group_by;
1620
+		$req .= pack('N', $this->max_matches);
1621
+		$req .= pack('N', strlen($this->group_sort)) . $this->group_sort;
1622
+		$req .= pack('NNN', $this->cutoff, $this->retry_count, $this->retry_delay);
1623
+		$req .= pack('N', strlen($this->group_distinct)) . $this->group_distinct;
1624
+
1625
+		// anchor point
1626
+		if (empty($this->anchor)) {
1627
+			$req .= pack('N', 0);
1628
+		} else {
1629
+			$a =& $this->anchor;
1630
+			$req .= pack('N', 1);
1631
+			$req .= pack('N', strlen($a['attrlat'])) . $a['attrlat'];
1632
+			$req .= pack('N', strlen($a['attrlong'])) . $a['attrlong'];
1633
+			$req .= $this->packFloat($a['lat']) . $this->packFloat($a['long']);
1634
+		}
1635
+
1636
+		// per-index weights
1637
+		$req .= pack('N', count($this->index_weights));
1638
+		foreach ($this->index_weights as $idx => $weight) {
1639
+			$req .= pack('N', strlen($idx)) . $idx . pack('N', $weight);
1640
+		}
1641
+
1642
+		// max query time
1643
+		$req .= pack('N', $this->max_query_time);
1644
+
1645
+		// per-field weights
1646
+		$req .= pack('N', count($this->field_weights));
1647
+		foreach ($this->field_weights as $field => $weight) {
1648
+			$req .= pack('N', strlen($field)) . $field . pack('N', $weight);
1649
+		}
1650
+
1651
+		// comment
1652
+		$req .= pack('N', strlen($comment)) . $comment;
1653
+
1654
+		// attribute overrides
1655
+		$req .= pack('N', count($this->overrides));
1656
+		foreach ($this->overrides as $key => $entry) {
1657
+			$req .= pack('N', strlen($entry['attr'])) . $entry['attr'];
1658
+			$req .= pack('NN', $entry['type'], count($entry['values']));
1659
+			foreach ($entry['values'] as $id => $val) {
1660
+				assert(is_numeric($id));
1661
+				assert(is_numeric($val));
1662
+
1663
+				$req .= sphPackU64($id);
1664
+				switch ($entry['type']) {
1665
+					case SPH_ATTR_FLOAT:
1666
+						$req .= $this->packFloat($val);
1667
+						break;
1668
+					case SPH_ATTR_BIGINT:
1669
+						$req .= sphPackI64($val);
1670
+						break;
1671
+					default:
1672
+						$req .= pack('N', $val);
1673
+						break;
1674
+				}
1675
+			}
1676
+		}
1677
+
1678
+		// select-list
1679
+		$req .= pack('N', strlen($this->select)) . $this->select;
1680
+
1681
+		// max_predicted_time
1682
+		if ($this->predicted_time > 0) {
1683
+			$req .= pack('N', (int)$this->predicted_time);
1684
+		}
1685
+
1686
+		$req .= pack('N', strlen($this->outer_order_by)) . $this->outer_order_by;
1687
+		$req .= pack('NN', $this->outer_offset, $this->outer_limit);
1688
+		if ($this->has_outer) {
1689
+			$req .= pack('N', 1);
1690
+		} else {
1691
+			$req .= pack('N', 0);
1692
+		}
1693
+
1694
+		// mbstring workaround
1695
+		$this->mbPop();
1696
+
1697
+		// store request to requests array
1698
+		$this->reqs[] = $req;
1699
+		return count($this->reqs) - 1;
1700
+	}
1701
+
1702
+	/**
1703
+	 * Connect to searchd, run queries batch, and return an array of result sets
1704
+	 *
1705
+	 * @return array|bool
1706
+	 */
1707
+	public function runQueries()
1708
+	{
1709
+		if (empty($this->reqs)) {
1710
+			$this->error = 'no queries defined, issue AddQuery() first';
1711
+			return false;
1712
+		}
1713
+
1714
+		// mbstring workaround
1715
+		$this->mbPush();
1716
+
1717
+		if (!($fp = $this->connect())) {
1718
+			$this->mbPop();
1719
+			return false;
1720
+		}
1721
+
1722
+		// send query, get response
1723
+		$nreqs = count($this->reqs);
1724
+		$req = join('', $this->reqs);
1725
+		$len = 8 + strlen($req);
1726
+		$req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs) . $req; // add header
1727
+
1728
+		if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_SEARCH))) {
1729
+			$this->mbPop();
1730
+			return false;
1731
+		}
1732
+
1733
+		// query sent ok; we can reset reqs now
1734
+		$this->reqs = array();
1735
+
1736
+		// parse and return response
1737
+		return $this->parseSearchResponse($response, $nreqs);
1738
+	}
1739
+
1740
+	/**
1741
+	 * Parse and return search query (or queries) response
1742
+	 *
1743
+	 * @param string $response
1744
+	 * @param int $nreqs
1745
+	 *
1746
+	 * @return array
1747
+	 */
1748
+	protected function parseSearchResponse($response, $nreqs)
1749
+	{
1750
+		$p = 0; // current position
1751
+		$max = strlen($response); // max position for checks, to protect against broken responses
1752
+
1753
+		$results = array();
1754
+		for ($ires = 0; $ires < $nreqs && $p < $max; $ires++) {
1755
+			$results[] = array();
1756
+			$result =& $results[$ires];
1757
+
1758
+			$result['error'] = '';
1759
+			$result['warning'] = '';
1760
+
1761
+			// extract status
1762
+			list(, $status) = unpack('N*', substr($response, $p, 4));
1763
+			$p += 4;
1764
+			$result['status'] = $status;
1765
+			if ($status != SEARCHD_OK) {
1766
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1767
+				$p += 4;
1768
+				$message = substr($response, $p, $len);
1769
+				$p += $len;
1770
+
1771
+				if ($status == SEARCHD_WARNING) {
1772
+					$result['warning'] = $message;
1773
+				} else {
1774
+					$result['error'] = $message;
1775
+					continue;
1776
+				}
1777
+			}
1778
+
1779
+			// read schema
1780
+			$fields = array();
1781
+			$attrs = array();
1782
+
1783
+			list(, $nfields) = unpack('N*', substr($response, $p, 4));
1784
+			$p += 4;
1785
+			while ($nfields --> 0 && $p < $max) {
1786
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1787
+				$p += 4;
1788
+				$fields[] = substr($response, $p, $len);
1789
+				$p += $len;
1790
+			}
1791
+			$result['fields'] = $fields;
1792
+
1793
+			list(, $nattrs) = unpack('N*', substr($response, $p, 4));
1794
+			$p += 4;
1795
+			while ($nattrs --> 0 && $p < $max) {
1796
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1797
+				$p += 4;
1798
+				$attr = substr($response, $p, $len);
1799
+				$p += $len;
1800
+				list(, $type) = unpack('N*', substr($response, $p, 4));
1801
+				$p += 4;
1802
+				$attrs[$attr] = $type;
1803
+			}
1804
+			$result['attrs'] = $attrs;
1805
+
1806
+			// read match count
1807
+			list(, $count) = unpack('N*', substr($response, $p, 4));
1808
+			$p += 4;
1809
+			list(, $id64) = unpack('N*', substr($response, $p, 4));
1810
+			$p += 4;
1811
+
1812
+			// read matches
1813
+			$idx = -1;
1814
+			while ($count --> 0 && $p < $max) {
1815
+				// index into result array
1816
+				$idx++;
1817
+
1818
+				// parse document id and weight
1819
+				if ($id64) {
1820
+					$doc = sphUnpackU64(substr($response, $p, 8));
1821
+					$p += 8;
1822
+					list(,$weight) = unpack('N*', substr($response, $p, 4));
1823
+					$p += 4;
1824
+				} else {
1825
+					list($doc, $weight) = array_values(unpack('N*N*', substr($response, $p, 8)));
1826
+					$p += 8;
1827
+					$doc = sphFixUint($doc);
1828
+				}
1829
+				$weight = sprintf('%u', $weight);
1830
+
1831
+				// create match entry
1832
+				if ($this->array_result) {
1833
+					$result['matches'][$idx] = array('id' => $doc, 'weight' => $weight);
1834
+				} else {
1835
+					$result['matches'][$doc]['weight'] = $weight;
1836
+				}
1837
+
1838
+				// parse and create attributes
1839
+				$attrvals = array();
1840
+				foreach ($attrs as $attr => $type) {
1841
+					// handle 64bit ints
1842
+					if ($type == SPH_ATTR_BIGINT) {
1843
+						$attrvals[$attr] = sphUnpackI64(substr($response, $p, 8));
1844
+						$p += 8;
1845
+						continue;
1846
+					}
1847
+
1848
+					// handle floats
1849
+					if ($type == SPH_ATTR_FLOAT) {
1850
+						list(, $uval) = unpack('N*', substr($response, $p, 4));
1851
+						$p += 4;
1852
+						list(, $fval) = unpack('f*', pack('L', $uval));
1853
+						$attrvals[$attr] = $fval;
1854
+						continue;
1855
+					}
1856
+
1857
+					// handle everything else as unsigned ints
1858
+					list(, $val) = unpack('N*', substr($response, $p, 4));
1859
+					$p += 4;
1860
+					if ($type == SPH_ATTR_MULTI) {
1861
+						$attrvals[$attr] = array();
1862
+						$nvalues = $val;
1863
+						while ($nvalues --> 0 && $p < $max) {
1864
+							list(, $val) = unpack('N*', substr($response, $p, 4));
1865
+							$p += 4;
1866
+							$attrvals[$attr][] = sphFixUint($val);
1867
+						}
1868
+					} elseif ($type == SPH_ATTR_MULTI64) {
1869
+						$attrvals[$attr] = array();
1870
+						$nvalues = $val;
1871
+						while ($nvalues > 0 && $p < $max) {
1872
+							$attrvals[$attr][] = sphUnpackI64(substr($response, $p, 8));
1873
+							$p += 8;
1874
+							$nvalues -= 2;
1875
+						}
1876
+					} elseif ($type == SPH_ATTR_STRING) {
1877
+						$attrvals[$attr] = substr($response, $p, $val);
1878
+						$p += $val;
1879
+					} elseif ($type == SPH_ATTR_FACTORS) {
1880
+						$attrvals[$attr] = substr($response, $p, $val - 4);
1881
+						$p += $val-4;
1882
+					} else {
1883
+						$attrvals[$attr] = sphFixUint($val);
1884
+					}
1885
+				}
1886
+
1887
+				if ($this->array_result) {
1888
+					$result['matches'][$idx]['attrs'] = $attrvals;
1889
+				} else {
1890
+					$result['matches'][$doc]['attrs'] = $attrvals;
1891
+				}
1892
+			}
1893
+
1894
+			list($total, $total_found, $msecs, $words) = array_values(unpack('N*N*N*N*', substr($response, $p, 16)));
1895
+			$result['total'] = sprintf('%u', $total);
1896
+			$result['total_found'] = sprintf('%u', $total_found);
1897
+			$result['time'] = sprintf('%.3f', $msecs / 1000);
1898
+			$p += 16;
1899
+
1900
+			while ($words --> 0 && $p < $max) {
1901
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1902
+				$p += 4;
1903
+				$word = substr($response, $p, $len);
1904
+				$p += $len;
1905
+				list($docs, $hits) = array_values(unpack('N*N*', substr($response, $p, 8)));
1906
+				$p += 8;
1907
+				$result['words'][$word] = array (
1908
+					'docs' => sprintf('%u', $docs),
1909
+					'hits' => sprintf('%u', $hits)
1910
+				);
1911
+			}
1912
+		}
1913
+
1914
+		$this->mbPop();
1915
+		return $results;
1916
+	}
1917
+
1918
+	/////////////////////////////////////////////////////////////////////////////
1919
+	// excerpts generation
1920
+	/////////////////////////////////////////////////////////////////////////////
1921
+
1922
+	/**
1923
+	 * Connect to searchd server, and generate exceprts (snippets) of given documents for given query.
1924
+	 * Returns false on failure, an array of snippets on success
1925
+	 *
1926
+	 * @param array $docs
1927
+	 * @param string $index
1928
+	 * @param string $words
1929
+	 * @param array $opts
1930
+	 *
1931
+	 * @return array|bool
1932
+	 */
1933
+	public function buildExcerpts(array $docs, $index, $words, array $opts = array())
1934
+	{
1935
+		assert(is_string($index));
1936
+		assert(is_string($words));
1937
+
1938
+		$this->mbPush();
1939
+
1940
+		if (!($fp = $this->connect())) {
1941
+			$this->mbPop();
1942
+			return false;
1943
+		}
1944
+
1945
+		/////////////////
1946
+		// fixup options
1947
+		/////////////////
1948
+
1949
+		$opts = array_merge(array(
1950
+			'before_match' => '<b>',
1951
+			'after_match' => '</b>',
1952
+			'chunk_separator' => ' ... ',
1953
+			'limit' => 256,
1954
+			'limit_passages' => 0,
1955
+			'limit_words' => 0,
1956
+			'around' => 5,
1957
+			'exact_phrase' => false,
1958
+			'single_passage' => false,
1959
+			'use_boundaries' => false,
1960
+			'weight_order' => false,
1961
+			'query_mode' => false,
1962
+			'force_all_words' => false,
1963
+			'start_passage_id' => 1,
1964
+			'load_files' => false,
1965
+			'html_strip_mode' => 'index',
1966
+			'allow_empty' => false,
1967
+			'passage_boundary' => 'none',
1968
+			'emit_zones' => false,
1969
+			'load_files_scattered' => false
1970
+		), $opts);
1971
+
1972
+		/////////////////
1973
+		// build request
1974
+		/////////////////
1975
+
1976
+		// v.1.2 req
1977
+		$flags = 1; // remove spaces
1978
+		if ($opts['exact_phrase']) {
1979
+			$flags |= 2;
1980
+		}
1981
+		if ($opts['single_passage']) {
1982
+			$flags |= 4;
1983
+		}
1984
+		if ($opts['use_boundaries']) {
1985
+			$flags |= 8;
1986
+		}
1987
+		if ($opts['weight_order']) {
1988
+			$flags |= 16;
1989
+		}
1990
+		if ($opts['query_mode']) {
1991
+			$flags |= 32;
1992
+		}
1993
+		if ($opts['force_all_words']) {
1994
+			$flags |= 64;
1995
+		}
1996
+		if ($opts['load_files']) {
1997
+			$flags |= 128;
1998
+		}
1999
+		if ($opts['allow_empty']) {
2000
+			$flags |= 256;
2001
+		}
2002
+		if ($opts['emit_zones']) {
2003
+			$flags |= 512;
2004
+		}
2005
+		if ($opts['load_files_scattered']) {
2006
+			$flags |= 1024;
2007
+		}
2008
+		$req = pack('NN', 0, $flags); // mode=0, flags=$flags
2009
+		$req .= pack('N', strlen($index)) . $index; // req index
2010
+		$req .= pack('N', strlen($words)) . $words; // req words
2011
+
2012
+		// options
2013
+		$req .= pack('N', strlen($opts['before_match'])) . $opts['before_match'];
2014
+		$req .= pack('N', strlen($opts['after_match'])) . $opts['after_match'];
2015
+		$req .= pack('N', strlen($opts['chunk_separator'])) . $opts['chunk_separator'];
2016
+		$req .= pack('NN', (int)$opts['limit'], (int)$opts['around']);
2017
+		$req .= pack('NNN', (int)$opts['limit_passages'], (int)$opts['limit_words'], (int)$opts['start_passage_id']); // v.1.2
2018
+		$req .= pack('N', strlen($opts['html_strip_mode'])) . $opts['html_strip_mode'];
2019
+		$req .= pack('N', strlen($opts['passage_boundary'])) . $opts['passage_boundary'];
2020
+
2021
+		// documents
2022
+		$req .= pack('N', count($docs));
2023
+		foreach ($docs as $doc) {
2024
+			assert(is_string($doc));
2025
+			$req .= pack('N', strlen($doc)) . $doc;
2026
+		}
2027
+
2028
+		////////////////////////////
2029
+		// send query, get response
2030
+		////////////////////////////
2031
+
2032
+		$len = strlen($req);
2033
+		$req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len) . $req; // add header
2034
+		if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_EXCERPT))) {
2035
+			$this->mbPop();
2036
+			return false;
2037
+		}
2038
+
2039
+		//////////////////
2040
+		// parse response
2041
+		//////////////////
2042
+
2043
+		$pos = 0;
2044
+		$res = array();
2045
+		$rlen = strlen($response);
2046
+		$count = count($docs);
2047
+		while ($count--) {
2048
+			list(, $len) = unpack('N*', substr($response, $pos, 4));
2049
+			$pos += 4;
2050
+
2051
+			if ($pos + $len > $rlen) {
2052
+				$this->error = 'incomplete reply';
2053
+				$this->mbPop();
2054
+				return false;
2055
+			}
2056
+			$res[] = $len ? substr($response, $pos, $len) : '';
2057
+			$pos += $len;
2058
+		}
2059
+
2060
+		$this->mbPop();
2061
+		return $res;
2062
+	}
2063
+
2064
+
2065
+	/////////////////////////////////////////////////////////////////////////////
2066
+	// keyword generation
2067
+	/////////////////////////////////////////////////////////////////////////////
2068
+
2069
+	/**
2070
+	 * Connect to searchd server, and generate keyword list for a given query returns false on failure,
2071
+	 * an array of words on success
2072
+	 *
2073
+	 * @param string $query
2074
+	 * @param string $index
2075
+	 * @param bool $hits
2076
+	 *
2077
+	 * @return array|bool
2078
+	 */
2079
+	public function buildKeywords($query, $index, $hits)
2080
+	{
2081
+		assert(is_string($query));
2082
+		assert(is_string($index));
2083
+		assert(is_bool($hits));
2084
+
2085
+		$this->mbPush();
2086
+
2087
+		if (!($fp = $this->connect())) {
2088
+			$this->mbPop();
2089
+			return false;
2090
+		}
2091
+
2092
+		/////////////////
2093
+		// build request
2094
+		/////////////////
2095
+
2096
+		// v.1.0 req
2097
+		$req  = pack('N', strlen($query)) . $query; // req query
2098
+		$req .= pack('N', strlen($index)) . $index; // req index
2099
+		$req .= pack('N', (int)$hits);
2100
+
2101
+		////////////////////////////
2102
+		// send query, get response
2103
+		////////////////////////////
2104
+
2105
+		$len = strlen($req);
2106
+		$req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len) . $req; // add header
2107
+		if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_KEYWORDS))) {
2108
+			$this->mbPop();
2109
+			return false;
2110
+		}
2111
+
2112
+		//////////////////
2113
+		// parse response
2114
+		//////////////////
2115
+
2116
+		$pos = 0;
2117
+		$res = array();
2118
+		$rlen = strlen($response);
2119
+		list(, $nwords) = unpack('N*', substr($response, $pos, 4));
2120
+		$pos += 4;
2121
+		for ($i = 0; $i < $nwords; $i++) {
2122
+			list(, $len) = unpack('N*', substr($response, $pos, 4));
2123
+			$pos += 4;
2124
+			$tokenized = $len ? substr($response, $pos, $len) : '';
2125
+			$pos += $len;
2126
+
2127
+			list(, $len) = unpack('N*', substr($response, $pos, 4));
2128
+			$pos += 4;
2129
+			$normalized = $len ? substr($response, $pos, $len) : '';
2130
+			$pos += $len;
2131
+
2132
+			$res[] = array(
2133
+				'tokenized' => $tokenized,
2134
+				'normalized' => $normalized
2135
+			);
2136
+
2137
+			if ($hits) {
2138
+				list($ndocs, $nhits) = array_values(unpack('N*N*', substr($response, $pos, 8)));
2139
+				$pos += 8;
2140
+				$res[$i]['docs'] = $ndocs;
2141
+				$res[$i]['hits'] = $nhits;
2142
+			}
2143
+
2144
+			if ($pos > $rlen) {
2145
+				$this->error = 'incomplete reply';
2146
+				$this->mbPop();
2147
+				return false;
2148
+			}
2149
+		}
2150
+
2151
+		$this->mbPop();
2152
+		return $res;
2153
+	}
2154
+
2155
+	/**
2156
+	 * @param string $string
2157
+	 *
2158
+	 * @return string
2159
+	 */
2160
+	public function escapeString($string)
2161
+	{
2162
+		$from = array('\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=', '<');
2163
+		$to   = array('\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=', '\<');
2164
+
2165
+		return str_replace($from, $to, $string);
2166
+	}
2167
+
2168
+	/////////////////////////////////////////////////////////////////////////////
2169
+	// attribute updates
2170
+	/////////////////////////////////////////////////////////////////////////////
2171
+
2172
+	/**
2173
+	 * Batch update given attributes in given rows in given indexes
2174
+	 * Returns amount of updated documents (0 or more) on success, or -1 on failure
2175
+	 *
2176
+	 * @param string $index
2177
+	 * @param array $attrs
2178
+	 * @param array $values
2179
+	 * @param bool $mva
2180
+	 * @param bool $ignore_non_existent
2181
+	 *
2182
+	 * @return int
2183
+	 */
2184
+	public function updateAttributes($index, array $attrs, array $values, $mva = false, $ignore_non_existent = false)
2185
+	{
2186
+		// verify everything
2187
+		assert(is_string($index));
2188
+		assert(is_bool($mva));
2189
+		assert(is_bool($ignore_non_existent));
2190
+
2191
+		foreach ($attrs as $attr) {
2192
+			assert(is_string($attr));
2193
+		}
2194
+
2195
+		foreach ($values as $id => $entry) {
2196
+			assert(is_numeric($id));
2197
+			assert(is_array($entry));
2198
+			assert(count($entry) == count($attrs));
2199
+			foreach ($entry as $v) {
2200
+				if ($mva) {
2201
+					assert(is_array($v));
2202
+					foreach ($v as $vv) {
2203
+						assert(is_int($vv));
2204
+					}
2205
+				} else {
2206
+					assert(is_int($v));
2207
+				}
2208
+			}
2209
+		}
2210
+
2211
+		// build request
2212
+		$this->mbPush();
2213
+		$req = pack('N', strlen($index)) . $index;
2214
+
2215
+		$req .= pack('N', count($attrs));
2216
+		$req .= pack('N', $ignore_non_existent ? 1 : 0);
2217
+		foreach ($attrs as $attr) {
2218
+			$req .= pack('N', strlen($attr)) . $attr;
2219
+			$req .= pack('N', $mva ? 1 : 0);
2220
+		}
2221
+
2222
+		$req .= pack('N', count($values));
2223
+		foreach ($values as $id => $entry) {
2224
+			$req .= sphPackU64($id);
2225
+			foreach ($entry as $v) {
2226
+				$req .= pack('N', $mva ? count($v) : $v);
2227
+				if ($mva) {
2228
+					foreach ($v as $vv) {
2229
+						$req .= pack('N', $vv);
2230
+					}
2231
+				}
2232
+			}
2233
+		}
2234
+
2235
+		// connect, send query, get response
2236
+		if (!($fp = $this->connect())) {
2237
+			$this->mbPop();
2238
+			return -1;
2239
+		}
2240
+
2241
+		$len = strlen($req);
2242
+		$req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len) . $req; // add header
2243
+		if (!$this->send($fp, $req, $len + 8)) {
2244
+			$this->mbPop();
2245
+			return -1;
2246
+		}
2247
+
2248
+		if (!($response = $this->getResponse($fp, VER_COMMAND_UPDATE))) {
2249
+			$this->mbPop();
2250
+			return -1;
2251
+		}
2252
+
2253
+		// parse response
2254
+		list(, $updated) = unpack('N*', substr($response, 0, 4));
2255
+		$this->mbPop();
2256
+		return $updated;
2257
+	}
2258
+
2259
+	/////////////////////////////////////////////////////////////////////////////
2260
+	// persistent connections
2261
+	/////////////////////////////////////////////////////////////////////////////
2262
+
2263
+	/**
2264
+	 * @return bool
2265
+	 */
2266
+	public function open()
2267
+	{
2268
+		if ($this->socket !== false) {
2269
+			$this->error = 'already connected';
2270
+			return false;
2271
+		}
2272
+		if (!($fp = $this->connect()))
2273
+			return false;
2274
+
2275
+		// command, command version = 0, body length = 4, body = 1
2276
+		$req = pack('nnNN', SEARCHD_COMMAND_PERSIST, 0, 4, 1);
2277
+		if (!$this->send($fp, $req, 12)) {
2278
+			return false;
2279
+		}
2280
+
2281
+		$this->socket = $fp;
2282
+		return true;
2283
+	}
2284
+
2285
+	/**
2286
+	 * @return bool
2287
+	 */
2288
+	public function close()
2289
+	{
2290
+		if ($this->socket === false) {
2291
+			$this->error = 'not connected';
2292
+			return false;
2293
+		}
2294
+
2295
+		fclose($this->socket);
2296
+		$this->socket = false;
2297
+
2298
+		return true;
2299
+	}
2300
+
2301
+	//////////////////////////////////////////////////////////////////////////
2302
+	// status
2303
+	//////////////////////////////////////////////////////////////////////////
2304
+
2305
+	/**
2306
+	 * @param bool $session
2307
+	 *
2308
+	 * @return array|bool
2309
+	 */
2310
+	public function status($session = false)
2311
+	{
2312
+		assert(is_bool($session));
2313
+
2314
+		$this->mbPush();
2315
+		if (!($fp = $this->connect())) {
2316
+			$this->mbPop();
2317
+			return false;
2318
+		}
2319
+
2320
+		$req = pack('nnNN', SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, $session ? 0 : 1); // len=4, body=1
2321
+		if (!$this->send($fp, $req, 12) || !($response = $this->getResponse($fp, VER_COMMAND_STATUS))) {
2322
+			$this->mbPop();
2323
+			return false;
2324
+		}
2325
+
2326
+		$res = substr($response, 4); // just ignore length, error handling, etc
2327
+		$p = 0;
2328
+		list($rows, $cols) = array_values(unpack('N*N*', substr($response, $p, 8)));
2329
+		$p += 8;
2330
+
2331
+		$res = array();
2332
+		for ($i = 0; $i < $rows; $i++) {
2333
+			for ($j = 0; $j < $cols; $j++) {
2334
+				list(, $len) = unpack('N*', substr($response, $p, 4));
2335
+				$p += 4;
2336
+				$res[$i][] = substr($response, $p, $len);
2337
+				$p += $len;
2338
+			}
2339
+		}
2340
+
2341
+		$this->mbPop();
2342
+		return $res;
2343
+	}
2344
+
2345
+	//////////////////////////////////////////////////////////////////////////
2346
+	// flush
2347
+	//////////////////////////////////////////////////////////////////////////
2348
+
2349
+	/**
2350
+	 * @return int
2351
+	 */
2352
+	public function flushAttributes()
2353
+	{
2354
+		$this->mbPush();
2355
+		if (!($fp = $this->connect())) {
2356
+			$this->mbPop();
2357
+			return -1;
2358
+		}
2359
+
2360
+		$req = pack('nnN', SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0); // len=0
2361
+		if (!$this->send($fp, $req, 8) || !($response = $this->getResponse($fp, VER_COMMAND_FLUSHATTRS))) {
2362
+			$this->mbPop();
2363
+			return -1;
2364
+		}
2365
+
2366
+		$tag = -1;
2367
+		if (strlen($response) == 4) {
2368
+			list(, $tag) = unpack('N*', $response);
2369
+		} else {
2370
+			$this->error = 'unexpected response length';
2371
+		}
2372
+
2373
+		$this->mbPop();
2374
+		return $tag;
2375
+	}
2376 2376
 }
Please login to merge, or discard this patch.
Spacing   +201 added lines, -201 removed lines patch added patch discarded remove patch
@@ -33,82 +33,82 @@  discard block
 block discarded – undo
33 33
 /////////////////////////////////////////////////////////////////////////////
34 34
 
35 35
 // known searchd commands
36
-define('SEARCHD_COMMAND_SEARCH',     0);
37
-define('SEARCHD_COMMAND_EXCERPT',    1);
38
-define('SEARCHD_COMMAND_UPDATE',     2);
39
-define('SEARCHD_COMMAND_KEYWORDS',   3);
40
-define('SEARCHD_COMMAND_PERSIST',    4);
41
-define('SEARCHD_COMMAND_STATUS',     5);
36
+define('SEARCHD_COMMAND_SEARCH', 0);
37
+define('SEARCHD_COMMAND_EXCERPT', 1);
38
+define('SEARCHD_COMMAND_UPDATE', 2);
39
+define('SEARCHD_COMMAND_KEYWORDS', 3);
40
+define('SEARCHD_COMMAND_PERSIST', 4);
41
+define('SEARCHD_COMMAND_STATUS', 5);
42 42
 define('SEARCHD_COMMAND_FLUSHATTRS', 7);
43 43
 
44 44
 // current client-side command implementation versions
45
-define('VER_COMMAND_SEARCH',     0x11E);
46
-define('VER_COMMAND_EXCERPT',    0x104);
47
-define('VER_COMMAND_UPDATE',     0x103);
48
-define('VER_COMMAND_KEYWORDS',   0x100);
49
-define('VER_COMMAND_STATUS',     0x101);
50
-define('VER_COMMAND_QUERY',      0x100);
45
+define('VER_COMMAND_SEARCH', 0x11E);
46
+define('VER_COMMAND_EXCERPT', 0x104);
47
+define('VER_COMMAND_UPDATE', 0x103);
48
+define('VER_COMMAND_KEYWORDS', 0x100);
49
+define('VER_COMMAND_STATUS', 0x101);
50
+define('VER_COMMAND_QUERY', 0x100);
51 51
 define('VER_COMMAND_FLUSHATTRS', 0x100);
52 52
 
53 53
 // known searchd status codes
54
-define('SEARCHD_OK',      0);
55
-define('SEARCHD_ERROR',   1);
56
-define('SEARCHD_RETRY',   2);
54
+define('SEARCHD_OK', 0);
55
+define('SEARCHD_ERROR', 1);
56
+define('SEARCHD_RETRY', 2);
57 57
 define('SEARCHD_WARNING', 3);
58 58
 
59 59
 // known match modes
60
-define('SPH_MATCH_ALL',       0);
61
-define('SPH_MATCH_ANY',       1);
62
-define('SPH_MATCH_PHRASE',    2);
63
-define('SPH_MATCH_BOOLEAN',   3);
64
-define('SPH_MATCH_EXTENDED',  4);
65
-define('SPH_MATCH_FULLSCAN',  5);
60
+define('SPH_MATCH_ALL', 0);
61
+define('SPH_MATCH_ANY', 1);
62
+define('SPH_MATCH_PHRASE', 2);
63
+define('SPH_MATCH_BOOLEAN', 3);
64
+define('SPH_MATCH_EXTENDED', 4);
65
+define('SPH_MATCH_FULLSCAN', 5);
66 66
 define('SPH_MATCH_EXTENDED2', 6); // extended engine V2 (TEMPORARY, WILL BE REMOVED)
67 67
 
68 68
 // known ranking modes (ext2 only)
69 69
 define('SPH_RANK_PROXIMITY_BM25', 0); // default mode, phrase proximity major factor and BM25 minor one
70
-define('SPH_RANK_BM25',           1); // statistical mode, BM25 ranking only (faster but worse quality)
71
-define('SPH_RANK_NONE',           2); // no ranking, all matches get a weight of 1
72
-define('SPH_RANK_WORDCOUNT',      3); // simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
73
-define('SPH_RANK_PROXIMITY',      4);
74
-define('SPH_RANK_MATCHANY',       5);
75
-define('SPH_RANK_FIELDMASK',      6);
76
-define('SPH_RANK_SPH04',          7);
77
-define('SPH_RANK_EXPR',           8);
78
-define('SPH_RANK_TOTAL',          9);
70
+define('SPH_RANK_BM25', 1); // statistical mode, BM25 ranking only (faster but worse quality)
71
+define('SPH_RANK_NONE', 2); // no ranking, all matches get a weight of 1
72
+define('SPH_RANK_WORDCOUNT', 3); // simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
73
+define('SPH_RANK_PROXIMITY', 4);
74
+define('SPH_RANK_MATCHANY', 5);
75
+define('SPH_RANK_FIELDMASK', 6);
76
+define('SPH_RANK_SPH04', 7);
77
+define('SPH_RANK_EXPR', 8);
78
+define('SPH_RANK_TOTAL', 9);
79 79
 
80 80
 // known sort modes
81
-define('SPH_SORT_RELEVANCE',     0);
82
-define('SPH_SORT_ATTR_DESC',     1);
83
-define('SPH_SORT_ATTR_ASC',      2);
81
+define('SPH_SORT_RELEVANCE', 0);
82
+define('SPH_SORT_ATTR_DESC', 1);
83
+define('SPH_SORT_ATTR_ASC', 2);
84 84
 define('SPH_SORT_TIME_SEGMENTS', 3);
85
-define('SPH_SORT_EXTENDED',      4);
86
-define('SPH_SORT_EXPR',          5);
85
+define('SPH_SORT_EXTENDED', 4);
86
+define('SPH_SORT_EXPR', 5);
87 87
 
88 88
 // known filter types
89
-define('SPH_FILTER_VALUES',     0);
90
-define('SPH_FILTER_RANGE',      1);
89
+define('SPH_FILTER_VALUES', 0);
90
+define('SPH_FILTER_RANGE', 1);
91 91
 define('SPH_FILTER_FLOATRANGE', 2);
92
-define('SPH_FILTER_STRING',     3);
92
+define('SPH_FILTER_STRING', 3);
93 93
 
94 94
 // known attribute types
95
-define('SPH_ATTR_INTEGER',   1);
95
+define('SPH_ATTR_INTEGER', 1);
96 96
 define('SPH_ATTR_TIMESTAMP', 2);
97
-define('SPH_ATTR_ORDINAL',   3);
98
-define('SPH_ATTR_BOOL',      4);
99
-define('SPH_ATTR_FLOAT',     5);
100
-define('SPH_ATTR_BIGINT',    6);
101
-define('SPH_ATTR_STRING',    7);
102
-define('SPH_ATTR_FACTORS',   1001);
103
-define('SPH_ATTR_MULTI',     0x40000001);
104
-define('SPH_ATTR_MULTI64',   0x40000002);
97
+define('SPH_ATTR_ORDINAL', 3);
98
+define('SPH_ATTR_BOOL', 4);
99
+define('SPH_ATTR_FLOAT', 5);
100
+define('SPH_ATTR_BIGINT', 6);
101
+define('SPH_ATTR_STRING', 7);
102
+define('SPH_ATTR_FACTORS', 1001);
103
+define('SPH_ATTR_MULTI', 0x40000001);
104
+define('SPH_ATTR_MULTI64', 0x40000002);
105 105
 
106 106
 // known grouping functions
107
-define('SPH_GROUPBY_DAY',      0);
108
-define('SPH_GROUPBY_WEEK',     1);
109
-define('SPH_GROUPBY_MONTH',    2);
110
-define('SPH_GROUPBY_YEAR',     3);
111
-define('SPH_GROUPBY_ATTR',     4);
107
+define('SPH_GROUPBY_DAY', 0);
108
+define('SPH_GROUPBY_WEEK', 1);
109
+define('SPH_GROUPBY_MONTH', 2);
110
+define('SPH_GROUPBY_YEAR', 3);
111
+define('SPH_GROUPBY_ATTR', 4);
112 112
 define('SPH_GROUPBY_ATTRPAIR', 5);
113 113
 
114 114
 // important properties of PHP's integers:
@@ -149,19 +149,19 @@  discard block
 block discarded – undo
149 149
     assert(is_numeric($value));
150 150
 
151 151
     // x64
152
-    if (PHP_INT_SIZE >= 8) {
152
+    if (PHP_INT_SIZE>=8) {
153 153
         $value = (int)$value;
154 154
         return pack('NN', $value >> 32, $value & 0xFFFFFFFF);
155 155
     }
156 156
 
157 157
     // x32, int
158 158
     if (is_int($value)) {
159
-        return pack('NN', $value < 0 ? -1 : 0, $value);
159
+        return pack('NN', $value<0 ? -1 : 0, $value);
160 160
     }
161 161
 
162 162
     // x32, bcmath
163 163
     if (function_exists('bcmul')) {
164
-        if (bccomp($value, 0) == -1) {
164
+        if (bccomp($value, 0)==-1) {
165 165
             $value = bcadd('18446744073709551616', $value);
166 166
         }
167 167
         $h = bcdiv($value, '4294967296', 0);
@@ -179,8 +179,8 @@  discard block
 block discarded – undo
179 179
     $l = $m - ($q * 4294967296.0);
180 180
     $h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
181 181
 
182
-    if ($value < 0) {
183
-        if ($l == 0) {
182
+    if ($value<0) {
183
+        if ($l==0) {
184 184
             $h = 4294967296.0 - $h;
185 185
         } else {
186 186
             $h = 4294967295.0 - $h;
@@ -202,8 +202,8 @@  discard block
 block discarded – undo
202 202
     assert(is_numeric($value));
203 203
 
204 204
     // x64
205
-    if (PHP_INT_SIZE >= 8) {
206
-        assert($value >= 0);
205
+    if (PHP_INT_SIZE>=8) {
206
+        assert($value>=0);
207 207
 
208 208
         // x64, int
209 209
         if (is_int($value)) {
@@ -265,16 +265,16 @@  discard block
 block discarded – undo
265 265
 {
266 266
     list($hi, $lo) = array_values(unpack('N*N*', $value));
267 267
 
268
-    if (PHP_INT_SIZE >= 8) {
269
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
268
+    if (PHP_INT_SIZE>=8) {
269
+        if ($hi<0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
270 270
             $hi += 1 << 32;
271 271
         }
272
-        if ($lo < 0) {
272
+        if ($lo<0) {
273 273
             $lo += 1 << 32;
274 274
         }
275 275
 
276 276
         // x64, int
277
-        if ($hi <= 2147483647) {
277
+        if ($hi<=2147483647) {
278 278
             return ($hi << 32) + $lo;
279 279
         }
280 280
 
@@ -287,20 +287,20 @@  discard block
 block discarded – undo
287 287
         $C = 100000;
288 288
         $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
289 289
         $l = (($hi % $C) << 32) + ($lo % $C);
290
-        if ($l > $C) {
290
+        if ($l>$C) {
291 291
             $h += (int)($l / $C);
292 292
             $l  = $l % $C;
293 293
         }
294 294
 
295
-        if ($h == 0) {
295
+        if ($h==0) {
296 296
             return $l;
297 297
         }
298 298
         return sprintf('%d%05d', $h, $l);
299 299
     }
300 300
 
301 301
     // x32, int
302
-    if ($hi == 0) {
303
-        if ($lo > 0) {
302
+    if ($hi==0) {
303
+        if ($lo>0) {
304 304
             return $lo;
305 305
         }
306 306
         return sprintf('%u', $lo);
@@ -327,10 +327,10 @@  discard block
 block discarded – undo
327 327
 
328 328
     $h = sprintf('%.0f', $h);
329 329
     $l = sprintf('%07.0f', $l);
330
-    if ($h == '0') {
330
+    if ($h=='0') {
331 331
         return sprintf('%.0f', (float)$l);
332 332
     }
333
-    return $h . $l;
333
+    return $h.$l;
334 334
 }
335 335
 
336 336
 /**
@@ -345,24 +345,24 @@  discard block
 block discarded – undo
345 345
     list($hi, $lo) = array_values(unpack('N*N*', $value));
346 346
 
347 347
     // x64
348
-    if (PHP_INT_SIZE >= 8) {
349
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
348
+    if (PHP_INT_SIZE>=8) {
349
+        if ($hi<0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
350 350
             $hi += 1 << 32;
351 351
         }
352
-        if ($lo < 0) {
352
+        if ($lo<0) {
353 353
             $lo += 1 << 32;
354 354
         }
355 355
 
356 356
         return ($hi << 32) + $lo;
357 357
     }
358 358
 
359
-    if ($hi == 0) { // x32, int
360
-        if ($lo > 0) {
359
+    if ($hi==0) { // x32, int
360
+        if ($lo>0) {
361 361
             return $lo;
362 362
         }
363 363
         return sprintf('%u', $lo);
364
-    } elseif ($hi == -1) { // x32, int
365
-        if ($lo < 0) {
364
+    } elseif ($hi==-1) { // x32, int
365
+        if ($lo<0) {
366 366
             return $lo;
367 367
         }
368 368
         return sprintf('%.0f', $lo - 4294967296.0);
@@ -370,7 +370,7 @@  discard block
 block discarded – undo
370 370
 
371 371
     $neg = '';
372 372
     $c = 0;
373
-    if ($hi < 0) {
373
+    if ($hi<0) {
374 374
         $hi = ~$hi;
375 375
         $lo = ~$lo;
376 376
         $c = 1;
@@ -382,7 +382,7 @@  discard block
 block discarded – undo
382 382
 
383 383
     // x32, bcmath
384 384
     if (function_exists('bcmul')) {
385
-        return $neg . bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
385
+        return $neg.bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
386 386
     }
387 387
 
388 388
     // x32, no-bcmath
@@ -395,17 +395,17 @@  discard block
 block discarded – undo
395 395
     $mq = floor($m / 10000000.0);
396 396
     $l = $m - $mq * 10000000.0 + $c;
397 397
     $h = $q * 4294967296.0 + $r * 429.0 + $mq;
398
-    if ($l == 10000000) {
398
+    if ($l==10000000) {
399 399
         $l = 0;
400 400
         $h += 1;
401 401
     }
402 402
 
403 403
     $h = sprintf('%.0f', $h);
404 404
     $l = sprintf('%07.0f', $l);
405
-    if ($h == '0') {
406
-        return $neg . sprintf('%.0f', (float)$l);
405
+    if ($h=='0') {
406
+        return $neg.sprintf('%.0f', (float)$l);
407 407
     }
408
-    return $neg . $h . $l;
408
+    return $neg.$h.$l;
409 409
 }
410 410
 
411 411
 /**
@@ -415,9 +415,9 @@  discard block
 block discarded – undo
415 415
  */
416 416
 function sphFixUint($value)
417 417
 {
418
-    if (PHP_INT_SIZE >= 8) {
418
+    if (PHP_INT_SIZE>=8) {
419 419
         // x64 route, workaround broken unpack() in 5.2.2+
420
-        if ($value < 0) {
420
+        if ($value<0) {
421 421
             $value += 1 << 32;
422 422
         }
423 423
         return $value;
@@ -751,7 +751,7 @@  discard block
 block discarded – undo
751 751
 
752 752
     public function __destruct()
753 753
     {
754
-        if ($this->socket !== false) {
754
+        if ($this->socket!==false) {
755 755
             fclose($this->socket);
756 756
         }
757 757
     }
@@ -791,19 +791,19 @@  discard block
 block discarded – undo
791 791
     public function setServer($host, $port = 0)
792 792
     {
793 793
         assert(is_string($host));
794
-        if ($host[0] == '/') {
795
-            $this->path = 'unix://' . $host;
794
+        if ($host[0]=='/') {
795
+            $this->path = 'unix://'.$host;
796 796
             return;
797 797
         }
798
-        if (substr($host, 0, 7) == 'unix://') {
798
+        if (substr($host, 0, 7)=='unix://') {
799 799
             $this->path = $host;
800 800
             return;
801 801
         }
802 802
 
803 803
         $this->host = $host;
804 804
         $port = intval($port);
805
-        assert(0 <= $port && $port < 65536);
806
-        $this->port = $port == 0 ? 9312 : $port;
805
+        assert(0<=$port && $port<65536);
806
+        $this->port = $port==0 ? 9312 : $port;
807 807
         $this->path = '';
808 808
     }
809 809
 
@@ -827,7 +827,7 @@  discard block
 block discarded – undo
827 827
      */
828 828
     protected function send($handle, $data, $length)
829 829
     {
830
-        if (feof($handle) || fwrite($handle, $data, $length) !== $length) {
830
+        if (feof($handle) || fwrite($handle, $data, $length)!==$length) {
831 831
             $this->error = 'connection unexpectedly closed (timed out?)';
832 832
             $this->conn_error = true;
833 833
             return false;
@@ -866,7 +866,7 @@  discard block
 block discarded – undo
866 866
      */
867 867
     protected function connect()
868 868
     {
869
-        if ($this->socket !== false) {
869
+        if ($this->socket!==false) {
870 870
             // we are in persistent connection mode, so we have a socket
871 871
             // however, need to check whether it's still alive
872 872
             if (!@feof($this->socket)) {
@@ -889,7 +889,7 @@  discard block
 block discarded – undo
889 889
             $port = $this->port;
890 890
         }
891 891
 
892
-        if ($this->timeout <= 0) {
892
+        if ($this->timeout<=0) {
893 893
             $fp = @fsockopen($host, $port, $errno, $errstr);
894 894
         } else {
895 895
             $fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout);
@@ -921,7 +921,7 @@  discard block
 block discarded – undo
921 921
         // check version
922 922
         list(, $v) = unpack('N*', fread($fp, 4));
923 923
         $v = (int)$v;
924
-        if ($v < 1) {
924
+        if ($v<1) {
925 925
             fclose($fp);
926 926
             $this->error = "expected searchd protocol version 1+, got version '$v'";
927 927
             return false;
@@ -944,10 +944,10 @@  discard block
 block discarded – undo
944 944
         $len = 0;
945 945
 
946 946
         $header = fread($fp, 8);
947
-        if (strlen($header) == 8) {
947
+        if (strlen($header)==8) {
948 948
             list($status, $ver, $len) = array_values(unpack('n2a/Nb', $header));
949 949
             $left = $len;
950
-            while ($left > 0 && !feof($fp)) {
950
+            while ($left>0 && !feof($fp)) {
951 951
                 $chunk = fread($fp, min(8192, $left));
952 952
                 if ($chunk) {
953 953
                     $response .= $chunk;
@@ -956,13 +956,13 @@  discard block
 block discarded – undo
956 956
             }
957 957
         }
958 958
 
959
-        if ($this->socket === false) {
959
+        if ($this->socket===false) {
960 960
             fclose($fp);
961 961
         }
962 962
 
963 963
         // check response
964 964
         $read = strlen($response);
965
-        if (!$response || $read != $len) {
965
+        if (!$response || $read!=$len) {
966 966
             $this->error = $len
967 967
                 ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
968 968
                 : 'received zero-sized searchd response';
@@ -970,26 +970,26 @@  discard block
 block discarded – undo
970 970
         }
971 971
 
972 972
         // check status
973
-        if ($status == SEARCHD_WARNING) {
973
+        if ($status==SEARCHD_WARNING) {
974 974
             list(, $wlen) = unpack('N*', substr($response, 0, 4));
975 975
             $this->warning = substr($response, 4, $wlen);
976 976
             return substr($response, 4 + $wlen);
977 977
         }
978
-        if ($status == SEARCHD_ERROR) {
979
-            $this->error = 'searchd error: ' . substr($response, 4);
978
+        if ($status==SEARCHD_ERROR) {
979
+            $this->error = 'searchd error: '.substr($response, 4);
980 980
             return false;
981 981
         }
982
-        if ($status == SEARCHD_RETRY) {
983
-            $this->error = 'temporary searchd error: ' . substr($response, 4);
982
+        if ($status==SEARCHD_RETRY) {
983
+            $this->error = 'temporary searchd error: '.substr($response, 4);
984 984
             return false;
985 985
         }
986
-        if ($status != SEARCHD_OK) {
986
+        if ($status!=SEARCHD_OK) {
987 987
             $this->error = "unknown status code '$status'";
988 988
             return false;
989 989
         }
990 990
 
991 991
         // check version
992
-        if ($ver < $client_ver) {
992
+        if ($ver<$client_ver) {
993 993
             $this->warning = sprintf(
994 994
                 'searchd command v.%d.%d older than client\'s v.%d.%d, some options might not work',
995 995
                 $ver >> 8,
@@ -1018,15 +1018,15 @@  discard block
 block discarded – undo
1018 1018
     {
1019 1019
         assert(is_int($offset));
1020 1020
         assert(is_int($limit));
1021
-        assert($offset >= 0);
1022
-        assert($limit > 0);
1023
-        assert($max >= 0);
1021
+        assert($offset>=0);
1022
+        assert($limit>0);
1023
+        assert($max>=0);
1024 1024
         $this->offset = $offset;
1025 1025
         $this->limit = $limit;
1026
-        if ($max > 0) {
1026
+        if ($max>0) {
1027 1027
             $this->max_matches = $max;
1028 1028
         }
1029
-        if ($cutoff > 0) {
1029
+        if ($cutoff>0) {
1030 1030
             $this->cutoff = $cutoff;
1031 1031
         }
1032 1032
     }
@@ -1039,7 +1039,7 @@  discard block
 block discarded – undo
1039 1039
     public function setMaxQueryTime($max)
1040 1040
     {
1041 1041
         assert(is_int($max));
1042
-        assert($max >= 0);
1042
+        assert($max>=0);
1043 1043
         $this->max_query_time = $max;
1044 1044
     }
1045 1045
 
@@ -1072,9 +1072,9 @@  discard block
 block discarded – undo
1072 1072
      * @param int $ranker
1073 1073
      * @param string $rank_expr
1074 1074
      */
1075
-    public function setRankingMode($ranker, $rank_expr='')
1075
+    public function setRankingMode($ranker, $rank_expr = '')
1076 1076
     {
1077
-        assert($ranker === 0 || $ranker >= 1 && $ranker < SPH_RANK_TOTAL);
1077
+        assert($ranker===0 || $ranker>=1 && $ranker<SPH_RANK_TOTAL);
1078 1078
         assert(is_string($rank_expr));
1079 1079
         $this->ranker = $ranker;
1080 1080
         $this->rank_expr = $rank_expr;
@@ -1097,7 +1097,7 @@  discard block
 block discarded – undo
1097 1097
             SPH_SORT_EXPR
1098 1098
         )));
1099 1099
         assert(is_string($sort_by));
1100
-        assert($mode == SPH_SORT_RELEVANCE || strlen($sort_by) > 0);
1100
+        assert($mode==SPH_SORT_RELEVANCE || strlen($sort_by)>0);
1101 1101
 
1102 1102
         $this->sort = $mode;
1103 1103
         $this->sort_by = $sort_by;
@@ -1151,7 +1151,7 @@  discard block
 block discarded – undo
1151 1151
     {
1152 1152
         assert(is_numeric($min));
1153 1153
         assert(is_numeric($max));
1154
-        assert($min <= $max);
1154
+        assert($min<=$max);
1155 1155
 
1156 1156
         $this->min_id = $min;
1157 1157
         $this->max_id = $max;
@@ -1215,7 +1215,7 @@  discard block
 block discarded – undo
1215 1215
         assert(is_string($attribute));
1216 1216
         assert(is_numeric($min));
1217 1217
         assert(is_numeric($max));
1218
-        assert($min <= $max);
1218
+        assert($min<=$max);
1219 1219
 
1220 1220
         $this->filters[] = array(
1221 1221
             'type' => SPH_FILTER_RANGE,
@@ -1240,7 +1240,7 @@  discard block
 block discarded – undo
1240 1240
         assert(is_string($attribute));
1241 1241
         assert(is_float($min));
1242 1242
         assert(is_float($max));
1243
-        assert($min <= $max);
1243
+        assert($min<=$max);
1244 1244
 
1245 1245
         $this->filters[] = array(
1246 1246
             'type' => SPH_FILTER_FLOATRANGE,
@@ -1320,8 +1320,8 @@  discard block
 block discarded – undo
1320 1320
      */
1321 1321
     public function setRetries($count, $delay = 0)
1322 1322
     {
1323
-        assert(is_int($count) && $count >= 0);
1324
-        assert(is_int($delay) && $delay >= 0);
1323
+        assert(is_int($count) && $count>=0);
1324
+        assert(is_int($delay) && $delay>=0);
1325 1325
         $this->retry_count = $count;
1326 1326
         $this->retry_delay = $delay;
1327 1327
     }
@@ -1397,12 +1397,12 @@  discard block
 block discarded – undo
1397 1397
             'global_idf',
1398 1398
             'low_priority'
1399 1399
         );
1400
-        $flags = array (
1400
+        $flags = array(
1401 1401
             'reverse_scan' => array(0, 1),
1402 1402
             'sort_method' => array('pq', 'kbuffer'),
1403 1403
             'max_predicted_time' => array(0),
1404 1404
             'boolean_simplify' => array(true, false),
1405
-            'idf' => array ('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
1405
+            'idf' => array('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
1406 1406
             'global_idf' => array(true, false),
1407 1407
             'low_priority' => array(true, false)
1408 1408
         );
@@ -1410,29 +1410,29 @@  discard block
 block discarded – undo
1410 1410
         assert(isset($flag_name, $known_names));
1411 1411
         assert(
1412 1412
             in_array($flag_value, $flags[$flag_name], true) ||
1413
-            ($flag_name == 'max_predicted_time' && is_int($flag_value) && $flag_value >= 0)
1413
+            ($flag_name=='max_predicted_time' && is_int($flag_value) && $flag_value>=0)
1414 1414
         );
1415 1415
 
1416 1416
         switch ($flag_name) {
1417 1417
             case 'reverse_scan':
1418
-                $this->query_flags = sphSetBit($this->query_flags, 0, $flag_value == 1);
1418
+                $this->query_flags = sphSetBit($this->query_flags, 0, $flag_value==1);
1419 1419
                 break;
1420 1420
             case 'sort_method':
1421
-                $this->query_flags = sphSetBit($this->query_flags, 1, $flag_value == 'kbuffer');
1421
+                $this->query_flags = sphSetBit($this->query_flags, 1, $flag_value=='kbuffer');
1422 1422
                 break;
1423 1423
             case 'max_predicted_time':
1424
-                $this->query_flags = sphSetBit($this->query_flags, 2, $flag_value > 0);
1424
+                $this->query_flags = sphSetBit($this->query_flags, 2, $flag_value>0);
1425 1425
                 $this->predicted_time = (int)$flag_value;
1426 1426
                 break;
1427 1427
             case 'boolean_simplify':
1428 1428
                 $this->query_flags = sphSetBit($this->query_flags, 3, $flag_value);
1429 1429
                 break;
1430 1430
             case 'idf':
1431
-                if ($flag_value == 'normalized' || $flag_value == 'plain') {
1432
-                    $this->query_flags = sphSetBit($this->query_flags, 4, $flag_value == 'plain');
1431
+                if ($flag_value=='normalized' || $flag_value=='plain') {
1432
+                    $this->query_flags = sphSetBit($this->query_flags, 4, $flag_value=='plain');
1433 1433
                 }
1434
-                if ($flag_value == 'tfidf_normalized' || $flag_value == 'tfidf_unnormalized') {
1435
-                    $this->query_flags = sphSetBit($this->query_flags, 6, $flag_value == 'tfidf_normalized');
1434
+                if ($flag_value=='tfidf_normalized' || $flag_value=='tfidf_unnormalized') {
1435
+                    $this->query_flags = sphSetBit($this->query_flags, 6, $flag_value=='tfidf_normalized');
1436 1436
                 }
1437 1437
                 break;
1438 1438
             case 'global_idf':
@@ -1456,8 +1456,8 @@  discard block
 block discarded – undo
1456 1456
         assert(is_string($order_by));
1457 1457
         assert(is_int($offset));
1458 1458
         assert(is_int($limit));
1459
-        assert($offset >= 0);
1460
-        assert($limit > 0);
1459
+        assert($offset>=0);
1460
+        assert($limit>0);
1461 1461
 
1462 1462
         $this->outer_order_by = $order_by;
1463 1463
         $this->outer_offset = $offset;
@@ -1536,7 +1536,7 @@  discard block
 block discarded – undo
1536 1536
         $this->error = $results[0]['error'];
1537 1537
         $this->warning = $results[0]['warning'];
1538 1538
 
1539
-        if ($results[0]['status'] == SEARCHD_ERROR) {
1539
+        if ($results[0]['status']==SEARCHD_ERROR) {
1540 1540
             return false;
1541 1541
         } else {
1542 1542
             return $results[0];
@@ -1574,24 +1574,24 @@  discard block
 block discarded – undo
1574 1574
 
1575 1575
         // build request
1576 1576
         $req = pack('NNNNN', $this->query_flags, $this->offset, $this->limit, $this->mode, $this->ranker);
1577
-        if ($this->ranker == SPH_RANK_EXPR) {
1578
-            $req .= pack('N', strlen($this->rank_expr)) . $this->rank_expr;
1577
+        if ($this->ranker==SPH_RANK_EXPR) {
1578
+            $req .= pack('N', strlen($this->rank_expr)).$this->rank_expr;
1579 1579
         }
1580 1580
         $req .= pack('N', $this->sort); // (deprecated) sort mode
1581
-        $req .= pack('N', strlen($this->sort_by)) . $this->sort_by;
1582
-        $req .= pack('N', strlen($query)) . $query; // query itself
1581
+        $req .= pack('N', strlen($this->sort_by)).$this->sort_by;
1582
+        $req .= pack('N', strlen($query)).$query; // query itself
1583 1583
         $req .= pack('N', count($this->weights)); // weights
1584 1584
         foreach ($this->weights as $weight) {
1585 1585
             $req .= pack('N', (int)$weight);
1586 1586
         }
1587
-        $req .= pack('N', strlen($index)) . $index; // indexes
1587
+        $req .= pack('N', strlen($index)).$index; // indexes
1588 1588
         $req .= pack('N', 1); // id64 range marker
1589
-        $req .= sphPackU64($this->min_id) . sphPackU64($this->max_id); // id64 range
1589
+        $req .= sphPackU64($this->min_id).sphPackU64($this->max_id); // id64 range
1590 1590
 
1591 1591
         // filters
1592 1592
         $req .= pack('N', count($this->filters));
1593 1593
         foreach ($this->filters as $filter) {
1594
-            $req .= pack('N', strlen($filter['attr'])) . $filter['attr'];
1594
+            $req .= pack('N', strlen($filter['attr'])).$filter['attr'];
1595 1595
             $req .= pack('N', $filter['type']);
1596 1596
             switch ($filter['type']) {
1597 1597
                 case SPH_FILTER_VALUES:
@@ -1601,13 +1601,13 @@  discard block
 block discarded – undo
1601 1601
                     }
1602 1602
                     break;
1603 1603
                 case SPH_FILTER_RANGE:
1604
-                    $req .= sphPackI64($filter['min']) . sphPackI64($filter['max']);
1604
+                    $req .= sphPackI64($filter['min']).sphPackI64($filter['max']);
1605 1605
                     break;
1606 1606
                 case SPH_FILTER_FLOATRANGE:
1607
-                    $req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']);
1607
+                    $req .= $this->packFloat($filter['min']).$this->packFloat($filter['max']);
1608 1608
                     break;
1609 1609
                 case SPH_FILTER_STRING:
1610
-                    $req .= pack('N', strlen($filter['value'])) . $filter['value'];
1610
+                    $req .= pack('N', strlen($filter['value'])).$filter['value'];
1611 1611
                     break;
1612 1612
                 default:
1613 1613
                     assert(0 && 'internal error: unhandled filter type');
@@ -1616,27 +1616,27 @@  discard block
 block discarded – undo
1616 1616
         }
1617 1617
 
1618 1618
         // group-by clause, max-matches count, group-sort clause, cutoff count
1619
-        $req .= pack('NN', $this->group_func, strlen($this->group_by)) . $this->group_by;
1619
+        $req .= pack('NN', $this->group_func, strlen($this->group_by)).$this->group_by;
1620 1620
         $req .= pack('N', $this->max_matches);
1621
-        $req .= pack('N', strlen($this->group_sort)) . $this->group_sort;
1621
+        $req .= pack('N', strlen($this->group_sort)).$this->group_sort;
1622 1622
         $req .= pack('NNN', $this->cutoff, $this->retry_count, $this->retry_delay);
1623
-        $req .= pack('N', strlen($this->group_distinct)) . $this->group_distinct;
1623
+        $req .= pack('N', strlen($this->group_distinct)).$this->group_distinct;
1624 1624
 
1625 1625
         // anchor point
1626 1626
         if (empty($this->anchor)) {
1627 1627
             $req .= pack('N', 0);
1628 1628
         } else {
1629
-            $a =& $this->anchor;
1629
+            $a = & $this->anchor;
1630 1630
             $req .= pack('N', 1);
1631
-            $req .= pack('N', strlen($a['attrlat'])) . $a['attrlat'];
1632
-            $req .= pack('N', strlen($a['attrlong'])) . $a['attrlong'];
1633
-            $req .= $this->packFloat($a['lat']) . $this->packFloat($a['long']);
1631
+            $req .= pack('N', strlen($a['attrlat'])).$a['attrlat'];
1632
+            $req .= pack('N', strlen($a['attrlong'])).$a['attrlong'];
1633
+            $req .= $this->packFloat($a['lat']).$this->packFloat($a['long']);
1634 1634
         }
1635 1635
 
1636 1636
         // per-index weights
1637 1637
         $req .= pack('N', count($this->index_weights));
1638 1638
         foreach ($this->index_weights as $idx => $weight) {
1639
-            $req .= pack('N', strlen($idx)) . $idx . pack('N', $weight);
1639
+            $req .= pack('N', strlen($idx)).$idx.pack('N', $weight);
1640 1640
         }
1641 1641
 
1642 1642
         // max query time
@@ -1645,16 +1645,16 @@  discard block
 block discarded – undo
1645 1645
         // per-field weights
1646 1646
         $req .= pack('N', count($this->field_weights));
1647 1647
         foreach ($this->field_weights as $field => $weight) {
1648
-            $req .= pack('N', strlen($field)) . $field . pack('N', $weight);
1648
+            $req .= pack('N', strlen($field)).$field.pack('N', $weight);
1649 1649
         }
1650 1650
 
1651 1651
         // comment
1652
-        $req .= pack('N', strlen($comment)) . $comment;
1652
+        $req .= pack('N', strlen($comment)).$comment;
1653 1653
 
1654 1654
         // attribute overrides
1655 1655
         $req .= pack('N', count($this->overrides));
1656 1656
         foreach ($this->overrides as $key => $entry) {
1657
-            $req .= pack('N', strlen($entry['attr'])) . $entry['attr'];
1657
+            $req .= pack('N', strlen($entry['attr'])).$entry['attr'];
1658 1658
             $req .= pack('NN', $entry['type'], count($entry['values']));
1659 1659
             foreach ($entry['values'] as $id => $val) {
1660 1660
                 assert(is_numeric($id));
@@ -1676,14 +1676,14 @@  discard block
 block discarded – undo
1676 1676
         }
1677 1677
 
1678 1678
         // select-list
1679
-        $req .= pack('N', strlen($this->select)) . $this->select;
1679
+        $req .= pack('N', strlen($this->select)).$this->select;
1680 1680
 
1681 1681
         // max_predicted_time
1682
-        if ($this->predicted_time > 0) {
1682
+        if ($this->predicted_time>0) {
1683 1683
             $req .= pack('N', (int)$this->predicted_time);
1684 1684
         }
1685 1685
 
1686
-        $req .= pack('N', strlen($this->outer_order_by)) . $this->outer_order_by;
1686
+        $req .= pack('N', strlen($this->outer_order_by)).$this->outer_order_by;
1687 1687
         $req .= pack('NN', $this->outer_offset, $this->outer_limit);
1688 1688
         if ($this->has_outer) {
1689 1689
             $req .= pack('N', 1);
@@ -1723,7 +1723,7 @@  discard block
 block discarded – undo
1723 1723
         $nreqs = count($this->reqs);
1724 1724
         $req = join('', $this->reqs);
1725 1725
         $len = 8 + strlen($req);
1726
-        $req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs) . $req; // add header
1726
+        $req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs).$req; // add header
1727 1727
 
1728 1728
         if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_SEARCH))) {
1729 1729
             $this->mbPop();
@@ -1751,9 +1751,9 @@  discard block
 block discarded – undo
1751 1751
         $max = strlen($response); // max position for checks, to protect against broken responses
1752 1752
 
1753 1753
         $results = array();
1754
-        for ($ires = 0; $ires < $nreqs && $p < $max; $ires++) {
1754
+        for ($ires = 0; $ires<$nreqs && $p<$max; $ires++) {
1755 1755
             $results[] = array();
1756
-            $result =& $results[$ires];
1756
+            $result = & $results[$ires];
1757 1757
 
1758 1758
             $result['error'] = '';
1759 1759
             $result['warning'] = '';
@@ -1762,13 +1762,13 @@  discard block
 block discarded – undo
1762 1762
             list(, $status) = unpack('N*', substr($response, $p, 4));
1763 1763
             $p += 4;
1764 1764
             $result['status'] = $status;
1765
-            if ($status != SEARCHD_OK) {
1765
+            if ($status!=SEARCHD_OK) {
1766 1766
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1767 1767
                 $p += 4;
1768 1768
                 $message = substr($response, $p, $len);
1769 1769
                 $p += $len;
1770 1770
 
1771
-                if ($status == SEARCHD_WARNING) {
1771
+                if ($status==SEARCHD_WARNING) {
1772 1772
                     $result['warning'] = $message;
1773 1773
                 } else {
1774 1774
                     $result['error'] = $message;
@@ -1782,7 +1782,7 @@  discard block
 block discarded – undo
1782 1782
 
1783 1783
             list(, $nfields) = unpack('N*', substr($response, $p, 4));
1784 1784
             $p += 4;
1785
-            while ($nfields --> 0 && $p < $max) {
1785
+            while ($nfields-->0 && $p<$max) {
1786 1786
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1787 1787
                 $p += 4;
1788 1788
                 $fields[] = substr($response, $p, $len);
@@ -1792,7 +1792,7 @@  discard block
 block discarded – undo
1792 1792
 
1793 1793
             list(, $nattrs) = unpack('N*', substr($response, $p, 4));
1794 1794
             $p += 4;
1795
-            while ($nattrs --> 0 && $p < $max) {
1795
+            while ($nattrs-->0 && $p<$max) {
1796 1796
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1797 1797
                 $p += 4;
1798 1798
                 $attr = substr($response, $p, $len);
@@ -1811,7 +1811,7 @@  discard block
 block discarded – undo
1811 1811
 
1812 1812
             // read matches
1813 1813
             $idx = -1;
1814
-            while ($count --> 0 && $p < $max) {
1814
+            while ($count-->0 && $p<$max) {
1815 1815
                 // index into result array
1816 1816
                 $idx++;
1817 1817
 
@@ -1839,14 +1839,14 @@  discard block
 block discarded – undo
1839 1839
                 $attrvals = array();
1840 1840
                 foreach ($attrs as $attr => $type) {
1841 1841
                     // handle 64bit ints
1842
-                    if ($type == SPH_ATTR_BIGINT) {
1842
+                    if ($type==SPH_ATTR_BIGINT) {
1843 1843
                         $attrvals[$attr] = sphUnpackI64(substr($response, $p, 8));
1844 1844
                         $p += 8;
1845 1845
                         continue;
1846 1846
                     }
1847 1847
 
1848 1848
                     // handle floats
1849
-                    if ($type == SPH_ATTR_FLOAT) {
1849
+                    if ($type==SPH_ATTR_FLOAT) {
1850 1850
                         list(, $uval) = unpack('N*', substr($response, $p, 4));
1851 1851
                         $p += 4;
1852 1852
                         list(, $fval) = unpack('f*', pack('L', $uval));
@@ -1857,28 +1857,28 @@  discard block
 block discarded – undo
1857 1857
                     // handle everything else as unsigned ints
1858 1858
                     list(, $val) = unpack('N*', substr($response, $p, 4));
1859 1859
                     $p += 4;
1860
-                    if ($type == SPH_ATTR_MULTI) {
1860
+                    if ($type==SPH_ATTR_MULTI) {
1861 1861
                         $attrvals[$attr] = array();
1862 1862
                         $nvalues = $val;
1863
-                        while ($nvalues --> 0 && $p < $max) {
1863
+                        while ($nvalues-->0 && $p<$max) {
1864 1864
                             list(, $val) = unpack('N*', substr($response, $p, 4));
1865 1865
                             $p += 4;
1866 1866
                             $attrvals[$attr][] = sphFixUint($val);
1867 1867
                         }
1868
-                    } elseif ($type == SPH_ATTR_MULTI64) {
1868
+                    } elseif ($type==SPH_ATTR_MULTI64) {
1869 1869
                         $attrvals[$attr] = array();
1870 1870
                         $nvalues = $val;
1871
-                        while ($nvalues > 0 && $p < $max) {
1871
+                        while ($nvalues>0 && $p<$max) {
1872 1872
                             $attrvals[$attr][] = sphUnpackI64(substr($response, $p, 8));
1873 1873
                             $p += 8;
1874 1874
                             $nvalues -= 2;
1875 1875
                         }
1876
-                    } elseif ($type == SPH_ATTR_STRING) {
1876
+                    } elseif ($type==SPH_ATTR_STRING) {
1877 1877
                         $attrvals[$attr] = substr($response, $p, $val);
1878 1878
                         $p += $val;
1879
-                    } elseif ($type == SPH_ATTR_FACTORS) {
1879
+                    } elseif ($type==SPH_ATTR_FACTORS) {
1880 1880
                         $attrvals[$attr] = substr($response, $p, $val - 4);
1881
-                        $p += $val-4;
1881
+                        $p += $val - 4;
1882 1882
                     } else {
1883 1883
                         $attrvals[$attr] = sphFixUint($val);
1884 1884
                     }
@@ -1897,14 +1897,14 @@  discard block
 block discarded – undo
1897 1897
             $result['time'] = sprintf('%.3f', $msecs / 1000);
1898 1898
             $p += 16;
1899 1899
 
1900
-            while ($words --> 0 && $p < $max) {
1900
+            while ($words-->0 && $p<$max) {
1901 1901
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1902 1902
                 $p += 4;
1903 1903
                 $word = substr($response, $p, $len);
1904 1904
                 $p += $len;
1905 1905
                 list($docs, $hits) = array_values(unpack('N*N*', substr($response, $p, 8)));
1906 1906
                 $p += 8;
1907
-                $result['words'][$word] = array (
1907
+                $result['words'][$word] = array(
1908 1908
                     'docs' => sprintf('%u', $docs),
1909 1909
                     'hits' => sprintf('%u', $hits)
1910 1910
                 );
@@ -2006,23 +2006,23 @@  discard block
 block discarded – undo
2006 2006
             $flags |= 1024;
2007 2007
         }
2008 2008
         $req = pack('NN', 0, $flags); // mode=0, flags=$flags
2009
-        $req .= pack('N', strlen($index)) . $index; // req index
2010
-        $req .= pack('N', strlen($words)) . $words; // req words
2009
+        $req .= pack('N', strlen($index)).$index; // req index
2010
+        $req .= pack('N', strlen($words)).$words; // req words
2011 2011
 
2012 2012
         // options
2013
-        $req .= pack('N', strlen($opts['before_match'])) . $opts['before_match'];
2014
-        $req .= pack('N', strlen($opts['after_match'])) . $opts['after_match'];
2015
-        $req .= pack('N', strlen($opts['chunk_separator'])) . $opts['chunk_separator'];
2013
+        $req .= pack('N', strlen($opts['before_match'])).$opts['before_match'];
2014
+        $req .= pack('N', strlen($opts['after_match'])).$opts['after_match'];
2015
+        $req .= pack('N', strlen($opts['chunk_separator'])).$opts['chunk_separator'];
2016 2016
         $req .= pack('NN', (int)$opts['limit'], (int)$opts['around']);
2017 2017
         $req .= pack('NNN', (int)$opts['limit_passages'], (int)$opts['limit_words'], (int)$opts['start_passage_id']); // v.1.2
2018
-        $req .= pack('N', strlen($opts['html_strip_mode'])) . $opts['html_strip_mode'];
2019
-        $req .= pack('N', strlen($opts['passage_boundary'])) . $opts['passage_boundary'];
2018
+        $req .= pack('N', strlen($opts['html_strip_mode'])).$opts['html_strip_mode'];
2019
+        $req .= pack('N', strlen($opts['passage_boundary'])).$opts['passage_boundary'];
2020 2020
 
2021 2021
         // documents
2022 2022
         $req .= pack('N', count($docs));
2023 2023
         foreach ($docs as $doc) {
2024 2024
             assert(is_string($doc));
2025
-            $req .= pack('N', strlen($doc)) . $doc;
2025
+            $req .= pack('N', strlen($doc)).$doc;
2026 2026
         }
2027 2027
 
2028 2028
         ////////////////////////////
@@ -2030,7 +2030,7 @@  discard block
 block discarded – undo
2030 2030
         ////////////////////////////
2031 2031
 
2032 2032
         $len = strlen($req);
2033
-        $req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len) . $req; // add header
2033
+        $req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len).$req; // add header
2034 2034
         if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_EXCERPT))) {
2035 2035
             $this->mbPop();
2036 2036
             return false;
@@ -2048,7 +2048,7 @@  discard block
 block discarded – undo
2048 2048
             list(, $len) = unpack('N*', substr($response, $pos, 4));
2049 2049
             $pos += 4;
2050 2050
 
2051
-            if ($pos + $len > $rlen) {
2051
+            if ($pos + $len>$rlen) {
2052 2052
                 $this->error = 'incomplete reply';
2053 2053
                 $this->mbPop();
2054 2054
                 return false;
@@ -2094,8 +2094,8 @@  discard block
 block discarded – undo
2094 2094
         /////////////////
2095 2095
 
2096 2096
         // v.1.0 req
2097
-        $req  = pack('N', strlen($query)) . $query; // req query
2098
-        $req .= pack('N', strlen($index)) . $index; // req index
2097
+        $req  = pack('N', strlen($query)).$query; // req query
2098
+        $req .= pack('N', strlen($index)).$index; // req index
2099 2099
         $req .= pack('N', (int)$hits);
2100 2100
 
2101 2101
         ////////////////////////////
@@ -2103,7 +2103,7 @@  discard block
 block discarded – undo
2103 2103
         ////////////////////////////
2104 2104
 
2105 2105
         $len = strlen($req);
2106
-        $req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len) . $req; // add header
2106
+        $req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len).$req; // add header
2107 2107
         if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_KEYWORDS))) {
2108 2108
             $this->mbPop();
2109 2109
             return false;
@@ -2118,7 +2118,7 @@  discard block
 block discarded – undo
2118 2118
         $rlen = strlen($response);
2119 2119
         list(, $nwords) = unpack('N*', substr($response, $pos, 4));
2120 2120
         $pos += 4;
2121
-        for ($i = 0; $i < $nwords; $i++) {
2121
+        for ($i = 0; $i<$nwords; $i++) {
2122 2122
             list(, $len) = unpack('N*', substr($response, $pos, 4));
2123 2123
             $pos += 4;
2124 2124
             $tokenized = $len ? substr($response, $pos, $len) : '';
@@ -2141,7 +2141,7 @@  discard block
 block discarded – undo
2141 2141
                 $res[$i]['hits'] = $nhits;
2142 2142
             }
2143 2143
 
2144
-            if ($pos > $rlen) {
2144
+            if ($pos>$rlen) {
2145 2145
                 $this->error = 'incomplete reply';
2146 2146
                 $this->mbPop();
2147 2147
                 return false;
@@ -2159,8 +2159,8 @@  discard block
 block discarded – undo
2159 2159
      */
2160 2160
     public function escapeString($string)
2161 2161
     {
2162
-        $from = array('\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=', '<');
2163
-        $to   = array('\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=', '\<');
2162
+        $from = array('\\', '(', ')', '|', '-', '!', '@', '~', '"', '&', '/', '^', '$', '=', '<');
2163
+        $to   = array('\\\\', '\(', '\)', '\|', '\-', '\!', '\@', '\~', '\"', '\&', '\/', '\^', '\$', '\=', '\<');
2164 2164
 
2165 2165
         return str_replace($from, $to, $string);
2166 2166
     }
@@ -2195,7 +2195,7 @@  discard block
 block discarded – undo
2195 2195
         foreach ($values as $id => $entry) {
2196 2196
             assert(is_numeric($id));
2197 2197
             assert(is_array($entry));
2198
-            assert(count($entry) == count($attrs));
2198
+            assert(count($entry)==count($attrs));
2199 2199
             foreach ($entry as $v) {
2200 2200
                 if ($mva) {
2201 2201
                     assert(is_array($v));
@@ -2210,12 +2210,12 @@  discard block
 block discarded – undo
2210 2210
 
2211 2211
         // build request
2212 2212
         $this->mbPush();
2213
-        $req = pack('N', strlen($index)) . $index;
2213
+        $req = pack('N', strlen($index)).$index;
2214 2214
 
2215 2215
         $req .= pack('N', count($attrs));
2216 2216
         $req .= pack('N', $ignore_non_existent ? 1 : 0);
2217 2217
         foreach ($attrs as $attr) {
2218
-            $req .= pack('N', strlen($attr)) . $attr;
2218
+            $req .= pack('N', strlen($attr)).$attr;
2219 2219
             $req .= pack('N', $mva ? 1 : 0);
2220 2220
         }
2221 2221
 
@@ -2239,7 +2239,7 @@  discard block
 block discarded – undo
2239 2239
         }
2240 2240
 
2241 2241
         $len = strlen($req);
2242
-        $req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len) . $req; // add header
2242
+        $req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len).$req; // add header
2243 2243
         if (!$this->send($fp, $req, $len + 8)) {
2244 2244
             $this->mbPop();
2245 2245
             return -1;
@@ -2265,7 +2265,7 @@  discard block
 block discarded – undo
2265 2265
      */
2266 2266
     public function open()
2267 2267
     {
2268
-        if ($this->socket !== false) {
2268
+        if ($this->socket!==false) {
2269 2269
             $this->error = 'already connected';
2270 2270
             return false;
2271 2271
         }
@@ -2287,7 +2287,7 @@  discard block
 block discarded – undo
2287 2287
      */
2288 2288
     public function close()
2289 2289
     {
2290
-        if ($this->socket === false) {
2290
+        if ($this->socket===false) {
2291 2291
             $this->error = 'not connected';
2292 2292
             return false;
2293 2293
         }
@@ -2329,8 +2329,8 @@  discard block
 block discarded – undo
2329 2329
         $p += 8;
2330 2330
 
2331 2331
         $res = array();
2332
-        for ($i = 0; $i < $rows; $i++) {
2333
-            for ($j = 0; $j < $cols; $j++) {
2332
+        for ($i = 0; $i<$rows; $i++) {
2333
+            for ($j = 0; $j<$cols; $j++) {
2334 2334
                 list(, $len) = unpack('N*', substr($response, $p, 4));
2335 2335
                 $p += 4;
2336 2336
                 $res[$i][] = substr($response, $p, $len);
@@ -2364,7 +2364,7 @@  discard block
 block discarded – undo
2364 2364
         }
2365 2365
 
2366 2366
         $tag = -1;
2367
-        if (strlen($response) == 4) {
2367
+        if (strlen($response)==4) {
2368 2368
             list(, $tag) = unpack('N*', $response);
2369 2369
         } else {
2370 2370
             $this->error = 'unexpected response length';
Please login to merge, or discard this patch.
src/test.php 2 patches
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -74,7 +74,7 @@  discard block
 block discarded – undo
74 74
     $select = '';
75 75
     $count = count($args);
76 76
 
77
-    for ($i = 0; $i < $count; $i++) {
77
+    for ($i = 0; $i<$count; $i++) {
78 78
         switch ($args[$i]) {
79 79
             case '-h':
80 80
             case '--host':
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
                 }
169 169
                 break;
170 170
             default:
171
-                $q .= $args[$i] . ' ';
171
+                $q .= $args[$i].' ';
172 172
         }
173 173
     }
174 174
 
@@ -199,7 +199,7 @@  discard block
 block discarded – undo
199 199
         $cl->setSelect($select);
200 200
     }
201 201
     if ($limit) {
202
-        $cl->setLimits(0, $limit, ($limit > 1000) ? $limit : 1000);
202
+        $cl->setLimits(0, $limit, ($limit>1000) ? $limit : 1000);
203 203
     }
204 204
     $cl->setRankingMode($ranker);
205 205
     $res = $cl->query($q, $index);
@@ -208,16 +208,16 @@  discard block
 block discarded – undo
208 208
     // print me out
209 209
     ////////////////
210 210
 
211
-    if ($res === false) {
212
-        printf('Query failed: %s.' . PHP_EOL, $cl->getLastError());
211
+    if ($res===false) {
212
+        printf('Query failed: %s.'.PHP_EOL, $cl->getLastError());
213 213
 
214 214
     } else {
215 215
         if ($cl->getLastWarning()) {
216
-            printf('WARNING: %s' . PHP_EOL . PHP_EOL, $cl->getLastWarning());
216
+            printf('WARNING: %s'.PHP_EOL.PHP_EOL, $cl->getLastWarning());
217 217
         }
218 218
 
219 219
         print "Query '$q' retrieved {$res['total']} of {$res['total_found']} matches in {$res['time']} sec.\n";
220
-        print 'Query stats:' . PHP_EOL;
220
+        print 'Query stats:'.PHP_EOL;
221 221
         if (is_array($res['words'])) {
222 222
             foreach ($res['words'] as $word => $info) {
223 223
                 print "    '$word' found {$info['hits']} times in {$info['docs']} documents\n";
@@ -227,14 +227,14 @@  discard block
 block discarded – undo
227 227
 
228 228
         if (is_array($res['matches'])) {
229 229
             $n = 1;
230
-            print 'Matches:' . PHP_EOL;
230
+            print 'Matches:'.PHP_EOL;
231 231
             foreach ($res['matches'] as $docinfo) {
232 232
                 print "$n. doc_id={$docinfo['id']}, weight={$docinfo['weight']}";
233 233
                 foreach ($res['attrs'] as $attrname => $attrtype) {
234 234
                     $value = $docinfo['attrs'][$attrname];
235
-                    if ($attrtype == SPH_ATTR_MULTI || $attrtype == SPH_ATTR_MULTI64) {
236
-                        $value = '(' . join(',', $value) . ')';
237
-                    } elseif ($attrtype == SPH_ATTR_TIMESTAMP) {
235
+                    if ($attrtype==SPH_ATTR_MULTI || $attrtype==SPH_ATTR_MULTI64) {
236
+                        $value = '('.join(',', $value).')';
237
+                    } elseif ($attrtype==SPH_ATTR_TIMESTAMP) {
238 238
                         $value = date('Y-m-d H:i:s', $value);
239 239
                     }
240 240
                     print ", $attrname=$value";
Please login to merge, or discard this patch.
Indentation   +198 added lines, -198 removed lines patch added patch discarded remove patch
@@ -7,7 +7,7 @@  discard block
 block discarded – undo
7 7
 $file = __DIR__.'/../vendor/autoload.php';
8 8
 
9 9
 if (!file_exists($file)) {
10
-    throw new RuntimeException('Install dependencies to run test suite. "php composer.phar install --dev"');
10
+	throw new RuntimeException('Install dependencies to run test suite. "php composer.phar install --dev"');
11 11
 }
12 12
 
13 13
 require_once __DIR__.'/../vendor/autoload.php';
@@ -18,13 +18,13 @@  discard block
 block discarded – undo
18 18
 
19 19
 // for very old PHP versions, like at my home test server
20 20
 if (is_array($argv) && !isset($_SERVER['argv'])) {
21
-    $_SERVER['argv'] = $argv;
21
+	$_SERVER['argv'] = $argv;
22 22
 }
23 23
 unset($_SERVER['argv'][0]);
24 24
 
25 25
 // build query
26 26
 if (!is_array($_SERVER['argv']) || empty($_SERVER['argv'])) {
27
-    print <<<EOF
27
+	print <<<EOF
28 28
 Usage: php -f test.php [OPTIONS] query words
29 29
 
30 30
 Options are:
@@ -49,199 +49,199 @@  discard block
 block discarded – undo
49 49
 EOF;
50 50
 } else {
51 51
 
52
-    $args = array();
53
-    foreach ($_SERVER['argv'] as $arg) {
54
-        $args[] = $arg;
55
-    }
56
-
57
-    $cl = new SphinxClient();
58
-
59
-    $q = '';
60
-    $sql = '';
61
-    $mode = SPH_MATCH_ALL;
62
-    $host = 'localhost';
63
-    $port = 9312;
64
-    $index = '*';
65
-    $groupby = '';
66
-    $groupsort = '@group desc';
67
-    $filter = 'group_id';
68
-    $filtervals = array();
69
-    $distinct = '';
70
-    $sortby = '';
71
-    $sortexpr = '';
72
-    $limit = 20;
73
-    $ranker = SPH_RANK_PROXIMITY_BM25;
74
-    $select = '';
75
-    $count = count($args);
76
-
77
-    for ($i = 0; $i < $count; $i++) {
78
-        switch ($args[$i]) {
79
-            case '-h':
80
-            case '--host':
81
-                $host = $args[++$i];
82
-                break;
83
-            case '-p':
84
-            case '--port':
85
-                $port = (int)$args[++$i];
86
-                break;
87
-            case '-i':
88
-            case '--index':
89
-                $index = $args[++$i];
90
-                break;
91
-            case '-s':
92
-            case '--sortby':
93
-                $sortby = $args[++$i];
94
-                $sortexpr = '';
95
-                break;
96
-            case '-S':
97
-            case '--sortexpr':
98
-                $sortexpr = $args[++$i];
99
-                $sortby = '';
100
-                break;
101
-            case '-a':
102
-            case '--any':
103
-                $mode = SPH_MATCH_ANY;
104
-                break;
105
-            case '-b':
106
-            case '--boolean':
107
-                $mode = SPH_MATCH_BOOLEAN;
108
-                break;
109
-            case '-e':
110
-            case '--extended':
111
-                $mode = SPH_MATCH_EXTENDED;
112
-                break;
113
-            case '-e2':
114
-                $mode = SPH_MATCH_EXTENDED2;
115
-                break;
116
-            case '-ph':
117
-            case '--phrase':
118
-                $mode = SPH_MATCH_PHRASE;
119
-                break;
120
-            case '-f':
121
-            case '--filter':
122
-                $filter = $args[++$i];
123
-                break;
124
-            case '-v':
125
-            case '--value':
126
-                $filtervals[] = $args[++$i];
127
-                break;
128
-            case '-g':
129
-            case '--groupby':
130
-                $groupby = $args[++$i];
131
-                break;
132
-            case '-gs':
133
-            case '--groupsort':
134
-                $groupsort = $args[++$i];
135
-                break;
136
-            case '-d':
137
-            case '--distinct':
138
-                $distinct = $args[++$i];
139
-                break;
140
-            case '-l':
141
-            case '--limit':
142
-                $limit = (int)$args[++$i];
143
-                break;
144
-            case '--select':
145
-                $select = $args[++$i];
146
-                break;
147
-            case '-fr':
148
-            case '--filterrange':
149
-                $cl->setFilterRange($args[++$i], $args[++$i], $args[++$i]);
150
-                break;
151
-            case '-r':
152
-                switch (strtolower($args[++$i])) {
153
-                    case 'bm25':
154
-                        $ranker = SPH_RANK_BM25;
155
-                        break;
156
-                    case 'none':
157
-                        $ranker = SPH_RANK_NONE;
158
-                        break;
159
-                    case 'wordcount':
160
-                        $ranker = SPH_RANK_WORDCOUNT;
161
-                        break;
162
-                    case 'fieldmask':
163
-                        $ranker = SPH_RANK_FIELDMASK;
164
-                        break;
165
-                    case 'sph04':
166
-                        $ranker = SPH_RANK_SPH04;
167
-                        break;
168
-                }
169
-                break;
170
-            default:
171
-                $q .= $args[$i] . ' ';
172
-        }
173
-    }
174
-
175
-    ////////////
176
-    // do query
177
-    ////////////
178
-
179
-    $cl->setServer($host, $port);
180
-    $cl->setConnectTimeout(1);
181
-    $cl->setArrayResult(true);
182
-    $cl->setMatchMode($mode);
183
-    if (count($filtervals)) {
184
-        $cl->setFilter($filter, $filtervals);
185
-    }
186
-    if ($groupby) {
187
-        $cl->setGroupBy($groupby, SPH_GROUPBY_ATTR, $groupsort);
188
-    }
189
-    if ($sortby) {
190
-        $cl->setSortMode(SPH_SORT_EXTENDED, $sortby);
191
-    }
192
-    if ($sortexpr) {
193
-        $cl->setSortMode(SPH_SORT_EXPR, $sortexpr);
194
-    }
195
-    if ($distinct) {
196
-        $cl->setGroupDistinct($distinct);
197
-    }
198
-    if ($select) {
199
-        $cl->setSelect($select);
200
-    }
201
-    if ($limit) {
202
-        $cl->setLimits(0, $limit, ($limit > 1000) ? $limit : 1000);
203
-    }
204
-    $cl->setRankingMode($ranker);
205
-    $res = $cl->query($q, $index);
206
-
207
-    ////////////////
208
-    // print me out
209
-    ////////////////
210
-
211
-    if ($res === false) {
212
-        printf('Query failed: %s.' . PHP_EOL, $cl->getLastError());
213
-
214
-    } else {
215
-        if ($cl->getLastWarning()) {
216
-            printf('WARNING: %s' . PHP_EOL . PHP_EOL, $cl->getLastWarning());
217
-        }
218
-
219
-        print "Query '$q' retrieved {$res['total']} of {$res['total_found']} matches in {$res['time']} sec.\n";
220
-        print 'Query stats:' . PHP_EOL;
221
-        if (is_array($res['words'])) {
222
-            foreach ($res['words'] as $word => $info) {
223
-                print "    '$word' found {$info['hits']} times in {$info['docs']} documents\n";
224
-            }
225
-        }
226
-        print PHP_EOL;
227
-
228
-        if (is_array($res['matches'])) {
229
-            $n = 1;
230
-            print 'Matches:' . PHP_EOL;
231
-            foreach ($res['matches'] as $docinfo) {
232
-                print "$n. doc_id={$docinfo['id']}, weight={$docinfo['weight']}";
233
-                foreach ($res['attrs'] as $attrname => $attrtype) {
234
-                    $value = $docinfo['attrs'][$attrname];
235
-                    if ($attrtype == SPH_ATTR_MULTI || $attrtype == SPH_ATTR_MULTI64) {
236
-                        $value = '(' . join(',', $value) . ')';
237
-                    } elseif ($attrtype == SPH_ATTR_TIMESTAMP) {
238
-                        $value = date('Y-m-d H:i:s', $value);
239
-                    }
240
-                    print ", $attrname=$value";
241
-                }
242
-                print PHP_EOL;
243
-                $n++;
244
-            }
245
-        }
246
-    }
52
+	$args = array();
53
+	foreach ($_SERVER['argv'] as $arg) {
54
+		$args[] = $arg;
55
+	}
56
+
57
+	$cl = new SphinxClient();
58
+
59
+	$q = '';
60
+	$sql = '';
61
+	$mode = SPH_MATCH_ALL;
62
+	$host = 'localhost';
63
+	$port = 9312;
64
+	$index = '*';
65
+	$groupby = '';
66
+	$groupsort = '@group desc';
67
+	$filter = 'group_id';
68
+	$filtervals = array();
69
+	$distinct = '';
70
+	$sortby = '';
71
+	$sortexpr = '';
72
+	$limit = 20;
73
+	$ranker = SPH_RANK_PROXIMITY_BM25;
74
+	$select = '';
75
+	$count = count($args);
76
+
77
+	for ($i = 0; $i < $count; $i++) {
78
+		switch ($args[$i]) {
79
+			case '-h':
80
+			case '--host':
81
+				$host = $args[++$i];
82
+				break;
83
+			case '-p':
84
+			case '--port':
85
+				$port = (int)$args[++$i];
86
+				break;
87
+			case '-i':
88
+			case '--index':
89
+				$index = $args[++$i];
90
+				break;
91
+			case '-s':
92
+			case '--sortby':
93
+				$sortby = $args[++$i];
94
+				$sortexpr = '';
95
+				break;
96
+			case '-S':
97
+			case '--sortexpr':
98
+				$sortexpr = $args[++$i];
99
+				$sortby = '';
100
+				break;
101
+			case '-a':
102
+			case '--any':
103
+				$mode = SPH_MATCH_ANY;
104
+				break;
105
+			case '-b':
106
+			case '--boolean':
107
+				$mode = SPH_MATCH_BOOLEAN;
108
+				break;
109
+			case '-e':
110
+			case '--extended':
111
+				$mode = SPH_MATCH_EXTENDED;
112
+				break;
113
+			case '-e2':
114
+				$mode = SPH_MATCH_EXTENDED2;
115
+				break;
116
+			case '-ph':
117
+			case '--phrase':
118
+				$mode = SPH_MATCH_PHRASE;
119
+				break;
120
+			case '-f':
121
+			case '--filter':
122
+				$filter = $args[++$i];
123
+				break;
124
+			case '-v':
125
+			case '--value':
126
+				$filtervals[] = $args[++$i];
127
+				break;
128
+			case '-g':
129
+			case '--groupby':
130
+				$groupby = $args[++$i];
131
+				break;
132
+			case '-gs':
133
+			case '--groupsort':
134
+				$groupsort = $args[++$i];
135
+				break;
136
+			case '-d':
137
+			case '--distinct':
138
+				$distinct = $args[++$i];
139
+				break;
140
+			case '-l':
141
+			case '--limit':
142
+				$limit = (int)$args[++$i];
143
+				break;
144
+			case '--select':
145
+				$select = $args[++$i];
146
+				break;
147
+			case '-fr':
148
+			case '--filterrange':
149
+				$cl->setFilterRange($args[++$i], $args[++$i], $args[++$i]);
150
+				break;
151
+			case '-r':
152
+				switch (strtolower($args[++$i])) {
153
+					case 'bm25':
154
+						$ranker = SPH_RANK_BM25;
155
+						break;
156
+					case 'none':
157
+						$ranker = SPH_RANK_NONE;
158
+						break;
159
+					case 'wordcount':
160
+						$ranker = SPH_RANK_WORDCOUNT;
161
+						break;
162
+					case 'fieldmask':
163
+						$ranker = SPH_RANK_FIELDMASK;
164
+						break;
165
+					case 'sph04':
166
+						$ranker = SPH_RANK_SPH04;
167
+						break;
168
+				}
169
+				break;
170
+			default:
171
+				$q .= $args[$i] . ' ';
172
+		}
173
+	}
174
+
175
+	////////////
176
+	// do query
177
+	////////////
178
+
179
+	$cl->setServer($host, $port);
180
+	$cl->setConnectTimeout(1);
181
+	$cl->setArrayResult(true);
182
+	$cl->setMatchMode($mode);
183
+	if (count($filtervals)) {
184
+		$cl->setFilter($filter, $filtervals);
185
+	}
186
+	if ($groupby) {
187
+		$cl->setGroupBy($groupby, SPH_GROUPBY_ATTR, $groupsort);
188
+	}
189
+	if ($sortby) {
190
+		$cl->setSortMode(SPH_SORT_EXTENDED, $sortby);
191
+	}
192
+	if ($sortexpr) {
193
+		$cl->setSortMode(SPH_SORT_EXPR, $sortexpr);
194
+	}
195
+	if ($distinct) {
196
+		$cl->setGroupDistinct($distinct);
197
+	}
198
+	if ($select) {
199
+		$cl->setSelect($select);
200
+	}
201
+	if ($limit) {
202
+		$cl->setLimits(0, $limit, ($limit > 1000) ? $limit : 1000);
203
+	}
204
+	$cl->setRankingMode($ranker);
205
+	$res = $cl->query($q, $index);
206
+
207
+	////////////////
208
+	// print me out
209
+	////////////////
210
+
211
+	if ($res === false) {
212
+		printf('Query failed: %s.' . PHP_EOL, $cl->getLastError());
213
+
214
+	} else {
215
+		if ($cl->getLastWarning()) {
216
+			printf('WARNING: %s' . PHP_EOL . PHP_EOL, $cl->getLastWarning());
217
+		}
218
+
219
+		print "Query '$q' retrieved {$res['total']} of {$res['total_found']} matches in {$res['time']} sec.\n";
220
+		print 'Query stats:' . PHP_EOL;
221
+		if (is_array($res['words'])) {
222
+			foreach ($res['words'] as $word => $info) {
223
+				print "    '$word' found {$info['hits']} times in {$info['docs']} documents\n";
224
+			}
225
+		}
226
+		print PHP_EOL;
227
+
228
+		if (is_array($res['matches'])) {
229
+			$n = 1;
230
+			print 'Matches:' . PHP_EOL;
231
+			foreach ($res['matches'] as $docinfo) {
232
+				print "$n. doc_id={$docinfo['id']}, weight={$docinfo['weight']}";
233
+				foreach ($res['attrs'] as $attrname => $attrtype) {
234
+					$value = $docinfo['attrs'][$attrname];
235
+					if ($attrtype == SPH_ATTR_MULTI || $attrtype == SPH_ATTR_MULTI64) {
236
+						$value = '(' . join(',', $value) . ')';
237
+					} elseif ($attrtype == SPH_ATTR_TIMESTAMP) {
238
+						$value = date('Y-m-d H:i:s', $value);
239
+					}
240
+					print ", $attrname=$value";
241
+				}
242
+				print PHP_EOL;
243
+				$n++;
244
+			}
245
+		}
246
+	}
247 247
 }
Please login to merge, or discard this patch.