Completed
Push — master ( 7f247a...38af16 )
by Peter
02:17
created
src/SphinxClient.php 2 patches
Indentation   +1737 added lines, -1737 removed lines patch added patch discarded remove patch
@@ -140,1771 +140,1771 @@
 block discarded – undo
140 140
 /// pack 64-bit signed
141 141
 function sphPackI64($v)
142 142
 {
143
-    assert(is_numeric($v));
144
-
145
-    // x64
146
-    if (PHP_INT_SIZE >= 8) {
147
-        $v = (int)$v;
148
-        return pack('NN', $v >> 32, $v & 0xFFFFFFFF);
149
-    }
150
-
151
-    // x32, int
152
-    if (is_int($v)) {
153
-        return pack('NN', $v < 0 ? -1 : 0, $v);
154
-    }
155
-
156
-    // x32, bcmath
157
-    if (function_exists('bcmul')) {
158
-        if (bccomp($v, 0) == -1) {
159
-            $v = bcadd('18446744073709551616', $v);
160
-        }
161
-        $h = bcdiv($v, '4294967296', 0);
162
-        $l = bcmod($v, '4294967296');
163
-        return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
164
-    }
165
-
166
-    // x32, no-bcmath
167
-    $p = max(0, strlen($v) - 13);
168
-    $lo = abs((float)substr($v, $p));
169
-    $hi = abs((float)substr($v, 0, $p));
170
-
171
-    $m = $lo + $hi * 1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
172
-    $q = floor($m / 4294967296.0);
173
-    $l = $m - ($q * 4294967296.0);
174
-    $h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
175
-
176
-    if ($v < 0) {
177
-        if ($l == 0) {
178
-            $h = 4294967296.0 - $h;
179
-        } else {
180
-            $h = 4294967295.0 - $h;
181
-            $l = 4294967296.0 - $l;
182
-        }
183
-    }
184
-    return pack('NN', $h, $l);
143
+	assert(is_numeric($v));
144
+
145
+	// x64
146
+	if (PHP_INT_SIZE >= 8) {
147
+		$v = (int)$v;
148
+		return pack('NN', $v >> 32, $v & 0xFFFFFFFF);
149
+	}
150
+
151
+	// x32, int
152
+	if (is_int($v)) {
153
+		return pack('NN', $v < 0 ? -1 : 0, $v);
154
+	}
155
+
156
+	// x32, bcmath
157
+	if (function_exists('bcmul')) {
158
+		if (bccomp($v, 0) == -1) {
159
+			$v = bcadd('18446744073709551616', $v);
160
+		}
161
+		$h = bcdiv($v, '4294967296', 0);
162
+		$l = bcmod($v, '4294967296');
163
+		return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
164
+	}
165
+
166
+	// x32, no-bcmath
167
+	$p = max(0, strlen($v) - 13);
168
+	$lo = abs((float)substr($v, $p));
169
+	$hi = abs((float)substr($v, 0, $p));
170
+
171
+	$m = $lo + $hi * 1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
172
+	$q = floor($m / 4294967296.0);
173
+	$l = $m - ($q * 4294967296.0);
174
+	$h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
175
+
176
+	if ($v < 0) {
177
+		if ($l == 0) {
178
+			$h = 4294967296.0 - $h;
179
+		} else {
180
+			$h = 4294967295.0 - $h;
181
+			$l = 4294967296.0 - $l;
182
+		}
183
+	}
184
+	return pack('NN', $h, $l);
185 185
 }
186 186
 
187 187
 /// pack 64-bit unsigned
188 188
 function sphPackU64($v)
189 189
 {
190
-    assert(is_numeric($v));
191
-
192
-    // x64
193
-    if (PHP_INT_SIZE >= 8) {
194
-        assert($v >= 0);
195
-
196
-        // x64, int
197
-        if (is_int($v)) {
198
-            return pack('NN', $v >> 32, $v & 0xFFFFFFFF);
199
-        }
200
-
201
-        // x64, bcmath
202
-        if (function_exists('bcmul')) {
203
-            $h = bcdiv($v, 4294967296, 0);
204
-            $l = bcmod($v, 4294967296);
205
-            return pack('NN', $h, $l);
206
-        }
207
-
208
-        // x64, no-bcmath
209
-        $p = max(0, strlen($v) - 13);
210
-        $lo = (int)substr($v, $p);
211
-        $hi = (int)substr($v, 0, $p);
212
-
213
-        $m = $lo + $hi * 1316134912;
214
-        $l = $m % 4294967296;
215
-        $h = $hi * 2328 + (int)($m / 4294967296);
216
-
217
-        return pack('NN', $h, $l);
218
-    }
219
-
220
-    // x32, int
221
-    if (is_int($v)) {
222
-        return pack('NN', 0, $v);
223
-    }
224
-
225
-    // x32, bcmath
226
-    if (function_exists('bcmul')) {
227
-        $h = bcdiv($v, '4294967296', 0);
228
-        $l = bcmod($v, '4294967296');
229
-        return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
230
-    }
231
-
232
-    // x32, no-bcmath
233
-    $p = max(0, strlen($v) - 13);
234
-    $lo = (float)substr($v, $p);
235
-    $hi = (float)substr($v, 0, $p);
236
-
237
-    $m = $lo + $hi * 1316134912.0;
238
-    $q = floor($m / 4294967296.0);
239
-    $l = $m - ($q * 4294967296.0);
240
-    $h = $hi * 2328.0 + $q;
241
-
242
-    return pack('NN', $h, $l);
190
+	assert(is_numeric($v));
191
+
192
+	// x64
193
+	if (PHP_INT_SIZE >= 8) {
194
+		assert($v >= 0);
195
+
196
+		// x64, int
197
+		if (is_int($v)) {
198
+			return pack('NN', $v >> 32, $v & 0xFFFFFFFF);
199
+		}
200
+
201
+		// x64, bcmath
202
+		if (function_exists('bcmul')) {
203
+			$h = bcdiv($v, 4294967296, 0);
204
+			$l = bcmod($v, 4294967296);
205
+			return pack('NN', $h, $l);
206
+		}
207
+
208
+		// x64, no-bcmath
209
+		$p = max(0, strlen($v) - 13);
210
+		$lo = (int)substr($v, $p);
211
+		$hi = (int)substr($v, 0, $p);
212
+
213
+		$m = $lo + $hi * 1316134912;
214
+		$l = $m % 4294967296;
215
+		$h = $hi * 2328 + (int)($m / 4294967296);
216
+
217
+		return pack('NN', $h, $l);
218
+	}
219
+
220
+	// x32, int
221
+	if (is_int($v)) {
222
+		return pack('NN', 0, $v);
223
+	}
224
+
225
+	// x32, bcmath
226
+	if (function_exists('bcmul')) {
227
+		$h = bcdiv($v, '4294967296', 0);
228
+		$l = bcmod($v, '4294967296');
229
+		return pack('NN', (float)$h, (float)$l); // conversion to float is intentional; int would lose 31st bit
230
+	}
231
+
232
+	// x32, no-bcmath
233
+	$p = max(0, strlen($v) - 13);
234
+	$lo = (float)substr($v, $p);
235
+	$hi = (float)substr($v, 0, $p);
236
+
237
+	$m = $lo + $hi * 1316134912.0;
238
+	$q = floor($m / 4294967296.0);
239
+	$l = $m - ($q * 4294967296.0);
240
+	$h = $hi * 2328.0 + $q;
241
+
242
+	return pack('NN', $h, $l);
243 243
 }
244 244
 
245 245
 // unpack 64-bit unsigned
246 246
 function sphUnpackU64($v)
247 247
 {
248
-    list($hi, $lo) = array_values(unpack('N*N*', $v));
249
-
250
-    if (PHP_INT_SIZE >= 8) {
251
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
252
-            $hi += 1 << 32;
253
-        }
254
-        if ($lo < 0) {
255
-            $lo += 1 << 32;
256
-        }
257
-
258
-        // x64, int
259
-        if ($hi <= 2147483647) {
260
-            return ($hi << 32) + $lo;
261
-        }
262
-
263
-        // x64, bcmath
264
-        if (function_exists('bcmul')) {
265
-            return bcadd($lo, bcmul($hi, '4294967296'));
266
-        }
267
-
268
-        // x64, no-bcmath
269
-        $C = 100000;
270
-        $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
271
-        $l = (($hi % $C) << 32) + ($lo % $C);
272
-        if ($l > $C) {
273
-            $h += (int)($l / $C);
274
-            $l  = $l % $C;
275
-        }
276
-
277
-        if ($h == 0) {
278
-            return $l;
279
-        }
280
-        return sprintf('%d%05d', $h, $l);
281
-    }
282
-
283
-    // x32, int
284
-    if ($hi == 0) {
285
-        if ($lo > 0) {
286
-            return $lo;
287
-        }
288
-        return sprintf('%u', $lo);
289
-    }
290
-
291
-    $hi = sprintf('%u', $hi);
292
-    $lo = sprintf('%u', $lo);
293
-
294
-    // x32, bcmath
295
-    if (function_exists('bcmul')) {
296
-        return bcadd($lo, bcmul($hi, '4294967296'));
297
-    }
298
-
299
-    // x32, no-bcmath
300
-    $hi = (float)$hi;
301
-    $lo = (float)$lo;
302
-
303
-    $q = floor($hi / 10000000.0);
304
-    $r = $hi - $q * 10000000.0;
305
-    $m = $lo + $r * 4967296.0;
306
-    $mq = floor($m / 10000000.0);
307
-    $l = $m - $mq * 10000000.0;
308
-    $h = $q * 4294967296.0 + $r * 429.0 + $mq;
309
-
310
-    $h = sprintf('%.0f', $h);
311
-    $l = sprintf('%07.0f', $l);
312
-    if ($h == '0') {
313
-        return sprintf('%.0f', (float)$l);
314
-    }
315
-    return $h . $l;
248
+	list($hi, $lo) = array_values(unpack('N*N*', $v));
249
+
250
+	if (PHP_INT_SIZE >= 8) {
251
+		if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
252
+			$hi += 1 << 32;
253
+		}
254
+		if ($lo < 0) {
255
+			$lo += 1 << 32;
256
+		}
257
+
258
+		// x64, int
259
+		if ($hi <= 2147483647) {
260
+			return ($hi << 32) + $lo;
261
+		}
262
+
263
+		// x64, bcmath
264
+		if (function_exists('bcmul')) {
265
+			return bcadd($lo, bcmul($hi, '4294967296'));
266
+		}
267
+
268
+		// x64, no-bcmath
269
+		$C = 100000;
270
+		$h = ((int)($hi / $C) << 32) + (int)($lo / $C);
271
+		$l = (($hi % $C) << 32) + ($lo % $C);
272
+		if ($l > $C) {
273
+			$h += (int)($l / $C);
274
+			$l  = $l % $C;
275
+		}
276
+
277
+		if ($h == 0) {
278
+			return $l;
279
+		}
280
+		return sprintf('%d%05d', $h, $l);
281
+	}
282
+
283
+	// x32, int
284
+	if ($hi == 0) {
285
+		if ($lo > 0) {
286
+			return $lo;
287
+		}
288
+		return sprintf('%u', $lo);
289
+	}
290
+
291
+	$hi = sprintf('%u', $hi);
292
+	$lo = sprintf('%u', $lo);
293
+
294
+	// x32, bcmath
295
+	if (function_exists('bcmul')) {
296
+		return bcadd($lo, bcmul($hi, '4294967296'));
297
+	}
298
+
299
+	// x32, no-bcmath
300
+	$hi = (float)$hi;
301
+	$lo = (float)$lo;
302
+
303
+	$q = floor($hi / 10000000.0);
304
+	$r = $hi - $q * 10000000.0;
305
+	$m = $lo + $r * 4967296.0;
306
+	$mq = floor($m / 10000000.0);
307
+	$l = $m - $mq * 10000000.0;
308
+	$h = $q * 4294967296.0 + $r * 429.0 + $mq;
309
+
310
+	$h = sprintf('%.0f', $h);
311
+	$l = sprintf('%07.0f', $l);
312
+	if ($h == '0') {
313
+		return sprintf('%.0f', (float)$l);
314
+	}
315
+	return $h . $l;
316 316
 }
317 317
 
318 318
 // unpack 64-bit signed
319 319
 function sphUnpackI64($v)
320 320
 {
321
-    list($hi, $lo) = array_values(unpack('N*N*', $v));
322
-
323
-    // x64
324
-    if (PHP_INT_SIZE >= 8) {
325
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
326
-            $hi += 1 << 32;
327
-        }
328
-        if ($lo < 0) {
329
-            $lo += 1 << 32;
330
-        }
331
-
332
-        return ($hi << 32) + $lo;
333
-    }
334
-
335
-    if ($hi == 0) { // x32, int
336
-        if ($lo > 0) {
337
-            return $lo;
338
-        }
339
-        return sprintf('%u', $lo);
340
-    } elseif ($hi == -1) { // x32, int
341
-        if ($lo < 0) {
342
-            return $lo;
343
-        }
344
-        return sprintf('%.0f', $lo - 4294967296.0);
345
-    }
346
-
347
-    $neg = '';
348
-    $c = 0;
349
-    if ($hi < 0) {
350
-        $hi = ~$hi;
351
-        $lo = ~$lo;
352
-        $c = 1;
353
-        $neg = '-';
354
-    }
355
-
356
-    $hi = sprintf('%u', $hi);
357
-    $lo = sprintf('%u', $lo);
358
-
359
-    // x32, bcmath
360
-    if (function_exists('bcmul')) {
361
-        return $neg . bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
362
-    }
363
-
364
-    // x32, no-bcmath
365
-    $hi = (float)$hi;
366
-    $lo = (float)$lo;
367
-
368
-    $q = floor($hi / 10000000.0);
369
-    $r = $hi - $q * 10000000.0;
370
-    $m = $lo + $r * 4967296.0;
371
-    $mq = floor($m / 10000000.0);
372
-    $l = $m - $mq * 10000000.0 + $c;
373
-    $h = $q * 4294967296.0 + $r * 429.0 + $mq;
374
-    if ($l == 10000000) {
375
-        $l = 0;
376
-        $h += 1;
377
-    }
378
-
379
-    $h = sprintf('%.0f', $h);
380
-    $l = sprintf('%07.0f', $l);
381
-    if ($h == '0') {
382
-        return $neg . sprintf('%.0f', (float)$l);
383
-    }
384
-    return $neg . $h . $l;
321
+	list($hi, $lo) = array_values(unpack('N*N*', $v));
322
+
323
+	// x64
324
+	if (PHP_INT_SIZE >= 8) {
325
+		if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
326
+			$hi += 1 << 32;
327
+		}
328
+		if ($lo < 0) {
329
+			$lo += 1 << 32;
330
+		}
331
+
332
+		return ($hi << 32) + $lo;
333
+	}
334
+
335
+	if ($hi == 0) { // x32, int
336
+		if ($lo > 0) {
337
+			return $lo;
338
+		}
339
+		return sprintf('%u', $lo);
340
+	} elseif ($hi == -1) { // x32, int
341
+		if ($lo < 0) {
342
+			return $lo;
343
+		}
344
+		return sprintf('%.0f', $lo - 4294967296.0);
345
+	}
346
+
347
+	$neg = '';
348
+	$c = 0;
349
+	if ($hi < 0) {
350
+		$hi = ~$hi;
351
+		$lo = ~$lo;
352
+		$c = 1;
353
+		$neg = '-';
354
+	}
355
+
356
+	$hi = sprintf('%u', $hi);
357
+	$lo = sprintf('%u', $lo);
358
+
359
+	// x32, bcmath
360
+	if (function_exists('bcmul')) {
361
+		return $neg . bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
362
+	}
363
+
364
+	// x32, no-bcmath
365
+	$hi = (float)$hi;
366
+	$lo = (float)$lo;
367
+
368
+	$q = floor($hi / 10000000.0);
369
+	$r = $hi - $q * 10000000.0;
370
+	$m = $lo + $r * 4967296.0;
371
+	$mq = floor($m / 10000000.0);
372
+	$l = $m - $mq * 10000000.0 + $c;
373
+	$h = $q * 4294967296.0 + $r * 429.0 + $mq;
374
+	if ($l == 10000000) {
375
+		$l = 0;
376
+		$h += 1;
377
+	}
378
+
379
+	$h = sprintf('%.0f', $h);
380
+	$l = sprintf('%07.0f', $l);
381
+	if ($h == '0') {
382
+		return $neg . sprintf('%.0f', (float)$l);
383
+	}
384
+	return $neg . $h . $l;
385 385
 }
386 386
 
387 387
 
388 388
 function sphFixUint($value)
389 389
 {
390
-    if (PHP_INT_SIZE >= 8) {
391
-        // x64 route, workaround broken unpack() in 5.2.2+
392
-        if ($value < 0) {
393
-            $value += 1 << 32;
394
-        }
395
-        return $value;
396
-    } else {
397
-        // x32 route, workaround php signed/unsigned braindamage
398
-        return sprintf('%u', $value);
399
-    }
390
+	if (PHP_INT_SIZE >= 8) {
391
+		// x64 route, workaround broken unpack() in 5.2.2+
392
+		if ($value < 0) {
393
+			$value += 1 << 32;
394
+		}
395
+		return $value;
396
+	} else {
397
+		// x32 route, workaround php signed/unsigned braindamage
398
+		return sprintf('%u', $value);
399
+	}
400 400
 }
401 401
 
402 402
 function sphSetBit($flag, $bit, $on)
403 403
 {
404
-    if ($on) {
405
-        $flag |= 1 << $bit;
406
-    } else {
407
-        $reset = 16777215 ^ (1 << $bit);
408
-        $flag = $flag & $reset;
409
-    }
410
-    return $flag;
404
+	if ($on) {
405
+		$flag |= 1 << $bit;
406
+	} else {
407
+		$reset = 16777215 ^ (1 << $bit);
408
+		$flag = $flag & $reset;
409
+	}
410
+	return $flag;
411 411
 }
412 412
 
413 413
 
414 414
 /// sphinx searchd client class
415 415
 class SphinxClient
416 416
 {
417
-    protected $host = 'localhost'; ///< searchd host (default is 'localhost')
418
-    protected $port = 9312; ///< searchd port (default is 9312)
419
-    protected $offset = 0; ///< how many records to seek from result-set start (default is 0)
420
-    protected $limit = 20; ///< how many records to return from result-set starting at offset (default is 20)
421
-    protected $mode = SPH_MATCH_EXTENDED2; ///< query matching mode (default is SPH_MATCH_EXTENDED2)
422
-    protected $weights = array(); ///< per-field weights (default is 1 for all fields)
423
-    protected $sort = SPH_SORT_RELEVANCE; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
424
-    protected $sort_by = ''; ///< attribute to sort by (defualt is '')
425
-    protected $min_id = 0; ///< min ID to match (default is 0, which means no limit)
426
-    protected $max_id = 0; ///< max ID to match (default is 0, which means no limit)
427
-    protected $filters = array(); ///< search filters
428
-    protected $group_by = ''; ///< group-by attribute name
429
-    protected $group_func = SPH_GROUPBY_DAY; ///< group-by function (to pre-process group-by attribute value with)
430
-    protected $group_sort = '@group desc'; ///< group-by sorting clause (to sort groups in result set with)
431
-    protected $group_distinct = ''; ///< group-by count-distinct attribute
432
-    protected $max_matches = 1000; ///< max matches to retrieve
433
-    protected $cutoff = 0; ///< cutoff to stop searching at (default is 0)
434
-    protected $retry_count = 0; ///< distributed retries count
435
-    protected $retry_delay = 0; ///< distributed retries delay
436
-    protected $anchor = array(); ///< geographical anchor point
437
-    protected $index_weights = array(); ///< per-index weights
438
-    protected $ranker = SPH_RANK_PROXIMITY_BM25; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
439
-    protected $rank_expr = ''; ///< ranking mode expression (for SPH_RANK_EXPR)
440
-    protected $max_query_time = 0; ///< max query time, milliseconds (default is 0, do not limit)
441
-    protected $field_weights = array(); ///< per-field-name weights
442
-    protected $overrides = array(); ///< per-query attribute values overrides
443
-    protected $select = '*'; ///< select-list (attributes or expressions, with optional aliases)
444
-    protected $query_flags = 0; ///< per-query various flags
445
-    protected $predicted_time = 0; ///< per-query max_predicted_time
446
-    protected $outer_order_by = ''; ///< outer match sort by
447
-    protected $outer_offset = 0; ///< outer offset
448
-    protected $outer_limit = 0; ///< outer limit
449
-    protected $has_outer = false;
450
-
451
-    protected $error = ''; ///< last error message
452
-    protected $warning = ''; ///< last warning message
453
-    protected $conn_error = false; ///< connection error vs remote error flag
454
-
455
-    protected $reqs = array(); ///< requests array for multi-query
456
-    protected $mbenc = ''; ///< stored mbstring encoding
457
-    protected $array_result = false; ///< whether $result['matches'] should be a hash or an array
458
-    protected $timeout = 0; ///< connect timeout
459
-
460
-    protected $path = false;
461
-    protected $socket = false;
462
-
463
-    /////////////////////////////////////////////////////////////////////////////
464
-    // common stuff
465
-    /////////////////////////////////////////////////////////////////////////////
466
-
467
-    public function __construct()
468
-    {
469
-        $this->query_flags = sphSetBit(0, 6, true); // default idf=tfidf_normalized
470
-    }
471
-
472
-    public function __destruct()
473
-    {
474
-        if ($this->socket !== false) {
475
-            fclose($this->socket);
476
-        }
477
-    }
478
-
479
-    /// get last error message (string)
480
-    public function getLastError()
481
-    {
482
-        return $this->error;
483
-    }
484
-
485
-    /// get last warning message (string)
486
-    public function getLastWarning()
487
-    {
488
-        return $this->warning;
489
-    }
490
-
491
-    /// get last error flag (to tell network connection errors from searchd errors or broken responses)
492
-    public function isConnectError()
493
-    {
494
-        return $this->conn_error;
495
-    }
496
-
497
-    /// set searchd host name (string) and port (integer)
498
-    public function setServer($host, $port = 0)
499
-    {
500
-        assert(is_string($host));
501
-        if ($host[0] == '/') {
502
-            $this->path = 'unix://' . $host;
503
-            return;
504
-        }
505
-        if (substr($host, 0, 7) == 'unix://') {
506
-            $this->path = $host;
507
-            return;
508
-        }
509
-
510
-        $this->host = $host;
511
-        $port = intval($port);
512
-        assert(0 <= $port && $port < 65536);
513
-        $this->port = $port == 0 ? 9312 : $port;
514
-        $this->path = '';
515
-    }
516
-
517
-    /// set server connection timeout (0 to remove)
518
-    public function setConnectTimeout($timeout)
519
-    {
520
-        assert(is_numeric($timeout));
521
-        $this->timeout = $timeout;
522
-    }
523
-
524
-
525
-    protected function send($handle, $data, $length)
526
-    {
527
-        if (feof($handle) || fwrite($handle, $data, $length) !== $length) {
528
-            $this->error = 'connection unexpectedly closed (timed out?)';
529
-            $this->conn_error = true;
530
-            return false;
531
-        }
532
-        return true;
533
-    }
534
-
535
-    /////////////////////////////////////////////////////////////////////////////
536
-
537
-    /// enter mbstring workaround mode
538
-    protected function mbPush()
539
-    {
540
-        $this->mbenc = '';
541
-        if (ini_get('mbstring.func_overload') & 2) {
542
-            $this->mbenc = mb_internal_encoding();
543
-            mb_internal_encoding('latin1');
544
-        }
545
-    }
546
-
547
-    /// leave mbstring workaround mode
548
-    protected function mbPop()
549
-    {
550
-        if ($this->mbenc) {
551
-            mb_internal_encoding($this->mbenc);
552
-        }
553
-    }
554
-
555
-    /// connect to searchd server
556
-    protected function connect()
557
-    {
558
-        if ($this->socket !== false) {
559
-            // we are in persistent connection mode, so we have a socket
560
-            // however, need to check whether it's still alive
561
-            if (!@feof($this->socket)) {
562
-                return $this->socket;
563
-            }
564
-
565
-            // force reopen
566
-            $this->socket = false;
567
-        }
568
-
569
-        $errno = 0;
570
-        $errstr = '';
571
-        $this->conn_error = false;
572
-
573
-        if ($this->path) {
574
-            $host = $this->path;
575
-            $port = 0;
576
-        } else {
577
-            $host = $this->host;
578
-            $port = $this->port;
579
-        }
580
-
581
-        if ($this->timeout <= 0) {
582
-            $fp = @fsockopen($host, $port, $errno, $errstr);
583
-        } else {
584
-            $fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout);
585
-        }
586
-
587
-        if (!$fp) {
588
-            if ($this->path) {
589
-                $location = $this->path;
590
-            } else {
591
-                $location = "{$this->host}:{$this->port}";
592
-            }
593
-
594
-            $errstr = trim($errstr);
595
-            $this->error = "connection to $location failed (errno=$errno, msg=$errstr)";
596
-            $this->conn_error = true;
597
-            return false;
598
-        }
599
-
600
-        // send my version
601
-        // this is a subtle part. we must do it before (!) reading back from searchd.
602
-        // because otherwise under some conditions (reported on FreeBSD for instance)
603
-        // TCP stack could throttle write-write-read pattern because of Nagle.
604
-        if (!$this->send($fp, pack('N', 1), 4)) {
605
-            fclose($fp);
606
-            $this->error = 'failed to send client protocol version';
607
-            return false;
608
-        }
609
-
610
-        // check version
611
-        list(, $v) = unpack('N*', fread($fp, 4));
612
-        $v = (int)$v;
613
-        if ($v < 1) {
614
-            fclose($fp);
615
-            $this->error = "expected searchd protocol version 1+, got version '$v'";
616
-            return false;
617
-        }
618
-
619
-        return $fp;
620
-    }
621
-
622
-    /// get and check response packet from searchd server
623
-    protected function getResponse($fp, $client_ver)
624
-    {
625
-        $response = '';
626
-        $len = 0;
627
-
628
-        $header = fread($fp, 8);
629
-        if (strlen($header) == 8) {
630
-            list($status, $ver, $len) = array_values(unpack('n2a/Nb', $header));
631
-            $left = $len;
632
-            while ($left > 0 && !feof($fp)) {
633
-                $chunk = fread($fp, min(8192, $left));
634
-                if ($chunk) {
635
-                    $response .= $chunk;
636
-                    $left -= strlen($chunk);
637
-                }
638
-            }
639
-        }
640
-        if ($this->socket === false) {
641
-            fclose($fp);
642
-        }
643
-
644
-        // check response
645
-        $read = strlen($response);
646
-        if (!$response || $read != $len) {
647
-            $this->error = $len
648
-                ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
649
-                : 'received zero-sized searchd response';
650
-            return false;
651
-        }
652
-
653
-        // check status
654
-        if ($status == SEARCHD_WARNING) {
655
-            list(, $wlen) = unpack('N*', substr($response, 0, 4));
656
-            $this->warning = substr($response, 4, $wlen);
657
-            return substr($response, 4 + $wlen);
658
-        }
659
-        if ($status == SEARCHD_ERROR) {
660
-            $this->error = 'searchd error: ' . substr($response, 4);
661
-            return false;
662
-        }
663
-        if ($status == SEARCHD_RETRY) {
664
-            $this->error = 'temporary searchd error: ' . substr($response, 4);
665
-            return false;
666
-        }
667
-        if ($status != SEARCHD_OK) {
668
-            $this->error = "unknown status code '$status'";
669
-            return false;
670
-        }
671
-
672
-        // check version
673
-        if ($ver < $client_ver) {
674
-            $this->warning = sprintf(
675
-                'searchd command v.%d.%d older than client\'s v.%d.%d, some options might not work',
676
-                $ver >> 8,
677
-                $ver & 0xff,
678
-                $client_ver >> 8,
679
-                $client_ver & 0xff
680
-            );
681
-        }
682
-
683
-        return $response;
684
-    }
685
-
686
-    /////////////////////////////////////////////////////////////////////////////
687
-    // searching
688
-    /////////////////////////////////////////////////////////////////////////////
689
-
690
-    /// set offset and count into result set,
691
-    /// and optionally set max-matches and cutoff limits
692
-    public function setLimits($offset, $limit, $max = 0, $cutoff = 0)
693
-    {
694
-        assert(is_int($offset));
695
-        assert(is_int($limit));
696
-        assert($offset >= 0);
697
-        assert($limit > 0);
698
-        assert($max >= 0);
699
-        $this->offset = $offset;
700
-        $this->limit = $limit;
701
-        if ($max > 0) {
702
-            $this->max_matches = $max;
703
-        }
704
-        if ($cutoff > 0) {
705
-            $this->cutoff = $cutoff;
706
-        }
707
-    }
708
-
709
-    /// set maximum query time, in milliseconds, per-index
710
-    /// integer, 0 means 'do not limit'
711
-    public function setMaxQueryTime($max)
712
-    {
713
-        assert(is_int($max));
714
-        assert($max >= 0);
715
-        $this->max_query_time = $max;
716
-    }
717
-
718
-    /// set matching mode
719
-    public function setMatchMode($mode)
720
-    {
721
-        trigger_error(
722
-            'DEPRECATED: Do not call this method or, even better, use SphinxQL instead of an API',
723
-            E_USER_DEPRECATED
724
-        );
725
-        assert(
726
-            $mode == SPH_MATCH_ALL ||
727
-            $mode == SPH_MATCH_ANY ||
728
-            $mode == SPH_MATCH_PHRASE ||
729
-            $mode == SPH_MATCH_BOOLEAN ||
730
-            $mode == SPH_MATCH_EXTENDED ||
731
-            $mode == SPH_MATCH_FULLSCAN ||
732
-            $mode == SPH_MATCH_EXTENDED2
733
-        );
734
-        $this->mode = $mode;
735
-    }
736
-
737
-    /// set ranking mode
738
-    public function setRankingMode($ranker, $rankexpr='')
739
-    {
740
-        assert($ranker === 0 || $ranker >= 1 && $ranker < SPH_RANK_TOTAL);
741
-        assert(is_string($rankexpr));
742
-        $this->ranker = $ranker;
743
-        $this->rank_expr = $rankexpr;
744
-    }
745
-
746
-    /// set matches sorting mode
747
-    public function setSortMode($mode, $sortby = '')
748
-    {
749
-        assert (
750
-            $mode == SPH_SORT_RELEVANCE ||
751
-            $mode == SPH_SORT_ATTR_DESC ||
752
-            $mode == SPH_SORT_ATTR_ASC ||
753
-            $mode == SPH_SORT_TIME_SEGMENTS ||
754
-            $mode == SPH_SORT_EXTENDED ||
755
-            $mode == SPH_SORT_EXPR
756
-        );
757
-        assert(is_string($sortby));
758
-        assert($mode == SPH_SORT_RELEVANCE || strlen($sortby) > 0);
759
-
760
-        $this->sort = $mode;
761
-        $this->sort_by = $sortby;
762
-    }
763
-
764
-    /**
765
-     * Bind per-field weights by order
766
-     *
767
-     * @deprecated use setFieldWeights() instead
768
-     */
769
-    public function setWeights()
770
-    {
771
-        throw new \RuntimeException('This method is now deprecated; please use setFieldWeights instead');
772
-    }
773
-
774
-    /// bind per-field weights by name
775
-    public function setFieldWeights($weights)
776
-    {
777
-        assert(is_array($weights));
778
-        foreach ($weights as $name => $weight) {
779
-            assert(is_string($name));
780
-            assert(is_int($weight));
781
-        }
782
-        $this->field_weights = $weights;
783
-    }
784
-
785
-    /// bind per-index weights by name
786
-    public function setIndexWeights($weights)
787
-    {
788
-        assert(is_array($weights));
789
-        foreach ($weights as $index => $weight) {
790
-            assert(is_string($index));
791
-            assert(is_int($weight));
792
-        }
793
-        $this->index_weights = $weights;
794
-    }
795
-
796
-    /// set IDs range to match
797
-    /// only match records if document ID is beetwen $min and $max (inclusive)
798
-    public function setIDRange($min, $max)
799
-    {
800
-        assert(is_numeric($min));
801
-        assert(is_numeric($max));
802
-        assert($min <= $max);
803
-        $this->min_id = $min;
804
-        $this->max_id = $max;
805
-    }
806
-
807
-    /// set values set filter
808
-    /// only match records where $attribute value is in given set
809
-    public function setFilter($attribute, $values, $exclude = false)
810
-    {
811
-        assert(is_string($attribute));
812
-        assert(is_array($values));
813
-        assert(count($values));
814
-
815
-        if (is_array($values) && count($values)) {
816
-            foreach ($values as $value) {
817
-                assert(is_numeric($value));
818
-            }
819
-
820
-            $this->filters[] = array(
821
-                'type' => SPH_FILTER_VALUES,
822
-                'attr' => $attribute,
823
-                'exclude' => $exclude,
824
-                'values' => $values
825
-            );
826
-        }
827
-    }
828
-
829
-    /// set string filter
830
-    /// only match records where $attribute value is equal
831
-    public function setFilterString($attribute, $value, $exclude = false)
832
-    {
833
-        assert(is_string($attribute));
834
-        assert(is_string($value));
835
-        $this->filters[] = array(
836
-            'type' => SPH_FILTER_STRING,
837
-            'attr' => $attribute,
838
-            'exclude' => $exclude,
839
-            'value' => $value
840
-        );
841
-    }    
842
-
843
-    /// set range filter
844
-    /// only match records if $attribute value is beetwen $min and $max (inclusive)
845
-    public function setFilterRange($attribute, $min, $max, $exclude = false)
846
-    {
847
-        assert(is_string($attribute));
848
-        assert(is_numeric($min));
849
-        assert(is_numeric($max));
850
-        assert($min <= $max);
851
-
852
-        $this->filters[] = array(
853
-            'type' => SPH_FILTER_RANGE,
854
-            'attr' => $attribute,
855
-            'exclude' => $exclude,
856
-            'min' => $min,
857
-            'max' => $max
858
-        );
859
-    }
860
-
861
-    /// set float range filter
862
-    /// only match records if $attribute value is beetwen $min and $max (inclusive)
863
-    public function setFilterFloatRange($attribute, $min, $max, $exclude = false)
864
-    {
865
-        assert(is_string($attribute));
866
-        assert(is_float($min));
867
-        assert(is_float($max));
868
-        assert($min <= $max);
869
-
870
-        $this->filters[] = array(
871
-            'type' => SPH_FILTER_FLOATRANGE,
872
-            'attr' => $attribute,
873
-            'exclude' => $exclude,
874
-            'min' => $min,
875
-            'max' => $max
876
-        );
877
-    }
878
-
879
-    /// setup anchor point for geosphere distance calculations
880
-    /// required to use @geodist in filters and sorting
881
-    /// latitude and longitude must be in radians
882
-    public function setGeoAnchor($attrlat, $attrlong, $lat, $long)
883
-    {
884
-        assert(is_string($attrlat));
885
-        assert(is_string($attrlong));
886
-        assert(is_float($lat));
887
-        assert(is_float($long));
888
-
889
-        $this->anchor = array(
890
-            'attrlat' => $attrlat,
891
-            'attrlong' => $attrlong,
892
-            'lat' => $lat,
893
-            'long' => $long
894
-        );
895
-    }
896
-
897
-    /// set grouping attribute and function
898
-    public function setGroupBy($attribute, $func, $groupsort = '@group desc')
899
-    {
900
-        assert(is_string($attribute));
901
-        assert(is_string($groupsort));
902
-        assert(
903
-            $func == SPH_GROUPBY_DAY ||
904
-            $func == SPH_GROUPBY_WEEK ||
905
-            $func == SPH_GROUPBY_MONTH ||
906
-            $func == SPH_GROUPBY_YEAR ||
907
-            $func == SPH_GROUPBY_ATTR ||
908
-            $func == SPH_GROUPBY_ATTRPAIR
909
-        );
910
-
911
-        $this->group_by = $attribute;
912
-        $this->group_func = $func;
913
-        $this->group_sort = $groupsort;
914
-    }
915
-
916
-    /// set count-distinct attribute for group-by queries
917
-    public function setGroupDistinct($attribute)
918
-    {
919
-        assert(is_string($attribute));
920
-        $this->group_distinct = $attribute;
921
-    }
922
-
923
-    /// set distributed retries count and delay
924
-    public function setRetries($count, $delay = 0)
925
-    {
926
-        assert(is_int($count) && $count >= 0);
927
-        assert(is_int($delay) && $delay >= 0);
928
-        $this->retry_count = $count;
929
-        $this->retry_delay = $delay;
930
-    }
931
-
932
-    /// set result set format (hash or array; hash by default)
933
-    /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
934
-    public function setArrayResult($arrayresult)
935
-    {
936
-        assert(is_bool($arrayresult));
937
-        $this->array_result = $arrayresult;
938
-    }
939
-
940
-    /// set attribute values override
941
-    /// there can be only one override per attribute
942
-    /// $values must be a hash that maps document IDs to attribute values
943
-    public function setOverride($attrname, $attrtype, $values)
944
-    {
945
-        trigger_error(
946
-            'DEPRECATED: Do not call this method. Use SphinxQL REMAP() function instead.',
947
-            E_USER_DEPRECATED
948
-        );
949
-        assert(is_string($attrname));
950
-        assert(in_array($attrtype, array(
951
-            SPH_ATTR_INTEGER,
952
-            SPH_ATTR_TIMESTAMP,
953
-            SPH_ATTR_BOOL,
954
-            SPH_ATTR_FLOAT,
955
-            SPH_ATTR_BIGINT
956
-        )));
957
-        assert(is_array($values));
958
-
959
-        $this->overrides[$attrname] = array(
960
-            'attr' => $attrname,
961
-            'type' => $attrtype,
962
-            'values' => $values
963
-        );
964
-    }
965
-
966
-    /// set select-list (attributes or expressions), SQL-like syntax
967
-    public function setSelect($select)
968
-    {
969
-        assert(is_string($select));
970
-        $this->select = $select;
971
-    }
972
-
973
-    public function setQueryFlag($flag_name, $flag_value)
974
-    {
975
-        $known_names = array(
976
-            'reverse_scan',
977
-            'sort_method',
978
-            'max_predicted_time',
979
-            'boolean_simplify',
980
-            'idf',
981
-            'global_idf',
982
-            'low_priority'
983
-        );
984
-        $flags = array (
985
-            'reverse_scan' => array(0, 1),
986
-            'sort_method' => array('pq', 'kbuffer'),
987
-            'max_predicted_time' => array(0),
988
-            'boolean_simplify' => array(true, false),
989
-            'idf' => array ('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
990
-            'global_idf' => array(true, false),
991
-            'low_priority' => array(true, false)
992
-        );
993
-
994
-        assert(isset($flag_name, $known_names));
995
-        assert(
996
-            in_array($flag_value, $flags[$flag_name], true) ||
997
-            ($flag_name == 'max_predicted_time' && is_int($flag_value) && $flag_value >= 0)
998
-        );
999
-
1000
-        if ($flag_name == 'reverse_scan') {
1001
-            $this->query_flags = sphSetBit($this->query_flags, 0, $flag_value == 1);
1002
-        }
1003
-        if ($flag_name == 'sort_method') {
1004
-            $this->query_flags = sphSetBit($this->query_flags, 1, $flag_value == 'kbuffer');
1005
-        }
1006
-        if ($flag_name == 'max_predicted_time') {
1007
-            $this->query_flags = sphSetBit($this->query_flags, 2, $flag_value > 0);
1008
-            $this->predicted_time = (int)$flag_value;
1009
-        }
1010
-        if ($flag_name == 'boolean_simplify') {
1011
-            $this->query_flags = sphSetBit($this->query_flags, 3, $flag_value);
1012
-        }
1013
-        if ($flag_name == 'idf' && ($flag_value == 'normalized' || $flag_value == 'plain')) {
1014
-            $this->query_flags = sphSetBit($this->query_flags, 4, $flag_value == 'plain');
1015
-        }
1016
-        if ($flag_name == 'global_idf') {
1017
-            $this->query_flags = sphSetBit($this->query_flags, 5, $flag_value);
1018
-        }
1019
-        if ($flag_name == 'idf' && ($flag_value == 'tfidf_normalized' || $flag_value == 'tfidf_unnormalized')) {
1020
-            $this->query_flags = sphSetBit($this->query_flags, 6, $flag_value == 'tfidf_normalized');
1021
-        }
1022
-        if ($flag_name == 'low_priority') {
1023
-            $this->query_flags = sphSetBit($this->query_flags, 8, $flag_value);
1024
-        }
1025
-    }
1026
-
1027
-    /// set outer order by parameters
1028
-    public function setOuterSelect($orderby, $offset, $limit)
1029
-    {
1030
-        assert(is_string($orderby));
1031
-        assert(is_int($offset));
1032
-        assert(is_int($limit));
1033
-        assert($offset >= 0);
1034
-        assert($limit > 0);
1035
-
1036
-        $this->outer_order_by = $orderby;
1037
-        $this->outer_offset = $offset;
1038
-        $this->outer_limit = $limit;
1039
-        $this->has_outer = true;
1040
-    }
1041
-
1042
-
1043
-    //////////////////////////////////////////////////////////////////////////////
1044
-
1045
-    /// clear all filters (for multi-queries)
1046
-    public function resetFilters()
1047
-    {
1048
-        $this->filters = array();
1049
-        $this->anchor = array();
1050
-    }
1051
-
1052
-    /// clear groupby settings (for multi-queries)
1053
-    public function resetGroupBy()
1054
-    {
1055
-        $this->group_by = '';
1056
-        $this->group_func = SPH_GROUPBY_DAY;
1057
-        $this->group_sort = '@group desc';
1058
-        $this->group_distinct = '';
1059
-    }
1060
-
1061
-    /// clear all attribute value overrides (for multi-queries)
1062
-    public function resetOverrides()
1063
-    {
1064
-        $this->overrides = array();
1065
-    }
1066
-
1067
-    public function resetQueryFlag()
1068
-    {
1069
-        $this->query_flags = sphSetBit(0, 6, true); // default idf=tfidf_normalized
1070
-        $this->predicted_time = 0;
1071
-    }
1072
-
1073
-    public function resetOuterSelect()
1074
-    {
1075
-        $this->outer_order_by = '';
1076
-        $this->outer_offset = 0;
1077
-        $this->outer_limit = 0;
1078
-        $this->has_outer = false;
1079
-    }
1080
-
1081
-    //////////////////////////////////////////////////////////////////////////////
1082
-
1083
-    /// connect to searchd server, run given search query through given indexes,
1084
-    /// and return the search results
1085
-    public function query($query, $index = '*', $comment = '')
1086
-    {
1087
-        assert(empty($this->reqs));
1088
-
1089
-        $this->addQuery($query, $index, $comment);
1090
-        $results = $this->runQueries();
1091
-        $this->reqs = array(); // just in case it failed too early
1092
-
1093
-        if (!is_array($results)) {
1094
-            return false; // probably network error; error message should be already filled
1095
-        }
1096
-
1097
-        $this->error = $results[0]['error'];
1098
-        $this->warning = $results[0]['warning'];
1099
-        if ($results[0]['status'] == SEARCHD_ERROR) {
1100
-            return false;
1101
-        } else {
1102
-            return $results[0];
1103
-        }
1104
-    }
1105
-
1106
-    /// helper to pack floats in network byte order
1107
-    protected function packFloat($f)
1108
-    {
1109
-        $t1 = pack('f', $f); // machine order
1110
-        list(, $t2) = unpack('L*', $t1); // int in machine order
1111
-        return pack('N', $t2);
1112
-    }
1113
-
1114
-    /// add query to multi-query batch
1115
-    /// returns index into results array from RunQueries() call
1116
-    public function addQuery($query, $index = '*', $comment = '')
1117
-    {
1118
-        // mbstring workaround
1119
-        $this->mbPush();
1120
-
1121
-        // build request
1122
-        $req = pack('NNNNN', $this->query_flags, $this->offset, $this->limit, $this->mode, $this->ranker);
1123
-        if ($this->ranker == SPH_RANK_EXPR) {
1124
-            $req .= pack('N', strlen($this->rank_expr)) . $this->rank_expr;
1125
-        }
1126
-        $req .= pack('N', $this->sort); // (deprecated) sort mode
1127
-        $req .= pack('N', strlen($this->sort_by)) . $this->sort_by;
1128
-        $req .= pack('N', strlen($query)) . $query; // query itself
1129
-        $req .= pack('N', count($this->weights)); // weights
1130
-        foreach ($this->weights as $weight) {
1131
-            $req .= pack('N', (int)$weight);
1132
-        }
1133
-        $req .= pack('N', strlen($index)) . $index; // indexes
1134
-        $req .= pack('N', 1); // id64 range marker
1135
-        $req .= sphPackU64($this->min_id) . sphPackU64($this->max_id); // id64 range
1136
-
1137
-        // filters
1138
-        $req .= pack('N', count($this->filters));
1139
-        foreach ($this->filters as $filter) {
1140
-            $req .= pack('N', strlen($filter['attr'])) . $filter['attr'];
1141
-            $req .= pack('N', $filter['type']);
1142
-            switch ($filter['type']) {
1143
-                case SPH_FILTER_VALUES:
1144
-                    $req .= pack('N', count($filter['values']));
1145
-                    foreach ($filter['values'] as $value) {
1146
-                        $req .= sphPackI64($value);
1147
-                    }
1148
-                    break;
1149
-                case SPH_FILTER_RANGE:
1150
-                    $req .= sphPackI64($filter['min']) . sphPackI64($filter['max']);
1151
-                    break;
1152
-                case SPH_FILTER_FLOATRANGE:
1153
-                    $req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']);
1154
-                    break;
1155
-                case SPH_FILTER_STRING:
1156
-                    $req .= pack('N', strlen($filter['value'])) . $filter['value'];
1157
-                    break;
1158
-                default:
1159
-                    assert(0 && 'internal error: unhandled filter type');
1160
-            }
1161
-            $req .= pack('N', $filter['exclude']);
1162
-        }
1163
-
1164
-        // group-by clause, max-matches count, group-sort clause, cutoff count
1165
-        $req .= pack('NN', $this->group_func, strlen($this->group_by)) . $this->group_by;
1166
-        $req .= pack('N', $this->max_matches);
1167
-        $req .= pack('N', strlen($this->group_sort)) . $this->group_sort;
1168
-        $req .= pack('NNN', $this->cutoff, $this->retry_count, $this->retry_delay);
1169
-        $req .= pack('N', strlen($this->group_distinct)) . $this->group_distinct;
1170
-
1171
-        // anchor point
1172
-        if (empty($this->anchor)) {
1173
-            $req .= pack('N', 0);
1174
-        } else {
1175
-            $a =& $this->anchor;
1176
-            $req .= pack('N', 1);
1177
-            $req .= pack('N', strlen($a['attrlat'])) . $a['attrlat'];
1178
-            $req .= pack('N', strlen($a['attrlong'])) . $a['attrlong'];
1179
-            $req .= $this->packFloat($a['lat']) . $this->packFloat($a['long']);
1180
-        }
1181
-
1182
-        // per-index weights
1183
-        $req .= pack('N', count($this->index_weights));
1184
-        foreach ($this->index_weights as $idx => $weight) {
1185
-            $req .= pack('N', strlen($idx)) . $idx . pack('N', $weight);
1186
-        }
1187
-
1188
-        // max query time
1189
-        $req .= pack('N', $this->max_query_time);
1190
-
1191
-        // per-field weights
1192
-        $req .= pack('N', count($this->field_weights));
1193
-        foreach ($this->field_weights as $field => $weight) {
1194
-            $req .= pack('N', strlen($field)) . $field . pack('N', $weight);
1195
-        }
1196
-
1197
-        // comment
1198
-        $req .= pack('N', strlen($comment)) . $comment;
1199
-
1200
-        // attribute overrides
1201
-        $req .= pack('N', count($this->overrides));
1202
-        foreach ($this->overrides as $key => $entry) {
1203
-            $req .= pack('N', strlen($entry['attr'])) . $entry['attr'];
1204
-            $req .= pack('NN', $entry['type'], count($entry['values']));
1205
-            foreach ($entry['values'] as $id => $val) {
1206
-                assert(is_numeric($id));
1207
-                assert(is_numeric($val));
1208
-
1209
-                $req .= sphPackU64($id);
1210
-                switch ($entry['type']) {
1211
-                    case SPH_ATTR_FLOAT:
1212
-                        $req .= $this->packFloat($val);
1213
-                        break;
1214
-                    case SPH_ATTR_BIGINT:
1215
-                        $req .= sphPackI64($val);
1216
-                        break;
1217
-                    default:
1218
-                        $req .= pack('N', $val);
1219
-                        break;
1220
-                }
1221
-            }
1222
-        }
1223
-
1224
-        // select-list
1225
-        $req .= pack('N', strlen($this->select)) . $this->select;
1226
-
1227
-        // max_predicted_time
1228
-        if ($this->predicted_time > 0) {
1229
-            $req .= pack('N', (int)$this->predicted_time);
1230
-        }
1231
-
1232
-        $req .= pack('N', strlen($this->outer_order_by)) . $this->outer_order_by;
1233
-        $req .= pack('NN', $this->outer_offset, $this->outer_limit);
1234
-        if ($this->has_outer) {
1235
-            $req .= pack('N', 1);
1236
-        } else {
1237
-            $req .= pack('N', 0);
1238
-        }
1239
-
1240
-        // mbstring workaround
1241
-        $this->mbPop();
1242
-
1243
-        // store request to requests array
1244
-        $this->reqs[] = $req;
1245
-        return count($this->reqs) - 1;
1246
-    }
1247
-
1248
-    /// connect to searchd, run queries batch, and return an array of result sets
1249
-    public function runQueries()
1250
-    {
1251
-        if (empty($this->reqs)) {
1252
-            $this->error = 'no queries defined, issue AddQuery() first';
1253
-            return false;
1254
-        }
1255
-
1256
-        // mbstring workaround
1257
-        $this->mbPush();
1258
-
1259
-        if (!($fp = $this->connect())) {
1260
-            $this->mbPop();
1261
-            return false;
1262
-        }
1263
-
1264
-        // send query, get response
1265
-        $nreqs = count($this->reqs);
1266
-        $req = join('', $this->reqs);
1267
-        $len = 8 + strlen($req);
1268
-        $req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs) . $req; // add header
1269
-
1270
-        if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_SEARCH))) {
1271
-            $this->mbPop();
1272
-            return false;
1273
-        }
1274
-
1275
-        // query sent ok; we can reset reqs now
1276
-        $this->reqs = array();
1277
-
1278
-        // parse and return response
1279
-        return $this->parseSearchResponse($response, $nreqs);
1280
-    }
1281
-
1282
-    /// parse and return search query (or queries) response
1283
-    protected function parseSearchResponse($response, $nreqs)
1284
-    {
1285
-        $p = 0; // current position
1286
-        $max = strlen($response); // max position for checks, to protect against broken responses
1287
-
1288
-        $results = array();
1289
-        for ($ires = 0; $ires < $nreqs && $p < $max; $ires++) {
1290
-            $results[] = array();
1291
-            $result =& $results[$ires];
1292
-
1293
-            $result['error'] = '';
1294
-            $result['warning'] = '';
1295
-
1296
-            // extract status
1297
-            list(, $status) = unpack('N*', substr($response, $p, 4));
1298
-            $p += 4;
1299
-            $result['status'] = $status;
1300
-            if ($status != SEARCHD_OK) {
1301
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1302
-                $p += 4;
1303
-                $message = substr($response, $p, $len);
1304
-                $p += $len;
1305
-
1306
-                if ($status == SEARCHD_WARNING) {
1307
-                    $result['warning'] = $message;
1308
-                } else {
1309
-                    $result['error'] = $message;
1310
-                    continue;
1311
-                }
1312
-            }
1313
-
1314
-            // read schema
1315
-            $fields = array();
1316
-            $attrs = array();
1317
-
1318
-            list(, $nfields) = unpack('N*', substr($response, $p, 4));
1319
-            $p += 4;
1320
-            while ($nfields --> 0 && $p < $max) {
1321
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1322
-                $p += 4;
1323
-                $fields[] = substr($response, $p, $len);
1324
-                $p += $len;
1325
-            }
1326
-            $result['fields'] = $fields;
1327
-
1328
-            list(, $nattrs) = unpack('N*', substr($response, $p, 4));
1329
-            $p += 4;
1330
-            while ($nattrs --> 0 && $p < $max) {
1331
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1332
-                $p += 4;
1333
-                $attr = substr($response, $p, $len);
1334
-                $p += $len;
1335
-                list(, $type) = unpack('N*', substr($response, $p, 4));
1336
-                $p += 4;
1337
-                $attrs[$attr] = $type;
1338
-            }
1339
-            $result['attrs'] = $attrs;
1340
-
1341
-            // read match count
1342
-            list(, $count) = unpack('N*', substr($response, $p, 4));
1343
-            $p += 4;
1344
-            list(, $id64) = unpack('N*', substr($response, $p, 4));
1345
-            $p += 4;
1346
-
1347
-            // read matches
1348
-            $idx = -1;
1349
-            while ($count --> 0 && $p < $max) {
1350
-                // index into result array
1351
-                $idx++;
1352
-
1353
-                // parse document id and weight
1354
-                if ($id64) {
1355
-                    $doc = sphUnpackU64(substr($response, $p, 8));
1356
-                    $p += 8;
1357
-                    list(,$weight) = unpack('N*', substr($response, $p, 4));
1358
-                    $p += 4;
1359
-                } else {
1360
-                    list($doc, $weight) = array_values(unpack('N*N*', substr($response, $p, 8)));
1361
-                    $p += 8;
1362
-                    $doc = sphFixUint($doc);
1363
-                }
1364
-                $weight = sprintf('%u', $weight);
1365
-
1366
-                // create match entry
1367
-                if ($this->array_result) {
1368
-                    $result['matches'][$idx] = array('id' => $doc, 'weight' => $weight);
1369
-                } else {
1370
-                    $result['matches'][$doc]['weight'] = $weight;
1371
-                }
1372
-
1373
-                // parse and create attributes
1374
-                $attrvals = array();
1375
-                foreach ($attrs as $attr => $type) {
1376
-                    // handle 64bit ints
1377
-                    if ($type == SPH_ATTR_BIGINT) {
1378
-                        $attrvals[$attr] = sphUnpackI64(substr($response, $p, 8));
1379
-                        $p += 8;
1380
-                        continue;
1381
-                    }
1382
-
1383
-                    // handle floats
1384
-                    if ($type == SPH_ATTR_FLOAT) {
1385
-                        list(, $uval) = unpack('N*', substr($response, $p, 4));
1386
-                        $p += 4;
1387
-                        list(, $fval) = unpack('f*', pack('L', $uval));
1388
-                        $attrvals[$attr] = $fval;
1389
-                        continue;
1390
-                    }
1391
-
1392
-                    // handle everything else as unsigned ints
1393
-                    list(, $val) = unpack('N*', substr($response, $p, 4));
1394
-                    $p += 4;
1395
-                    if ($type == SPH_ATTR_MULTI) {
1396
-                        $attrvals[$attr] = array();
1397
-                        $nvalues = $val;
1398
-                        while ($nvalues --> 0 && $p < $max) {
1399
-                            list(, $val) = unpack('N*', substr($response, $p, 4));
1400
-                            $p += 4;
1401
-                            $attrvals[$attr][] = sphFixUint($val);
1402
-                        }
1403
-                    } elseif ($type == SPH_ATTR_MULTI64) {
1404
-                        $attrvals[$attr] = array();
1405
-                        $nvalues = $val;
1406
-                        while ($nvalues > 0 && $p < $max) {
1407
-                            $attrvals[$attr][] = sphUnpackI64(substr($response, $p, 8));
1408
-                            $p += 8;
1409
-                            $nvalues -= 2;
1410
-                        }
1411
-                    } elseif ($type == SPH_ATTR_STRING) {
1412
-                        $attrvals[$attr] = substr($response, $p, $val);
1413
-                        $p += $val;
1414
-                    } elseif ($type == SPH_ATTR_FACTORS) {
1415
-                        $attrvals[$attr] = substr($response, $p, $val - 4);
1416
-                        $p += $val-4;
1417
-                    } else {
1418
-                        $attrvals[$attr] = sphFixUint($val);
1419
-                    }
1420
-                }
1421
-
1422
-                if ($this->array_result) {
1423
-                    $result['matches'][$idx]['attrs'] = $attrvals;
1424
-                } else {
1425
-                    $result['matches'][$doc]['attrs'] = $attrvals;
1426
-                }
1427
-            }
1428
-
1429
-            list($total, $total_found, $msecs, $words) = array_values(unpack('N*N*N*N*', substr($response, $p, 16)));
1430
-            $result['total'] = sprintf('%u', $total);
1431
-            $result['total_found'] = sprintf('%u', $total_found);
1432
-            $result['time'] = sprintf('%.3f', $msecs / 1000);
1433
-            $p += 16;
1434
-
1435
-            while ($words --> 0 && $p < $max) {
1436
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1437
-                $p += 4;
1438
-                $word = substr($response, $p, $len);
1439
-                $p += $len;
1440
-                list($docs, $hits) = array_values(unpack('N*N*', substr($response, $p, 8)));
1441
-                $p += 8;
1442
-                $result['words'][$word] = array (
1443
-                    'docs' => sprintf('%u', $docs),
1444
-                    'hits' => sprintf('%u', $hits)
1445
-                );
1446
-            }
1447
-        }
1448
-
1449
-        $this->mbPop();
1450
-        return $results;
1451
-    }
1452
-
1453
-    /////////////////////////////////////////////////////////////////////////////
1454
-    // excerpts generation
1455
-    /////////////////////////////////////////////////////////////////////////////
1456
-
1457
-    /// connect to searchd server, and generate exceprts (snippets)
1458
-    /// of given documents for given query. returns false on failure,
1459
-    /// an array of snippets on success
1460
-    public function buildExcerpts($docs, $index, $words, $opts = array())
1461
-    {
1462
-        assert(is_array($docs));
1463
-        assert(is_string($index));
1464
-        assert(is_string($words));
1465
-        assert(is_array($opts));
1466
-
1467
-        $this->mbPush();
1468
-
1469
-        if (!($fp = $this->connect())) {
1470
-            $this->mbPop();
1471
-            return false;
1472
-        }
1473
-
1474
-        /////////////////
1475
-        // fixup options
1476
-        /////////////////
1477
-
1478
-        if (!isset($opts['before_match'])) {
1479
-            $opts['before_match'] = '<b>';
1480
-        }
1481
-        if (!isset($opts['after_match'])) {
1482
-            $opts['after_match'] = '</b>';
1483
-        }
1484
-        if (!isset($opts['chunk_separator'])) {
1485
-            $opts['chunk_separator'] = ' ... ';
1486
-        }
1487
-        if (!isset($opts['limit'])) {
1488
-            $opts['limit'] = 256;
1489
-        }
1490
-        if (!isset($opts['limit_passages'])) {
1491
-            $opts['limit_passages'] = 0;
1492
-        }
1493
-        if (!isset($opts['limit_words'])) {
1494
-            $opts['limit_words'] = 0;
1495
-        }
1496
-        if (!isset($opts['around'])) {
1497
-            $opts['around'] = 5;
1498
-        }
1499
-        if (!isset($opts['exact_phrase'])) {
1500
-            $opts['exact_phrase'] = false;
1501
-        }
1502
-        if (!isset($opts['single_passage'])) {
1503
-            $opts['single_passage'] = false;
1504
-        }
1505
-        if (!isset($opts['use_boundaries'])) {
1506
-            $opts['use_boundaries'] = false;
1507
-        }
1508
-        if (!isset($opts['weight_order'])) {
1509
-            $opts['weight_order'] = false;
1510
-        }
1511
-        if (!isset($opts['query_mode'])) {
1512
-            $opts['query_mode'] = false;
1513
-        }
1514
-        if (!isset($opts['force_all_words'])) {
1515
-            $opts['force_all_words'] = false;
1516
-        }
1517
-        if (!isset($opts['start_passage_id'])) {
1518
-            $opts['start_passage_id'] = 1;
1519
-        }
1520
-        if (!isset($opts['load_files'])) {
1521
-            $opts['load_files'] = false;
1522
-        }
1523
-        if (!isset($opts['html_strip_mode'])) {
1524
-            $opts['html_strip_mode'] = 'index';
1525
-        }
1526
-        if (!isset($opts['allow_empty'])) {
1527
-            $opts['allow_empty'] = false;
1528
-        }
1529
-        if (!isset($opts['passage_boundary'])) {
1530
-            $opts['passage_boundary'] = 'none';
1531
-        }
1532
-        if (!isset($opts['emit_zones'])) {
1533
-            $opts['emit_zones'] = false;
1534
-        }
1535
-        if (!isset($opts['load_files_scattered'])) {
1536
-            $opts['load_files_scattered'] = false;
1537
-        }
1538
-
1539
-
1540
-        /////////////////
1541
-        // build request
1542
-        /////////////////
1543
-
1544
-        // v.1.2 req
1545
-        $flags = 1; // remove spaces
1546
-        if ($opts['exact_phrase']) {
1547
-            $flags |= 2;
1548
-        }
1549
-        if ($opts['single_passage']) {
1550
-            $flags |= 4;
1551
-        }
1552
-        if ($opts['use_boundaries']) {
1553
-            $flags |= 8;
1554
-        }
1555
-        if ($opts['weight_order']) {
1556
-            $flags |= 16;
1557
-        }
1558
-        if ($opts['query_mode']) {
1559
-            $flags |= 32;
1560
-        }
1561
-        if ($opts['force_all_words']) {
1562
-            $flags |= 64;
1563
-        }
1564
-        if ($opts['load_files']) {
1565
-            $flags |= 128;
1566
-        }
1567
-        if ($opts['allow_empty']) {
1568
-            $flags |= 256;
1569
-        }
1570
-        if ($opts['emit_zones']) {
1571
-            $flags |= 512;
1572
-        }
1573
-        if ($opts['load_files_scattered']) {
1574
-            $flags |= 1024;
1575
-        }
1576
-        $req = pack('NN', 0, $flags); // mode=0, flags=$flags
1577
-        $req .= pack('N', strlen($index)) . $index; // req index
1578
-        $req .= pack('N', strlen($words)) . $words; // req words
1579
-
1580
-        // options
1581
-        $req .= pack('N', strlen($opts['before_match'])) . $opts['before_match'];
1582
-        $req .= pack('N', strlen($opts['after_match'])) . $opts['after_match'];
1583
-        $req .= pack('N', strlen($opts['chunk_separator'])) . $opts['chunk_separator'];
1584
-        $req .= pack('NN', (int)$opts['limit'], (int)$opts['around']);
1585
-        $req .= pack('NNN', (int)$opts['limit_passages'], (int)$opts['limit_words'], (int)$opts['start_passage_id']); // v.1.2
1586
-        $req .= pack('N', strlen($opts['html_strip_mode'])) . $opts['html_strip_mode'];
1587
-        $req .= pack('N', strlen($opts['passage_boundary'])) . $opts['passage_boundary'];
1588
-
1589
-        // documents
1590
-        $req .= pack('N', count($docs));
1591
-        foreach ($docs as $doc) {
1592
-            assert(is_string($doc));
1593
-            $req .= pack('N', strlen($doc)) . $doc;
1594
-        }
1595
-
1596
-        ////////////////////////////
1597
-        // send query, get response
1598
-        ////////////////////////////
1599
-
1600
-        $len = strlen($req);
1601
-        $req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len) . $req; // add header
1602
-        if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_EXCERPT))) {
1603
-            $this->mbPop();
1604
-            return false;
1605
-        }
1606
-
1607
-        //////////////////
1608
-        // parse response
1609
-        //////////////////
1610
-
1611
-        $pos = 0;
1612
-        $res = array();
1613
-        $rlen = strlen($response);
1614
-        $count = count($docs);
1615
-        while ($count--) {
1616
-            list(, $len) = unpack('N*', substr($response, $pos, 4));
1617
-            $pos += 4;
1618
-
1619
-            if ($pos + $len > $rlen) {
1620
-                $this->error = 'incomplete reply';
1621
-                $this->mbPop();
1622
-                return false;
1623
-            }
1624
-            $res[] = $len ? substr($response, $pos, $len) : '';
1625
-            $pos += $len;
1626
-        }
1627
-
1628
-        $this->mbPop();
1629
-        return $res;
1630
-    }
1631
-
1632
-
1633
-    /////////////////////////////////////////////////////////////////////////////
1634
-    // keyword generation
1635
-    /////////////////////////////////////////////////////////////////////////////
1636
-
1637
-    /// connect to searchd server, and generate keyword list for a given query
1638
-    /// returns false on failure,
1639
-    /// an array of words on success
1640
-    public function buildKeywords($query, $index, $hits)
1641
-    {
1642
-        assert(is_string($query));
1643
-        assert(is_string($index));
1644
-        assert(is_bool($hits));
1645
-
1646
-        $this->mbPush();
1647
-
1648
-        if (!($fp = $this->connect())) {
1649
-            $this->mbPop();
1650
-            return false;
1651
-        }
1652
-
1653
-        /////////////////
1654
-        // build request
1655
-        /////////////////
1656
-
1657
-        // v.1.0 req
1658
-        $req  = pack('N', strlen($query)) . $query; // req query
1659
-        $req .= pack('N', strlen($index)) . $index; // req index
1660
-        $req .= pack('N', (int)$hits);
1661
-
1662
-        ////////////////////////////
1663
-        // send query, get response
1664
-        ////////////////////////////
1665
-
1666
-        $len = strlen($req);
1667
-        $req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len) . $req; // add header
1668
-        if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_KEYWORDS))) {
1669
-            $this->mbPop();
1670
-            return false;
1671
-        }
1672
-
1673
-        //////////////////
1674
-        // parse response
1675
-        //////////////////
1676
-
1677
-        $pos = 0;
1678
-        $res = array();
1679
-        $rlen = strlen($response);
1680
-        list(, $nwords) = unpack('N*', substr($response, $pos, 4));
1681
-        $pos += 4;
1682
-        for ($i = 0; $i < $nwords; $i++) {
1683
-            list(, $len) = unpack('N*', substr($response, $pos, 4));
1684
-            $pos += 4;
1685
-            $tokenized = $len ? substr($response, $pos, $len) : '';
1686
-            $pos += $len;
1687
-
1688
-            list(, $len) = unpack('N*', substr($response, $pos, 4));
1689
-            $pos += 4;
1690
-            $normalized = $len ? substr($response, $pos, $len) : '';
1691
-            $pos += $len;
1692
-
1693
-            $res[] = array(
1694
-                'tokenized' => $tokenized,
1695
-                'normalized' => $normalized
1696
-            );
1697
-
1698
-            if ($hits) {
1699
-                list($ndocs, $nhits) = array_values(unpack('N*N*', substr($response, $pos, 8)));
1700
-                $pos += 8;
1701
-                $res[$i]['docs'] = $ndocs;
1702
-                $res[$i]['hits'] = $nhits;
1703
-            }
1704
-
1705
-            if ($pos > $rlen) {
1706
-                $this->error = 'incomplete reply';
1707
-                $this->mbPop();
1708
-                return false;
1709
-            }
1710
-        }
1711
-
1712
-        $this->mbPop();
1713
-        return $res;
1714
-    }
1715
-
1716
-    public function escapeString($string)
1717
-    {
1718
-        $from = array('\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=', '<');
1719
-        $to   = array('\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=', '\<');
1720
-
1721
-        return str_replace($from, $to, $string);
1722
-    }
1723
-
1724
-    /////////////////////////////////////////////////////////////////////////////
1725
-    // attribute updates
1726
-    /////////////////////////////////////////////////////////////////////////////
1727
-
1728
-    /// batch update given attributes in given rows in given indexes
1729
-    /// returns amount of updated documents (0 or more) on success, or -1 on failure
1730
-    public function updateAttributes($index, $attrs, $values, $mva = false, $ignorenonexistent = false)
1731
-    {
1732
-        // verify everything
1733
-        assert(is_string($index));
1734
-        assert(is_bool($mva));
1735
-        assert(is_bool($ignorenonexistent));
1736
-
1737
-        assert(is_array($attrs));
1738
-        foreach ($attrs as $attr) {
1739
-            assert(is_string($attr));
1740
-        }
1741
-
1742
-        assert(is_array($values));
1743
-        foreach ($values as $id => $entry) {
1744
-            assert(is_numeric($id));
1745
-            assert(is_array($entry));
1746
-            assert(count($entry) == count($attrs));
1747
-            foreach ($entry as $v) {
1748
-                if ($mva) {
1749
-                    assert(is_array($v));
1750
-                    foreach ($v as $vv) {
1751
-                        assert(is_int($vv));
1752
-                    }
1753
-                } else {
1754
-                    assert(is_int($v));
1755
-                }
1756
-            }
1757
-        }
1758
-
1759
-        // build request
1760
-        $this->mbPush();
1761
-        $req = pack('N', strlen($index)) . $index;
1762
-
1763
-        $req .= pack('N', count($attrs));
1764
-        $req .= pack('N', $ignorenonexistent ? 1 : 0);
1765
-        foreach ($attrs as $attr) {
1766
-            $req .= pack('N', strlen($attr)) . $attr;
1767
-            $req .= pack('N', $mva ? 1 : 0);
1768
-        }
1769
-
1770
-        $req .= pack('N', count($values));
1771
-        foreach ($values as $id => $entry) {
1772
-            $req .= sphPackU64($id);
1773
-            foreach ($entry as $v) {
1774
-                $req .= pack('N', $mva ? count($v) : $v);
1775
-                if ($mva) {
1776
-                    foreach ($v as $vv) {
1777
-                        $req .= pack('N', $vv);
1778
-                    }
1779
-                }
1780
-            }
1781
-        }
1782
-
1783
-        // connect, send query, get response
1784
-        if (!($fp = $this->connect())) {
1785
-            $this->mbPop();
1786
-            return -1;
1787
-        }
1788
-
1789
-        $len = strlen($req);
1790
-        $req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len) . $req; // add header
1791
-        if (!$this->send($fp, $req, $len + 8)) {
1792
-            $this->mbPop();
1793
-            return -1;
1794
-        }
1795
-
1796
-        if (!($response = $this->getResponse($fp, VER_COMMAND_UPDATE))) {
1797
-            $this->mbPop();
1798
-            return -1;
1799
-        }
1800
-
1801
-        // parse response
1802
-        list(, $updated) = unpack('N*', substr($response, 0, 4));
1803
-        $this->mbPop();
1804
-        return $updated;
1805
-    }
1806
-
1807
-    /////////////////////////////////////////////////////////////////////////////
1808
-    // persistent connections
1809
-    /////////////////////////////////////////////////////////////////////////////
1810
-
1811
-    public function open()
1812
-    {
1813
-        if ($this->socket !== false) {
1814
-            $this->error = 'already connected';
1815
-            return false;
1816
-        }
1817
-        if (!($fp = $this->connect()))
1818
-            return false;
1819
-
1820
-        // command, command version = 0, body length = 4, body = 1
1821
-        $req = pack('nnNN', SEARCHD_COMMAND_PERSIST, 0, 4, 1);
1822
-        if (!$this->send($fp, $req, 12)) {
1823
-            return false;
1824
-        }
1825
-
1826
-        $this->socket = $fp;
1827
-        return true;
1828
-    }
1829
-
1830
-    public function close()
1831
-    {
1832
-        if ($this->socket === false) {
1833
-            $this->error = 'not connected';
1834
-            return false;
1835
-        }
1836
-
1837
-        fclose($this->socket);
1838
-        $this->socket = false;
1839
-
1840
-        return true;
1841
-    }
1842
-
1843
-    //////////////////////////////////////////////////////////////////////////
1844
-    // status
1845
-    //////////////////////////////////////////////////////////////////////////
1846
-
1847
-    public function status($session = false)
1848
-    {
1849
-        assert(is_bool($session));
1850
-
1851
-        $this->mbPush();
1852
-        if (!($fp = $this->connect())) {
1853
-            $this->mbPop();
1854
-            return false;
1855
-        }
1856
-
1857
-        $req = pack('nnNN', SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, $session ? 0 : 1); // len=4, body=1
1858
-        if (!$this->send($fp, $req, 12) || !($response = $this->getResponse($fp, VER_COMMAND_STATUS))) {
1859
-            $this->mbPop();
1860
-            return false;
1861
-        }
1862
-
1863
-        $res = substr($response, 4); // just ignore length, error handling, etc
1864
-        $p = 0;
1865
-        list($rows, $cols) = array_values(unpack('N*N*', substr($response, $p, 8)));
1866
-        $p += 8;
1867
-
1868
-        $res = array();
1869
-        for ($i = 0; $i < $rows; $i++) {
1870
-            for ($j = 0; $j < $cols; $j++) {
1871
-                list(, $len) = unpack('N*', substr($response, $p, 4));
1872
-                $p += 4;
1873
-                $res[$i][] = substr($response, $p, $len);
1874
-                $p += $len;
1875
-            }
1876
-        }
1877
-
1878
-        $this->mbPop();
1879
-        return $res;
1880
-    }
1881
-
1882
-    //////////////////////////////////////////////////////////////////////////
1883
-    // flush
1884
-    //////////////////////////////////////////////////////////////////////////
1885
-
1886
-    public function flushAttributes()
1887
-    {
1888
-        $this->mbPush();
1889
-        if (!($fp = $this->connect())) {
1890
-            $this->mbPop();
1891
-            return -1;
1892
-        }
1893
-
1894
-        $req = pack('nnN', SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0); // len=0
1895
-        if (!$this->send($fp, $req, 8) || !($response = $this->getResponse($fp, VER_COMMAND_FLUSHATTRS))) {
1896
-            $this->mbPop();
1897
-            return -1;
1898
-        }
1899
-
1900
-        $tag = -1;
1901
-        if (strlen($response) == 4) {
1902
-            list(, $tag) = unpack('N*', $response);
1903
-        } else {
1904
-            $this->error = 'unexpected response length';
1905
-        }
1906
-
1907
-        $this->mbPop();
1908
-        return $tag;
1909
-    }
417
+	protected $host = 'localhost'; ///< searchd host (default is 'localhost')
418
+	protected $port = 9312; ///< searchd port (default is 9312)
419
+	protected $offset = 0; ///< how many records to seek from result-set start (default is 0)
420
+	protected $limit = 20; ///< how many records to return from result-set starting at offset (default is 20)
421
+	protected $mode = SPH_MATCH_EXTENDED2; ///< query matching mode (default is SPH_MATCH_EXTENDED2)
422
+	protected $weights = array(); ///< per-field weights (default is 1 for all fields)
423
+	protected $sort = SPH_SORT_RELEVANCE; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
424
+	protected $sort_by = ''; ///< attribute to sort by (defualt is '')
425
+	protected $min_id = 0; ///< min ID to match (default is 0, which means no limit)
426
+	protected $max_id = 0; ///< max ID to match (default is 0, which means no limit)
427
+	protected $filters = array(); ///< search filters
428
+	protected $group_by = ''; ///< group-by attribute name
429
+	protected $group_func = SPH_GROUPBY_DAY; ///< group-by function (to pre-process group-by attribute value with)
430
+	protected $group_sort = '@group desc'; ///< group-by sorting clause (to sort groups in result set with)
431
+	protected $group_distinct = ''; ///< group-by count-distinct attribute
432
+	protected $max_matches = 1000; ///< max matches to retrieve
433
+	protected $cutoff = 0; ///< cutoff to stop searching at (default is 0)
434
+	protected $retry_count = 0; ///< distributed retries count
435
+	protected $retry_delay = 0; ///< distributed retries delay
436
+	protected $anchor = array(); ///< geographical anchor point
437
+	protected $index_weights = array(); ///< per-index weights
438
+	protected $ranker = SPH_RANK_PROXIMITY_BM25; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
439
+	protected $rank_expr = ''; ///< ranking mode expression (for SPH_RANK_EXPR)
440
+	protected $max_query_time = 0; ///< max query time, milliseconds (default is 0, do not limit)
441
+	protected $field_weights = array(); ///< per-field-name weights
442
+	protected $overrides = array(); ///< per-query attribute values overrides
443
+	protected $select = '*'; ///< select-list (attributes or expressions, with optional aliases)
444
+	protected $query_flags = 0; ///< per-query various flags
445
+	protected $predicted_time = 0; ///< per-query max_predicted_time
446
+	protected $outer_order_by = ''; ///< outer match sort by
447
+	protected $outer_offset = 0; ///< outer offset
448
+	protected $outer_limit = 0; ///< outer limit
449
+	protected $has_outer = false;
450
+
451
+	protected $error = ''; ///< last error message
452
+	protected $warning = ''; ///< last warning message
453
+	protected $conn_error = false; ///< connection error vs remote error flag
454
+
455
+	protected $reqs = array(); ///< requests array for multi-query
456
+	protected $mbenc = ''; ///< stored mbstring encoding
457
+	protected $array_result = false; ///< whether $result['matches'] should be a hash or an array
458
+	protected $timeout = 0; ///< connect timeout
459
+
460
+	protected $path = false;
461
+	protected $socket = false;
462
+
463
+	/////////////////////////////////////////////////////////////////////////////
464
+	// common stuff
465
+	/////////////////////////////////////////////////////////////////////////////
466
+
467
+	public function __construct()
468
+	{
469
+		$this->query_flags = sphSetBit(0, 6, true); // default idf=tfidf_normalized
470
+	}
471
+
472
+	public function __destruct()
473
+	{
474
+		if ($this->socket !== false) {
475
+			fclose($this->socket);
476
+		}
477
+	}
478
+
479
+	/// get last error message (string)
480
+	public function getLastError()
481
+	{
482
+		return $this->error;
483
+	}
484
+
485
+	/// get last warning message (string)
486
+	public function getLastWarning()
487
+	{
488
+		return $this->warning;
489
+	}
490
+
491
+	/// get last error flag (to tell network connection errors from searchd errors or broken responses)
492
+	public function isConnectError()
493
+	{
494
+		return $this->conn_error;
495
+	}
496
+
497
+	/// set searchd host name (string) and port (integer)
498
+	public function setServer($host, $port = 0)
499
+	{
500
+		assert(is_string($host));
501
+		if ($host[0] == '/') {
502
+			$this->path = 'unix://' . $host;
503
+			return;
504
+		}
505
+		if (substr($host, 0, 7) == 'unix://') {
506
+			$this->path = $host;
507
+			return;
508
+		}
509
+
510
+		$this->host = $host;
511
+		$port = intval($port);
512
+		assert(0 <= $port && $port < 65536);
513
+		$this->port = $port == 0 ? 9312 : $port;
514
+		$this->path = '';
515
+	}
516
+
517
+	/// set server connection timeout (0 to remove)
518
+	public function setConnectTimeout($timeout)
519
+	{
520
+		assert(is_numeric($timeout));
521
+		$this->timeout = $timeout;
522
+	}
523
+
524
+
525
+	protected function send($handle, $data, $length)
526
+	{
527
+		if (feof($handle) || fwrite($handle, $data, $length) !== $length) {
528
+			$this->error = 'connection unexpectedly closed (timed out?)';
529
+			$this->conn_error = true;
530
+			return false;
531
+		}
532
+		return true;
533
+	}
534
+
535
+	/////////////////////////////////////////////////////////////////////////////
536
+
537
+	/// enter mbstring workaround mode
538
+	protected function mbPush()
539
+	{
540
+		$this->mbenc = '';
541
+		if (ini_get('mbstring.func_overload') & 2) {
542
+			$this->mbenc = mb_internal_encoding();
543
+			mb_internal_encoding('latin1');
544
+		}
545
+	}
546
+
547
+	/// leave mbstring workaround mode
548
+	protected function mbPop()
549
+	{
550
+		if ($this->mbenc) {
551
+			mb_internal_encoding($this->mbenc);
552
+		}
553
+	}
554
+
555
+	/// connect to searchd server
556
+	protected function connect()
557
+	{
558
+		if ($this->socket !== false) {
559
+			// we are in persistent connection mode, so we have a socket
560
+			// however, need to check whether it's still alive
561
+			if (!@feof($this->socket)) {
562
+				return $this->socket;
563
+			}
564
+
565
+			// force reopen
566
+			$this->socket = false;
567
+		}
568
+
569
+		$errno = 0;
570
+		$errstr = '';
571
+		$this->conn_error = false;
572
+
573
+		if ($this->path) {
574
+			$host = $this->path;
575
+			$port = 0;
576
+		} else {
577
+			$host = $this->host;
578
+			$port = $this->port;
579
+		}
580
+
581
+		if ($this->timeout <= 0) {
582
+			$fp = @fsockopen($host, $port, $errno, $errstr);
583
+		} else {
584
+			$fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout);
585
+		}
586
+
587
+		if (!$fp) {
588
+			if ($this->path) {
589
+				$location = $this->path;
590
+			} else {
591
+				$location = "{$this->host}:{$this->port}";
592
+			}
593
+
594
+			$errstr = trim($errstr);
595
+			$this->error = "connection to $location failed (errno=$errno, msg=$errstr)";
596
+			$this->conn_error = true;
597
+			return false;
598
+		}
599
+
600
+		// send my version
601
+		// this is a subtle part. we must do it before (!) reading back from searchd.
602
+		// because otherwise under some conditions (reported on FreeBSD for instance)
603
+		// TCP stack could throttle write-write-read pattern because of Nagle.
604
+		if (!$this->send($fp, pack('N', 1), 4)) {
605
+			fclose($fp);
606
+			$this->error = 'failed to send client protocol version';
607
+			return false;
608
+		}
609
+
610
+		// check version
611
+		list(, $v) = unpack('N*', fread($fp, 4));
612
+		$v = (int)$v;
613
+		if ($v < 1) {
614
+			fclose($fp);
615
+			$this->error = "expected searchd protocol version 1+, got version '$v'";
616
+			return false;
617
+		}
618
+
619
+		return $fp;
620
+	}
621
+
622
+	/// get and check response packet from searchd server
623
+	protected function getResponse($fp, $client_ver)
624
+	{
625
+		$response = '';
626
+		$len = 0;
627
+
628
+		$header = fread($fp, 8);
629
+		if (strlen($header) == 8) {
630
+			list($status, $ver, $len) = array_values(unpack('n2a/Nb', $header));
631
+			$left = $len;
632
+			while ($left > 0 && !feof($fp)) {
633
+				$chunk = fread($fp, min(8192, $left));
634
+				if ($chunk) {
635
+					$response .= $chunk;
636
+					$left -= strlen($chunk);
637
+				}
638
+			}
639
+		}
640
+		if ($this->socket === false) {
641
+			fclose($fp);
642
+		}
643
+
644
+		// check response
645
+		$read = strlen($response);
646
+		if (!$response || $read != $len) {
647
+			$this->error = $len
648
+				? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
649
+				: 'received zero-sized searchd response';
650
+			return false;
651
+		}
652
+
653
+		// check status
654
+		if ($status == SEARCHD_WARNING) {
655
+			list(, $wlen) = unpack('N*', substr($response, 0, 4));
656
+			$this->warning = substr($response, 4, $wlen);
657
+			return substr($response, 4 + $wlen);
658
+		}
659
+		if ($status == SEARCHD_ERROR) {
660
+			$this->error = 'searchd error: ' . substr($response, 4);
661
+			return false;
662
+		}
663
+		if ($status == SEARCHD_RETRY) {
664
+			$this->error = 'temporary searchd error: ' . substr($response, 4);
665
+			return false;
666
+		}
667
+		if ($status != SEARCHD_OK) {
668
+			$this->error = "unknown status code '$status'";
669
+			return false;
670
+		}
671
+
672
+		// check version
673
+		if ($ver < $client_ver) {
674
+			$this->warning = sprintf(
675
+				'searchd command v.%d.%d older than client\'s v.%d.%d, some options might not work',
676
+				$ver >> 8,
677
+				$ver & 0xff,
678
+				$client_ver >> 8,
679
+				$client_ver & 0xff
680
+			);
681
+		}
682
+
683
+		return $response;
684
+	}
685
+
686
+	/////////////////////////////////////////////////////////////////////////////
687
+	// searching
688
+	/////////////////////////////////////////////////////////////////////////////
689
+
690
+	/// set offset and count into result set,
691
+	/// and optionally set max-matches and cutoff limits
692
+	public function setLimits($offset, $limit, $max = 0, $cutoff = 0)
693
+	{
694
+		assert(is_int($offset));
695
+		assert(is_int($limit));
696
+		assert($offset >= 0);
697
+		assert($limit > 0);
698
+		assert($max >= 0);
699
+		$this->offset = $offset;
700
+		$this->limit = $limit;
701
+		if ($max > 0) {
702
+			$this->max_matches = $max;
703
+		}
704
+		if ($cutoff > 0) {
705
+			$this->cutoff = $cutoff;
706
+		}
707
+	}
708
+
709
+	/// set maximum query time, in milliseconds, per-index
710
+	/// integer, 0 means 'do not limit'
711
+	public function setMaxQueryTime($max)
712
+	{
713
+		assert(is_int($max));
714
+		assert($max >= 0);
715
+		$this->max_query_time = $max;
716
+	}
717
+
718
+	/// set matching mode
719
+	public function setMatchMode($mode)
720
+	{
721
+		trigger_error(
722
+			'DEPRECATED: Do not call this method or, even better, use SphinxQL instead of an API',
723
+			E_USER_DEPRECATED
724
+		);
725
+		assert(
726
+			$mode == SPH_MATCH_ALL ||
727
+			$mode == SPH_MATCH_ANY ||
728
+			$mode == SPH_MATCH_PHRASE ||
729
+			$mode == SPH_MATCH_BOOLEAN ||
730
+			$mode == SPH_MATCH_EXTENDED ||
731
+			$mode == SPH_MATCH_FULLSCAN ||
732
+			$mode == SPH_MATCH_EXTENDED2
733
+		);
734
+		$this->mode = $mode;
735
+	}
736
+
737
+	/// set ranking mode
738
+	public function setRankingMode($ranker, $rankexpr='')
739
+	{
740
+		assert($ranker === 0 || $ranker >= 1 && $ranker < SPH_RANK_TOTAL);
741
+		assert(is_string($rankexpr));
742
+		$this->ranker = $ranker;
743
+		$this->rank_expr = $rankexpr;
744
+	}
745
+
746
+	/// set matches sorting mode
747
+	public function setSortMode($mode, $sortby = '')
748
+	{
749
+		assert (
750
+			$mode == SPH_SORT_RELEVANCE ||
751
+			$mode == SPH_SORT_ATTR_DESC ||
752
+			$mode == SPH_SORT_ATTR_ASC ||
753
+			$mode == SPH_SORT_TIME_SEGMENTS ||
754
+			$mode == SPH_SORT_EXTENDED ||
755
+			$mode == SPH_SORT_EXPR
756
+		);
757
+		assert(is_string($sortby));
758
+		assert($mode == SPH_SORT_RELEVANCE || strlen($sortby) > 0);
759
+
760
+		$this->sort = $mode;
761
+		$this->sort_by = $sortby;
762
+	}
763
+
764
+	/**
765
+	 * Bind per-field weights by order
766
+	 *
767
+	 * @deprecated use setFieldWeights() instead
768
+	 */
769
+	public function setWeights()
770
+	{
771
+		throw new \RuntimeException('This method is now deprecated; please use setFieldWeights instead');
772
+	}
773
+
774
+	/// bind per-field weights by name
775
+	public function setFieldWeights($weights)
776
+	{
777
+		assert(is_array($weights));
778
+		foreach ($weights as $name => $weight) {
779
+			assert(is_string($name));
780
+			assert(is_int($weight));
781
+		}
782
+		$this->field_weights = $weights;
783
+	}
784
+
785
+	/// bind per-index weights by name
786
+	public function setIndexWeights($weights)
787
+	{
788
+		assert(is_array($weights));
789
+		foreach ($weights as $index => $weight) {
790
+			assert(is_string($index));
791
+			assert(is_int($weight));
792
+		}
793
+		$this->index_weights = $weights;
794
+	}
795
+
796
+	/// set IDs range to match
797
+	/// only match records if document ID is beetwen $min and $max (inclusive)
798
+	public function setIDRange($min, $max)
799
+	{
800
+		assert(is_numeric($min));
801
+		assert(is_numeric($max));
802
+		assert($min <= $max);
803
+		$this->min_id = $min;
804
+		$this->max_id = $max;
805
+	}
806
+
807
+	/// set values set filter
808
+	/// only match records where $attribute value is in given set
809
+	public function setFilter($attribute, $values, $exclude = false)
810
+	{
811
+		assert(is_string($attribute));
812
+		assert(is_array($values));
813
+		assert(count($values));
814
+
815
+		if (is_array($values) && count($values)) {
816
+			foreach ($values as $value) {
817
+				assert(is_numeric($value));
818
+			}
819
+
820
+			$this->filters[] = array(
821
+				'type' => SPH_FILTER_VALUES,
822
+				'attr' => $attribute,
823
+				'exclude' => $exclude,
824
+				'values' => $values
825
+			);
826
+		}
827
+	}
828
+
829
+	/// set string filter
830
+	/// only match records where $attribute value is equal
831
+	public function setFilterString($attribute, $value, $exclude = false)
832
+	{
833
+		assert(is_string($attribute));
834
+		assert(is_string($value));
835
+		$this->filters[] = array(
836
+			'type' => SPH_FILTER_STRING,
837
+			'attr' => $attribute,
838
+			'exclude' => $exclude,
839
+			'value' => $value
840
+		);
841
+	}    
842
+
843
+	/// set range filter
844
+	/// only match records if $attribute value is beetwen $min and $max (inclusive)
845
+	public function setFilterRange($attribute, $min, $max, $exclude = false)
846
+	{
847
+		assert(is_string($attribute));
848
+		assert(is_numeric($min));
849
+		assert(is_numeric($max));
850
+		assert($min <= $max);
851
+
852
+		$this->filters[] = array(
853
+			'type' => SPH_FILTER_RANGE,
854
+			'attr' => $attribute,
855
+			'exclude' => $exclude,
856
+			'min' => $min,
857
+			'max' => $max
858
+		);
859
+	}
860
+
861
+	/// set float range filter
862
+	/// only match records if $attribute value is beetwen $min and $max (inclusive)
863
+	public function setFilterFloatRange($attribute, $min, $max, $exclude = false)
864
+	{
865
+		assert(is_string($attribute));
866
+		assert(is_float($min));
867
+		assert(is_float($max));
868
+		assert($min <= $max);
869
+
870
+		$this->filters[] = array(
871
+			'type' => SPH_FILTER_FLOATRANGE,
872
+			'attr' => $attribute,
873
+			'exclude' => $exclude,
874
+			'min' => $min,
875
+			'max' => $max
876
+		);
877
+	}
878
+
879
+	/// setup anchor point for geosphere distance calculations
880
+	/// required to use @geodist in filters and sorting
881
+	/// latitude and longitude must be in radians
882
+	public function setGeoAnchor($attrlat, $attrlong, $lat, $long)
883
+	{
884
+		assert(is_string($attrlat));
885
+		assert(is_string($attrlong));
886
+		assert(is_float($lat));
887
+		assert(is_float($long));
888
+
889
+		$this->anchor = array(
890
+			'attrlat' => $attrlat,
891
+			'attrlong' => $attrlong,
892
+			'lat' => $lat,
893
+			'long' => $long
894
+		);
895
+	}
896
+
897
+	/// set grouping attribute and function
898
+	public function setGroupBy($attribute, $func, $groupsort = '@group desc')
899
+	{
900
+		assert(is_string($attribute));
901
+		assert(is_string($groupsort));
902
+		assert(
903
+			$func == SPH_GROUPBY_DAY ||
904
+			$func == SPH_GROUPBY_WEEK ||
905
+			$func == SPH_GROUPBY_MONTH ||
906
+			$func == SPH_GROUPBY_YEAR ||
907
+			$func == SPH_GROUPBY_ATTR ||
908
+			$func == SPH_GROUPBY_ATTRPAIR
909
+		);
910
+
911
+		$this->group_by = $attribute;
912
+		$this->group_func = $func;
913
+		$this->group_sort = $groupsort;
914
+	}
915
+
916
+	/// set count-distinct attribute for group-by queries
917
+	public function setGroupDistinct($attribute)
918
+	{
919
+		assert(is_string($attribute));
920
+		$this->group_distinct = $attribute;
921
+	}
922
+
923
+	/// set distributed retries count and delay
924
+	public function setRetries($count, $delay = 0)
925
+	{
926
+		assert(is_int($count) && $count >= 0);
927
+		assert(is_int($delay) && $delay >= 0);
928
+		$this->retry_count = $count;
929
+		$this->retry_delay = $delay;
930
+	}
931
+
932
+	/// set result set format (hash or array; hash by default)
933
+	/// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
934
+	public function setArrayResult($arrayresult)
935
+	{
936
+		assert(is_bool($arrayresult));
937
+		$this->array_result = $arrayresult;
938
+	}
939
+
940
+	/// set attribute values override
941
+	/// there can be only one override per attribute
942
+	/// $values must be a hash that maps document IDs to attribute values
943
+	public function setOverride($attrname, $attrtype, $values)
944
+	{
945
+		trigger_error(
946
+			'DEPRECATED: Do not call this method. Use SphinxQL REMAP() function instead.',
947
+			E_USER_DEPRECATED
948
+		);
949
+		assert(is_string($attrname));
950
+		assert(in_array($attrtype, array(
951
+			SPH_ATTR_INTEGER,
952
+			SPH_ATTR_TIMESTAMP,
953
+			SPH_ATTR_BOOL,
954
+			SPH_ATTR_FLOAT,
955
+			SPH_ATTR_BIGINT
956
+		)));
957
+		assert(is_array($values));
958
+
959
+		$this->overrides[$attrname] = array(
960
+			'attr' => $attrname,
961
+			'type' => $attrtype,
962
+			'values' => $values
963
+		);
964
+	}
965
+
966
+	/// set select-list (attributes or expressions), SQL-like syntax
967
+	public function setSelect($select)
968
+	{
969
+		assert(is_string($select));
970
+		$this->select = $select;
971
+	}
972
+
973
+	public function setQueryFlag($flag_name, $flag_value)
974
+	{
975
+		$known_names = array(
976
+			'reverse_scan',
977
+			'sort_method',
978
+			'max_predicted_time',
979
+			'boolean_simplify',
980
+			'idf',
981
+			'global_idf',
982
+			'low_priority'
983
+		);
984
+		$flags = array (
985
+			'reverse_scan' => array(0, 1),
986
+			'sort_method' => array('pq', 'kbuffer'),
987
+			'max_predicted_time' => array(0),
988
+			'boolean_simplify' => array(true, false),
989
+			'idf' => array ('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
990
+			'global_idf' => array(true, false),
991
+			'low_priority' => array(true, false)
992
+		);
993
+
994
+		assert(isset($flag_name, $known_names));
995
+		assert(
996
+			in_array($flag_value, $flags[$flag_name], true) ||
997
+			($flag_name == 'max_predicted_time' && is_int($flag_value) && $flag_value >= 0)
998
+		);
999
+
1000
+		if ($flag_name == 'reverse_scan') {
1001
+			$this->query_flags = sphSetBit($this->query_flags, 0, $flag_value == 1);
1002
+		}
1003
+		if ($flag_name == 'sort_method') {
1004
+			$this->query_flags = sphSetBit($this->query_flags, 1, $flag_value == 'kbuffer');
1005
+		}
1006
+		if ($flag_name == 'max_predicted_time') {
1007
+			$this->query_flags = sphSetBit($this->query_flags, 2, $flag_value > 0);
1008
+			$this->predicted_time = (int)$flag_value;
1009
+		}
1010
+		if ($flag_name == 'boolean_simplify') {
1011
+			$this->query_flags = sphSetBit($this->query_flags, 3, $flag_value);
1012
+		}
1013
+		if ($flag_name == 'idf' && ($flag_value == 'normalized' || $flag_value == 'plain')) {
1014
+			$this->query_flags = sphSetBit($this->query_flags, 4, $flag_value == 'plain');
1015
+		}
1016
+		if ($flag_name == 'global_idf') {
1017
+			$this->query_flags = sphSetBit($this->query_flags, 5, $flag_value);
1018
+		}
1019
+		if ($flag_name == 'idf' && ($flag_value == 'tfidf_normalized' || $flag_value == 'tfidf_unnormalized')) {
1020
+			$this->query_flags = sphSetBit($this->query_flags, 6, $flag_value == 'tfidf_normalized');
1021
+		}
1022
+		if ($flag_name == 'low_priority') {
1023
+			$this->query_flags = sphSetBit($this->query_flags, 8, $flag_value);
1024
+		}
1025
+	}
1026
+
1027
+	/// set outer order by parameters
1028
+	public function setOuterSelect($orderby, $offset, $limit)
1029
+	{
1030
+		assert(is_string($orderby));
1031
+		assert(is_int($offset));
1032
+		assert(is_int($limit));
1033
+		assert($offset >= 0);
1034
+		assert($limit > 0);
1035
+
1036
+		$this->outer_order_by = $orderby;
1037
+		$this->outer_offset = $offset;
1038
+		$this->outer_limit = $limit;
1039
+		$this->has_outer = true;
1040
+	}
1041
+
1042
+
1043
+	//////////////////////////////////////////////////////////////////////////////
1044
+
1045
+	/// clear all filters (for multi-queries)
1046
+	public function resetFilters()
1047
+	{
1048
+		$this->filters = array();
1049
+		$this->anchor = array();
1050
+	}
1051
+
1052
+	/// clear groupby settings (for multi-queries)
1053
+	public function resetGroupBy()
1054
+	{
1055
+		$this->group_by = '';
1056
+		$this->group_func = SPH_GROUPBY_DAY;
1057
+		$this->group_sort = '@group desc';
1058
+		$this->group_distinct = '';
1059
+	}
1060
+
1061
+	/// clear all attribute value overrides (for multi-queries)
1062
+	public function resetOverrides()
1063
+	{
1064
+		$this->overrides = array();
1065
+	}
1066
+
1067
+	public function resetQueryFlag()
1068
+	{
1069
+		$this->query_flags = sphSetBit(0, 6, true); // default idf=tfidf_normalized
1070
+		$this->predicted_time = 0;
1071
+	}
1072
+
1073
+	public function resetOuterSelect()
1074
+	{
1075
+		$this->outer_order_by = '';
1076
+		$this->outer_offset = 0;
1077
+		$this->outer_limit = 0;
1078
+		$this->has_outer = false;
1079
+	}
1080
+
1081
+	//////////////////////////////////////////////////////////////////////////////
1082
+
1083
+	/// connect to searchd server, run given search query through given indexes,
1084
+	/// and return the search results
1085
+	public function query($query, $index = '*', $comment = '')
1086
+	{
1087
+		assert(empty($this->reqs));
1088
+
1089
+		$this->addQuery($query, $index, $comment);
1090
+		$results = $this->runQueries();
1091
+		$this->reqs = array(); // just in case it failed too early
1092
+
1093
+		if (!is_array($results)) {
1094
+			return false; // probably network error; error message should be already filled
1095
+		}
1096
+
1097
+		$this->error = $results[0]['error'];
1098
+		$this->warning = $results[0]['warning'];
1099
+		if ($results[0]['status'] == SEARCHD_ERROR) {
1100
+			return false;
1101
+		} else {
1102
+			return $results[0];
1103
+		}
1104
+	}
1105
+
1106
+	/// helper to pack floats in network byte order
1107
+	protected function packFloat($f)
1108
+	{
1109
+		$t1 = pack('f', $f); // machine order
1110
+		list(, $t2) = unpack('L*', $t1); // int in machine order
1111
+		return pack('N', $t2);
1112
+	}
1113
+
1114
+	/// add query to multi-query batch
1115
+	/// returns index into results array from RunQueries() call
1116
+	public function addQuery($query, $index = '*', $comment = '')
1117
+	{
1118
+		// mbstring workaround
1119
+		$this->mbPush();
1120
+
1121
+		// build request
1122
+		$req = pack('NNNNN', $this->query_flags, $this->offset, $this->limit, $this->mode, $this->ranker);
1123
+		if ($this->ranker == SPH_RANK_EXPR) {
1124
+			$req .= pack('N', strlen($this->rank_expr)) . $this->rank_expr;
1125
+		}
1126
+		$req .= pack('N', $this->sort); // (deprecated) sort mode
1127
+		$req .= pack('N', strlen($this->sort_by)) . $this->sort_by;
1128
+		$req .= pack('N', strlen($query)) . $query; // query itself
1129
+		$req .= pack('N', count($this->weights)); // weights
1130
+		foreach ($this->weights as $weight) {
1131
+			$req .= pack('N', (int)$weight);
1132
+		}
1133
+		$req .= pack('N', strlen($index)) . $index; // indexes
1134
+		$req .= pack('N', 1); // id64 range marker
1135
+		$req .= sphPackU64($this->min_id) . sphPackU64($this->max_id); // id64 range
1136
+
1137
+		// filters
1138
+		$req .= pack('N', count($this->filters));
1139
+		foreach ($this->filters as $filter) {
1140
+			$req .= pack('N', strlen($filter['attr'])) . $filter['attr'];
1141
+			$req .= pack('N', $filter['type']);
1142
+			switch ($filter['type']) {
1143
+				case SPH_FILTER_VALUES:
1144
+					$req .= pack('N', count($filter['values']));
1145
+					foreach ($filter['values'] as $value) {
1146
+						$req .= sphPackI64($value);
1147
+					}
1148
+					break;
1149
+				case SPH_FILTER_RANGE:
1150
+					$req .= sphPackI64($filter['min']) . sphPackI64($filter['max']);
1151
+					break;
1152
+				case SPH_FILTER_FLOATRANGE:
1153
+					$req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']);
1154
+					break;
1155
+				case SPH_FILTER_STRING:
1156
+					$req .= pack('N', strlen($filter['value'])) . $filter['value'];
1157
+					break;
1158
+				default:
1159
+					assert(0 && 'internal error: unhandled filter type');
1160
+			}
1161
+			$req .= pack('N', $filter['exclude']);
1162
+		}
1163
+
1164
+		// group-by clause, max-matches count, group-sort clause, cutoff count
1165
+		$req .= pack('NN', $this->group_func, strlen($this->group_by)) . $this->group_by;
1166
+		$req .= pack('N', $this->max_matches);
1167
+		$req .= pack('N', strlen($this->group_sort)) . $this->group_sort;
1168
+		$req .= pack('NNN', $this->cutoff, $this->retry_count, $this->retry_delay);
1169
+		$req .= pack('N', strlen($this->group_distinct)) . $this->group_distinct;
1170
+
1171
+		// anchor point
1172
+		if (empty($this->anchor)) {
1173
+			$req .= pack('N', 0);
1174
+		} else {
1175
+			$a =& $this->anchor;
1176
+			$req .= pack('N', 1);
1177
+			$req .= pack('N', strlen($a['attrlat'])) . $a['attrlat'];
1178
+			$req .= pack('N', strlen($a['attrlong'])) . $a['attrlong'];
1179
+			$req .= $this->packFloat($a['lat']) . $this->packFloat($a['long']);
1180
+		}
1181
+
1182
+		// per-index weights
1183
+		$req .= pack('N', count($this->index_weights));
1184
+		foreach ($this->index_weights as $idx => $weight) {
1185
+			$req .= pack('N', strlen($idx)) . $idx . pack('N', $weight);
1186
+		}
1187
+
1188
+		// max query time
1189
+		$req .= pack('N', $this->max_query_time);
1190
+
1191
+		// per-field weights
1192
+		$req .= pack('N', count($this->field_weights));
1193
+		foreach ($this->field_weights as $field => $weight) {
1194
+			$req .= pack('N', strlen($field)) . $field . pack('N', $weight);
1195
+		}
1196
+
1197
+		// comment
1198
+		$req .= pack('N', strlen($comment)) . $comment;
1199
+
1200
+		// attribute overrides
1201
+		$req .= pack('N', count($this->overrides));
1202
+		foreach ($this->overrides as $key => $entry) {
1203
+			$req .= pack('N', strlen($entry['attr'])) . $entry['attr'];
1204
+			$req .= pack('NN', $entry['type'], count($entry['values']));
1205
+			foreach ($entry['values'] as $id => $val) {
1206
+				assert(is_numeric($id));
1207
+				assert(is_numeric($val));
1208
+
1209
+				$req .= sphPackU64($id);
1210
+				switch ($entry['type']) {
1211
+					case SPH_ATTR_FLOAT:
1212
+						$req .= $this->packFloat($val);
1213
+						break;
1214
+					case SPH_ATTR_BIGINT:
1215
+						$req .= sphPackI64($val);
1216
+						break;
1217
+					default:
1218
+						$req .= pack('N', $val);
1219
+						break;
1220
+				}
1221
+			}
1222
+		}
1223
+
1224
+		// select-list
1225
+		$req .= pack('N', strlen($this->select)) . $this->select;
1226
+
1227
+		// max_predicted_time
1228
+		if ($this->predicted_time > 0) {
1229
+			$req .= pack('N', (int)$this->predicted_time);
1230
+		}
1231
+
1232
+		$req .= pack('N', strlen($this->outer_order_by)) . $this->outer_order_by;
1233
+		$req .= pack('NN', $this->outer_offset, $this->outer_limit);
1234
+		if ($this->has_outer) {
1235
+			$req .= pack('N', 1);
1236
+		} else {
1237
+			$req .= pack('N', 0);
1238
+		}
1239
+
1240
+		// mbstring workaround
1241
+		$this->mbPop();
1242
+
1243
+		// store request to requests array
1244
+		$this->reqs[] = $req;
1245
+		return count($this->reqs) - 1;
1246
+	}
1247
+
1248
+	/// connect to searchd, run queries batch, and return an array of result sets
1249
+	public function runQueries()
1250
+	{
1251
+		if (empty($this->reqs)) {
1252
+			$this->error = 'no queries defined, issue AddQuery() first';
1253
+			return false;
1254
+		}
1255
+
1256
+		// mbstring workaround
1257
+		$this->mbPush();
1258
+
1259
+		if (!($fp = $this->connect())) {
1260
+			$this->mbPop();
1261
+			return false;
1262
+		}
1263
+
1264
+		// send query, get response
1265
+		$nreqs = count($this->reqs);
1266
+		$req = join('', $this->reqs);
1267
+		$len = 8 + strlen($req);
1268
+		$req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs) . $req; // add header
1269
+
1270
+		if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_SEARCH))) {
1271
+			$this->mbPop();
1272
+			return false;
1273
+		}
1274
+
1275
+		// query sent ok; we can reset reqs now
1276
+		$this->reqs = array();
1277
+
1278
+		// parse and return response
1279
+		return $this->parseSearchResponse($response, $nreqs);
1280
+	}
1281
+
1282
+	/// parse and return search query (or queries) response
1283
+	protected function parseSearchResponse($response, $nreqs)
1284
+	{
1285
+		$p = 0; // current position
1286
+		$max = strlen($response); // max position for checks, to protect against broken responses
1287
+
1288
+		$results = array();
1289
+		for ($ires = 0; $ires < $nreqs && $p < $max; $ires++) {
1290
+			$results[] = array();
1291
+			$result =& $results[$ires];
1292
+
1293
+			$result['error'] = '';
1294
+			$result['warning'] = '';
1295
+
1296
+			// extract status
1297
+			list(, $status) = unpack('N*', substr($response, $p, 4));
1298
+			$p += 4;
1299
+			$result['status'] = $status;
1300
+			if ($status != SEARCHD_OK) {
1301
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1302
+				$p += 4;
1303
+				$message = substr($response, $p, $len);
1304
+				$p += $len;
1305
+
1306
+				if ($status == SEARCHD_WARNING) {
1307
+					$result['warning'] = $message;
1308
+				} else {
1309
+					$result['error'] = $message;
1310
+					continue;
1311
+				}
1312
+			}
1313
+
1314
+			// read schema
1315
+			$fields = array();
1316
+			$attrs = array();
1317
+
1318
+			list(, $nfields) = unpack('N*', substr($response, $p, 4));
1319
+			$p += 4;
1320
+			while ($nfields --> 0 && $p < $max) {
1321
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1322
+				$p += 4;
1323
+				$fields[] = substr($response, $p, $len);
1324
+				$p += $len;
1325
+			}
1326
+			$result['fields'] = $fields;
1327
+
1328
+			list(, $nattrs) = unpack('N*', substr($response, $p, 4));
1329
+			$p += 4;
1330
+			while ($nattrs --> 0 && $p < $max) {
1331
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1332
+				$p += 4;
1333
+				$attr = substr($response, $p, $len);
1334
+				$p += $len;
1335
+				list(, $type) = unpack('N*', substr($response, $p, 4));
1336
+				$p += 4;
1337
+				$attrs[$attr] = $type;
1338
+			}
1339
+			$result['attrs'] = $attrs;
1340
+
1341
+			// read match count
1342
+			list(, $count) = unpack('N*', substr($response, $p, 4));
1343
+			$p += 4;
1344
+			list(, $id64) = unpack('N*', substr($response, $p, 4));
1345
+			$p += 4;
1346
+
1347
+			// read matches
1348
+			$idx = -1;
1349
+			while ($count --> 0 && $p < $max) {
1350
+				// index into result array
1351
+				$idx++;
1352
+
1353
+				// parse document id and weight
1354
+				if ($id64) {
1355
+					$doc = sphUnpackU64(substr($response, $p, 8));
1356
+					$p += 8;
1357
+					list(,$weight) = unpack('N*', substr($response, $p, 4));
1358
+					$p += 4;
1359
+				} else {
1360
+					list($doc, $weight) = array_values(unpack('N*N*', substr($response, $p, 8)));
1361
+					$p += 8;
1362
+					$doc = sphFixUint($doc);
1363
+				}
1364
+				$weight = sprintf('%u', $weight);
1365
+
1366
+				// create match entry
1367
+				if ($this->array_result) {
1368
+					$result['matches'][$idx] = array('id' => $doc, 'weight' => $weight);
1369
+				} else {
1370
+					$result['matches'][$doc]['weight'] = $weight;
1371
+				}
1372
+
1373
+				// parse and create attributes
1374
+				$attrvals = array();
1375
+				foreach ($attrs as $attr => $type) {
1376
+					// handle 64bit ints
1377
+					if ($type == SPH_ATTR_BIGINT) {
1378
+						$attrvals[$attr] = sphUnpackI64(substr($response, $p, 8));
1379
+						$p += 8;
1380
+						continue;
1381
+					}
1382
+
1383
+					// handle floats
1384
+					if ($type == SPH_ATTR_FLOAT) {
1385
+						list(, $uval) = unpack('N*', substr($response, $p, 4));
1386
+						$p += 4;
1387
+						list(, $fval) = unpack('f*', pack('L', $uval));
1388
+						$attrvals[$attr] = $fval;
1389
+						continue;
1390
+					}
1391
+
1392
+					// handle everything else as unsigned ints
1393
+					list(, $val) = unpack('N*', substr($response, $p, 4));
1394
+					$p += 4;
1395
+					if ($type == SPH_ATTR_MULTI) {
1396
+						$attrvals[$attr] = array();
1397
+						$nvalues = $val;
1398
+						while ($nvalues --> 0 && $p < $max) {
1399
+							list(, $val) = unpack('N*', substr($response, $p, 4));
1400
+							$p += 4;
1401
+							$attrvals[$attr][] = sphFixUint($val);
1402
+						}
1403
+					} elseif ($type == SPH_ATTR_MULTI64) {
1404
+						$attrvals[$attr] = array();
1405
+						$nvalues = $val;
1406
+						while ($nvalues > 0 && $p < $max) {
1407
+							$attrvals[$attr][] = sphUnpackI64(substr($response, $p, 8));
1408
+							$p += 8;
1409
+							$nvalues -= 2;
1410
+						}
1411
+					} elseif ($type == SPH_ATTR_STRING) {
1412
+						$attrvals[$attr] = substr($response, $p, $val);
1413
+						$p += $val;
1414
+					} elseif ($type == SPH_ATTR_FACTORS) {
1415
+						$attrvals[$attr] = substr($response, $p, $val - 4);
1416
+						$p += $val-4;
1417
+					} else {
1418
+						$attrvals[$attr] = sphFixUint($val);
1419
+					}
1420
+				}
1421
+
1422
+				if ($this->array_result) {
1423
+					$result['matches'][$idx]['attrs'] = $attrvals;
1424
+				} else {
1425
+					$result['matches'][$doc]['attrs'] = $attrvals;
1426
+				}
1427
+			}
1428
+
1429
+			list($total, $total_found, $msecs, $words) = array_values(unpack('N*N*N*N*', substr($response, $p, 16)));
1430
+			$result['total'] = sprintf('%u', $total);
1431
+			$result['total_found'] = sprintf('%u', $total_found);
1432
+			$result['time'] = sprintf('%.3f', $msecs / 1000);
1433
+			$p += 16;
1434
+
1435
+			while ($words --> 0 && $p < $max) {
1436
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1437
+				$p += 4;
1438
+				$word = substr($response, $p, $len);
1439
+				$p += $len;
1440
+				list($docs, $hits) = array_values(unpack('N*N*', substr($response, $p, 8)));
1441
+				$p += 8;
1442
+				$result['words'][$word] = array (
1443
+					'docs' => sprintf('%u', $docs),
1444
+					'hits' => sprintf('%u', $hits)
1445
+				);
1446
+			}
1447
+		}
1448
+
1449
+		$this->mbPop();
1450
+		return $results;
1451
+	}
1452
+
1453
+	/////////////////////////////////////////////////////////////////////////////
1454
+	// excerpts generation
1455
+	/////////////////////////////////////////////////////////////////////////////
1456
+
1457
+	/// connect to searchd server, and generate exceprts (snippets)
1458
+	/// of given documents for given query. returns false on failure,
1459
+	/// an array of snippets on success
1460
+	public function buildExcerpts($docs, $index, $words, $opts = array())
1461
+	{
1462
+		assert(is_array($docs));
1463
+		assert(is_string($index));
1464
+		assert(is_string($words));
1465
+		assert(is_array($opts));
1466
+
1467
+		$this->mbPush();
1468
+
1469
+		if (!($fp = $this->connect())) {
1470
+			$this->mbPop();
1471
+			return false;
1472
+		}
1473
+
1474
+		/////////////////
1475
+		// fixup options
1476
+		/////////////////
1477
+
1478
+		if (!isset($opts['before_match'])) {
1479
+			$opts['before_match'] = '<b>';
1480
+		}
1481
+		if (!isset($opts['after_match'])) {
1482
+			$opts['after_match'] = '</b>';
1483
+		}
1484
+		if (!isset($opts['chunk_separator'])) {
1485
+			$opts['chunk_separator'] = ' ... ';
1486
+		}
1487
+		if (!isset($opts['limit'])) {
1488
+			$opts['limit'] = 256;
1489
+		}
1490
+		if (!isset($opts['limit_passages'])) {
1491
+			$opts['limit_passages'] = 0;
1492
+		}
1493
+		if (!isset($opts['limit_words'])) {
1494
+			$opts['limit_words'] = 0;
1495
+		}
1496
+		if (!isset($opts['around'])) {
1497
+			$opts['around'] = 5;
1498
+		}
1499
+		if (!isset($opts['exact_phrase'])) {
1500
+			$opts['exact_phrase'] = false;
1501
+		}
1502
+		if (!isset($opts['single_passage'])) {
1503
+			$opts['single_passage'] = false;
1504
+		}
1505
+		if (!isset($opts['use_boundaries'])) {
1506
+			$opts['use_boundaries'] = false;
1507
+		}
1508
+		if (!isset($opts['weight_order'])) {
1509
+			$opts['weight_order'] = false;
1510
+		}
1511
+		if (!isset($opts['query_mode'])) {
1512
+			$opts['query_mode'] = false;
1513
+		}
1514
+		if (!isset($opts['force_all_words'])) {
1515
+			$opts['force_all_words'] = false;
1516
+		}
1517
+		if (!isset($opts['start_passage_id'])) {
1518
+			$opts['start_passage_id'] = 1;
1519
+		}
1520
+		if (!isset($opts['load_files'])) {
1521
+			$opts['load_files'] = false;
1522
+		}
1523
+		if (!isset($opts['html_strip_mode'])) {
1524
+			$opts['html_strip_mode'] = 'index';
1525
+		}
1526
+		if (!isset($opts['allow_empty'])) {
1527
+			$opts['allow_empty'] = false;
1528
+		}
1529
+		if (!isset($opts['passage_boundary'])) {
1530
+			$opts['passage_boundary'] = 'none';
1531
+		}
1532
+		if (!isset($opts['emit_zones'])) {
1533
+			$opts['emit_zones'] = false;
1534
+		}
1535
+		if (!isset($opts['load_files_scattered'])) {
1536
+			$opts['load_files_scattered'] = false;
1537
+		}
1538
+
1539
+
1540
+		/////////////////
1541
+		// build request
1542
+		/////////////////
1543
+
1544
+		// v.1.2 req
1545
+		$flags = 1; // remove spaces
1546
+		if ($opts['exact_phrase']) {
1547
+			$flags |= 2;
1548
+		}
1549
+		if ($opts['single_passage']) {
1550
+			$flags |= 4;
1551
+		}
1552
+		if ($opts['use_boundaries']) {
1553
+			$flags |= 8;
1554
+		}
1555
+		if ($opts['weight_order']) {
1556
+			$flags |= 16;
1557
+		}
1558
+		if ($opts['query_mode']) {
1559
+			$flags |= 32;
1560
+		}
1561
+		if ($opts['force_all_words']) {
1562
+			$flags |= 64;
1563
+		}
1564
+		if ($opts['load_files']) {
1565
+			$flags |= 128;
1566
+		}
1567
+		if ($opts['allow_empty']) {
1568
+			$flags |= 256;
1569
+		}
1570
+		if ($opts['emit_zones']) {
1571
+			$flags |= 512;
1572
+		}
1573
+		if ($opts['load_files_scattered']) {
1574
+			$flags |= 1024;
1575
+		}
1576
+		$req = pack('NN', 0, $flags); // mode=0, flags=$flags
1577
+		$req .= pack('N', strlen($index)) . $index; // req index
1578
+		$req .= pack('N', strlen($words)) . $words; // req words
1579
+
1580
+		// options
1581
+		$req .= pack('N', strlen($opts['before_match'])) . $opts['before_match'];
1582
+		$req .= pack('N', strlen($opts['after_match'])) . $opts['after_match'];
1583
+		$req .= pack('N', strlen($opts['chunk_separator'])) . $opts['chunk_separator'];
1584
+		$req .= pack('NN', (int)$opts['limit'], (int)$opts['around']);
1585
+		$req .= pack('NNN', (int)$opts['limit_passages'], (int)$opts['limit_words'], (int)$opts['start_passage_id']); // v.1.2
1586
+		$req .= pack('N', strlen($opts['html_strip_mode'])) . $opts['html_strip_mode'];
1587
+		$req .= pack('N', strlen($opts['passage_boundary'])) . $opts['passage_boundary'];
1588
+
1589
+		// documents
1590
+		$req .= pack('N', count($docs));
1591
+		foreach ($docs as $doc) {
1592
+			assert(is_string($doc));
1593
+			$req .= pack('N', strlen($doc)) . $doc;
1594
+		}
1595
+
1596
+		////////////////////////////
1597
+		// send query, get response
1598
+		////////////////////////////
1599
+
1600
+		$len = strlen($req);
1601
+		$req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len) . $req; // add header
1602
+		if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_EXCERPT))) {
1603
+			$this->mbPop();
1604
+			return false;
1605
+		}
1606
+
1607
+		//////////////////
1608
+		// parse response
1609
+		//////////////////
1610
+
1611
+		$pos = 0;
1612
+		$res = array();
1613
+		$rlen = strlen($response);
1614
+		$count = count($docs);
1615
+		while ($count--) {
1616
+			list(, $len) = unpack('N*', substr($response, $pos, 4));
1617
+			$pos += 4;
1618
+
1619
+			if ($pos + $len > $rlen) {
1620
+				$this->error = 'incomplete reply';
1621
+				$this->mbPop();
1622
+				return false;
1623
+			}
1624
+			$res[] = $len ? substr($response, $pos, $len) : '';
1625
+			$pos += $len;
1626
+		}
1627
+
1628
+		$this->mbPop();
1629
+		return $res;
1630
+	}
1631
+
1632
+
1633
+	/////////////////////////////////////////////////////////////////////////////
1634
+	// keyword generation
1635
+	/////////////////////////////////////////////////////////////////////////////
1636
+
1637
+	/// connect to searchd server, and generate keyword list for a given query
1638
+	/// returns false on failure,
1639
+	/// an array of words on success
1640
+	public function buildKeywords($query, $index, $hits)
1641
+	{
1642
+		assert(is_string($query));
1643
+		assert(is_string($index));
1644
+		assert(is_bool($hits));
1645
+
1646
+		$this->mbPush();
1647
+
1648
+		if (!($fp = $this->connect())) {
1649
+			$this->mbPop();
1650
+			return false;
1651
+		}
1652
+
1653
+		/////////////////
1654
+		// build request
1655
+		/////////////////
1656
+
1657
+		// v.1.0 req
1658
+		$req  = pack('N', strlen($query)) . $query; // req query
1659
+		$req .= pack('N', strlen($index)) . $index; // req index
1660
+		$req .= pack('N', (int)$hits);
1661
+
1662
+		////////////////////////////
1663
+		// send query, get response
1664
+		////////////////////////////
1665
+
1666
+		$len = strlen($req);
1667
+		$req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len) . $req; // add header
1668
+		if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_KEYWORDS))) {
1669
+			$this->mbPop();
1670
+			return false;
1671
+		}
1672
+
1673
+		//////////////////
1674
+		// parse response
1675
+		//////////////////
1676
+
1677
+		$pos = 0;
1678
+		$res = array();
1679
+		$rlen = strlen($response);
1680
+		list(, $nwords) = unpack('N*', substr($response, $pos, 4));
1681
+		$pos += 4;
1682
+		for ($i = 0; $i < $nwords; $i++) {
1683
+			list(, $len) = unpack('N*', substr($response, $pos, 4));
1684
+			$pos += 4;
1685
+			$tokenized = $len ? substr($response, $pos, $len) : '';
1686
+			$pos += $len;
1687
+
1688
+			list(, $len) = unpack('N*', substr($response, $pos, 4));
1689
+			$pos += 4;
1690
+			$normalized = $len ? substr($response, $pos, $len) : '';
1691
+			$pos += $len;
1692
+
1693
+			$res[] = array(
1694
+				'tokenized' => $tokenized,
1695
+				'normalized' => $normalized
1696
+			);
1697
+
1698
+			if ($hits) {
1699
+				list($ndocs, $nhits) = array_values(unpack('N*N*', substr($response, $pos, 8)));
1700
+				$pos += 8;
1701
+				$res[$i]['docs'] = $ndocs;
1702
+				$res[$i]['hits'] = $nhits;
1703
+			}
1704
+
1705
+			if ($pos > $rlen) {
1706
+				$this->error = 'incomplete reply';
1707
+				$this->mbPop();
1708
+				return false;
1709
+			}
1710
+		}
1711
+
1712
+		$this->mbPop();
1713
+		return $res;
1714
+	}
1715
+
1716
+	public function escapeString($string)
1717
+	{
1718
+		$from = array('\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=', '<');
1719
+		$to   = array('\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=', '\<');
1720
+
1721
+		return str_replace($from, $to, $string);
1722
+	}
1723
+
1724
+	/////////////////////////////////////////////////////////////////////////////
1725
+	// attribute updates
1726
+	/////////////////////////////////////////////////////////////////////////////
1727
+
1728
+	/// batch update given attributes in given rows in given indexes
1729
+	/// returns amount of updated documents (0 or more) on success, or -1 on failure
1730
+	public function updateAttributes($index, $attrs, $values, $mva = false, $ignorenonexistent = false)
1731
+	{
1732
+		// verify everything
1733
+		assert(is_string($index));
1734
+		assert(is_bool($mva));
1735
+		assert(is_bool($ignorenonexistent));
1736
+
1737
+		assert(is_array($attrs));
1738
+		foreach ($attrs as $attr) {
1739
+			assert(is_string($attr));
1740
+		}
1741
+
1742
+		assert(is_array($values));
1743
+		foreach ($values as $id => $entry) {
1744
+			assert(is_numeric($id));
1745
+			assert(is_array($entry));
1746
+			assert(count($entry) == count($attrs));
1747
+			foreach ($entry as $v) {
1748
+				if ($mva) {
1749
+					assert(is_array($v));
1750
+					foreach ($v as $vv) {
1751
+						assert(is_int($vv));
1752
+					}
1753
+				} else {
1754
+					assert(is_int($v));
1755
+				}
1756
+			}
1757
+		}
1758
+
1759
+		// build request
1760
+		$this->mbPush();
1761
+		$req = pack('N', strlen($index)) . $index;
1762
+
1763
+		$req .= pack('N', count($attrs));
1764
+		$req .= pack('N', $ignorenonexistent ? 1 : 0);
1765
+		foreach ($attrs as $attr) {
1766
+			$req .= pack('N', strlen($attr)) . $attr;
1767
+			$req .= pack('N', $mva ? 1 : 0);
1768
+		}
1769
+
1770
+		$req .= pack('N', count($values));
1771
+		foreach ($values as $id => $entry) {
1772
+			$req .= sphPackU64($id);
1773
+			foreach ($entry as $v) {
1774
+				$req .= pack('N', $mva ? count($v) : $v);
1775
+				if ($mva) {
1776
+					foreach ($v as $vv) {
1777
+						$req .= pack('N', $vv);
1778
+					}
1779
+				}
1780
+			}
1781
+		}
1782
+
1783
+		// connect, send query, get response
1784
+		if (!($fp = $this->connect())) {
1785
+			$this->mbPop();
1786
+			return -1;
1787
+		}
1788
+
1789
+		$len = strlen($req);
1790
+		$req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len) . $req; // add header
1791
+		if (!$this->send($fp, $req, $len + 8)) {
1792
+			$this->mbPop();
1793
+			return -1;
1794
+		}
1795
+
1796
+		if (!($response = $this->getResponse($fp, VER_COMMAND_UPDATE))) {
1797
+			$this->mbPop();
1798
+			return -1;
1799
+		}
1800
+
1801
+		// parse response
1802
+		list(, $updated) = unpack('N*', substr($response, 0, 4));
1803
+		$this->mbPop();
1804
+		return $updated;
1805
+	}
1806
+
1807
+	/////////////////////////////////////////////////////////////////////////////
1808
+	// persistent connections
1809
+	/////////////////////////////////////////////////////////////////////////////
1810
+
1811
+	public function open()
1812
+	{
1813
+		if ($this->socket !== false) {
1814
+			$this->error = 'already connected';
1815
+			return false;
1816
+		}
1817
+		if (!($fp = $this->connect()))
1818
+			return false;
1819
+
1820
+		// command, command version = 0, body length = 4, body = 1
1821
+		$req = pack('nnNN', SEARCHD_COMMAND_PERSIST, 0, 4, 1);
1822
+		if (!$this->send($fp, $req, 12)) {
1823
+			return false;
1824
+		}
1825
+
1826
+		$this->socket = $fp;
1827
+		return true;
1828
+	}
1829
+
1830
+	public function close()
1831
+	{
1832
+		if ($this->socket === false) {
1833
+			$this->error = 'not connected';
1834
+			return false;
1835
+		}
1836
+
1837
+		fclose($this->socket);
1838
+		$this->socket = false;
1839
+
1840
+		return true;
1841
+	}
1842
+
1843
+	//////////////////////////////////////////////////////////////////////////
1844
+	// status
1845
+	//////////////////////////////////////////////////////////////////////////
1846
+
1847
+	public function status($session = false)
1848
+	{
1849
+		assert(is_bool($session));
1850
+
1851
+		$this->mbPush();
1852
+		if (!($fp = $this->connect())) {
1853
+			$this->mbPop();
1854
+			return false;
1855
+		}
1856
+
1857
+		$req = pack('nnNN', SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, $session ? 0 : 1); // len=4, body=1
1858
+		if (!$this->send($fp, $req, 12) || !($response = $this->getResponse($fp, VER_COMMAND_STATUS))) {
1859
+			$this->mbPop();
1860
+			return false;
1861
+		}
1862
+
1863
+		$res = substr($response, 4); // just ignore length, error handling, etc
1864
+		$p = 0;
1865
+		list($rows, $cols) = array_values(unpack('N*N*', substr($response, $p, 8)));
1866
+		$p += 8;
1867
+
1868
+		$res = array();
1869
+		for ($i = 0; $i < $rows; $i++) {
1870
+			for ($j = 0; $j < $cols; $j++) {
1871
+				list(, $len) = unpack('N*', substr($response, $p, 4));
1872
+				$p += 4;
1873
+				$res[$i][] = substr($response, $p, $len);
1874
+				$p += $len;
1875
+			}
1876
+		}
1877
+
1878
+		$this->mbPop();
1879
+		return $res;
1880
+	}
1881
+
1882
+	//////////////////////////////////////////////////////////////////////////
1883
+	// flush
1884
+	//////////////////////////////////////////////////////////////////////////
1885
+
1886
+	public function flushAttributes()
1887
+	{
1888
+		$this->mbPush();
1889
+		if (!($fp = $this->connect())) {
1890
+			$this->mbPop();
1891
+			return -1;
1892
+		}
1893
+
1894
+		$req = pack('nnN', SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0); // len=0
1895
+		if (!$this->send($fp, $req, 8) || !($response = $this->getResponse($fp, VER_COMMAND_FLUSHATTRS))) {
1896
+			$this->mbPop();
1897
+			return -1;
1898
+		}
1899
+
1900
+		$tag = -1;
1901
+		if (strlen($response) == 4) {
1902
+			list(, $tag) = unpack('N*', $response);
1903
+		} else {
1904
+			$this->error = 'unexpected response length';
1905
+		}
1906
+
1907
+		$this->mbPop();
1908
+		return $tag;
1909
+	}
1910 1910
 }
Please login to merge, or discard this patch.
Spacing   +227 added lines, -227 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:
@@ -143,19 +143,19 @@  discard block
 block discarded – undo
143 143
     assert(is_numeric($v));
144 144
 
145 145
     // x64
146
-    if (PHP_INT_SIZE >= 8) {
146
+    if (PHP_INT_SIZE>=8) {
147 147
         $v = (int)$v;
148 148
         return pack('NN', $v >> 32, $v & 0xFFFFFFFF);
149 149
     }
150 150
 
151 151
     // x32, int
152 152
     if (is_int($v)) {
153
-        return pack('NN', $v < 0 ? -1 : 0, $v);
153
+        return pack('NN', $v<0 ? -1 : 0, $v);
154 154
     }
155 155
 
156 156
     // x32, bcmath
157 157
     if (function_exists('bcmul')) {
158
-        if (bccomp($v, 0) == -1) {
158
+        if (bccomp($v, 0)==-1) {
159 159
             $v = bcadd('18446744073709551616', $v);
160 160
         }
161 161
         $h = bcdiv($v, '4294967296', 0);
@@ -173,8 +173,8 @@  discard block
 block discarded – undo
173 173
     $l = $m - ($q * 4294967296.0);
174 174
     $h = $hi * 2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
175 175
 
176
-    if ($v < 0) {
177
-        if ($l == 0) {
176
+    if ($v<0) {
177
+        if ($l==0) {
178 178
             $h = 4294967296.0 - $h;
179 179
         } else {
180 180
             $h = 4294967295.0 - $h;
@@ -190,8 +190,8 @@  discard block
 block discarded – undo
190 190
     assert(is_numeric($v));
191 191
 
192 192
     // x64
193
-    if (PHP_INT_SIZE >= 8) {
194
-        assert($v >= 0);
193
+    if (PHP_INT_SIZE>=8) {
194
+        assert($v>=0);
195 195
 
196 196
         // x64, int
197 197
         if (is_int($v)) {
@@ -247,16 +247,16 @@  discard block
 block discarded – undo
247 247
 {
248 248
     list($hi, $lo) = array_values(unpack('N*N*', $v));
249 249
 
250
-    if (PHP_INT_SIZE >= 8) {
251
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
250
+    if (PHP_INT_SIZE>=8) {
251
+        if ($hi<0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
252 252
             $hi += 1 << 32;
253 253
         }
254
-        if ($lo < 0) {
254
+        if ($lo<0) {
255 255
             $lo += 1 << 32;
256 256
         }
257 257
 
258 258
         // x64, int
259
-        if ($hi <= 2147483647) {
259
+        if ($hi<=2147483647) {
260 260
             return ($hi << 32) + $lo;
261 261
         }
262 262
 
@@ -269,20 +269,20 @@  discard block
 block discarded – undo
269 269
         $C = 100000;
270 270
         $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
271 271
         $l = (($hi % $C) << 32) + ($lo % $C);
272
-        if ($l > $C) {
272
+        if ($l>$C) {
273 273
             $h += (int)($l / $C);
274 274
             $l  = $l % $C;
275 275
         }
276 276
 
277
-        if ($h == 0) {
277
+        if ($h==0) {
278 278
             return $l;
279 279
         }
280 280
         return sprintf('%d%05d', $h, $l);
281 281
     }
282 282
 
283 283
     // x32, int
284
-    if ($hi == 0) {
285
-        if ($lo > 0) {
284
+    if ($hi==0) {
285
+        if ($lo>0) {
286 286
             return $lo;
287 287
         }
288 288
         return sprintf('%u', $lo);
@@ -309,10 +309,10 @@  discard block
 block discarded – undo
309 309
 
310 310
     $h = sprintf('%.0f', $h);
311 311
     $l = sprintf('%07.0f', $l);
312
-    if ($h == '0') {
312
+    if ($h=='0') {
313 313
         return sprintf('%.0f', (float)$l);
314 314
     }
315
-    return $h . $l;
315
+    return $h.$l;
316 316
 }
317 317
 
318 318
 // unpack 64-bit signed
@@ -321,24 +321,24 @@  discard block
 block discarded – undo
321 321
     list($hi, $lo) = array_values(unpack('N*N*', $v));
322 322
 
323 323
     // x64
324
-    if (PHP_INT_SIZE >= 8) {
325
-        if ($hi < 0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
324
+    if (PHP_INT_SIZE>=8) {
325
+        if ($hi<0) { // because php 5.2.2 to 5.2.5 is totally fucked up again
326 326
             $hi += 1 << 32;
327 327
         }
328
-        if ($lo < 0) {
328
+        if ($lo<0) {
329 329
             $lo += 1 << 32;
330 330
         }
331 331
 
332 332
         return ($hi << 32) + $lo;
333 333
     }
334 334
 
335
-    if ($hi == 0) { // x32, int
336
-        if ($lo > 0) {
335
+    if ($hi==0) { // x32, int
336
+        if ($lo>0) {
337 337
             return $lo;
338 338
         }
339 339
         return sprintf('%u', $lo);
340
-    } elseif ($hi == -1) { // x32, int
341
-        if ($lo < 0) {
340
+    } elseif ($hi==-1) { // x32, int
341
+        if ($lo<0) {
342 342
             return $lo;
343 343
         }
344 344
         return sprintf('%.0f', $lo - 4294967296.0);
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
 
347 347
     $neg = '';
348 348
     $c = 0;
349
-    if ($hi < 0) {
349
+    if ($hi<0) {
350 350
         $hi = ~$hi;
351 351
         $lo = ~$lo;
352 352
         $c = 1;
@@ -358,7 +358,7 @@  discard block
 block discarded – undo
358 358
 
359 359
     // x32, bcmath
360 360
     if (function_exists('bcmul')) {
361
-        return $neg . bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
361
+        return $neg.bcadd(bcadd($lo, bcmul($hi, '4294967296')), $c);
362 362
     }
363 363
 
364 364
     // x32, no-bcmath
@@ -371,25 +371,25 @@  discard block
 block discarded – undo
371 371
     $mq = floor($m / 10000000.0);
372 372
     $l = $m - $mq * 10000000.0 + $c;
373 373
     $h = $q * 4294967296.0 + $r * 429.0 + $mq;
374
-    if ($l == 10000000) {
374
+    if ($l==10000000) {
375 375
         $l = 0;
376 376
         $h += 1;
377 377
     }
378 378
 
379 379
     $h = sprintf('%.0f', $h);
380 380
     $l = sprintf('%07.0f', $l);
381
-    if ($h == '0') {
382
-        return $neg . sprintf('%.0f', (float)$l);
381
+    if ($h=='0') {
382
+        return $neg.sprintf('%.0f', (float)$l);
383 383
     }
384
-    return $neg . $h . $l;
384
+    return $neg.$h.$l;
385 385
 }
386 386
 
387 387
 
388 388
 function sphFixUint($value)
389 389
 {
390
-    if (PHP_INT_SIZE >= 8) {
390
+    if (PHP_INT_SIZE>=8) {
391 391
         // x64 route, workaround broken unpack() in 5.2.2+
392
-        if ($value < 0) {
392
+        if ($value<0) {
393 393
             $value += 1 << 32;
394 394
         }
395 395
         return $value;
@@ -471,7 +471,7 @@  discard block
 block discarded – undo
471 471
 
472 472
     public function __destruct()
473 473
     {
474
-        if ($this->socket !== false) {
474
+        if ($this->socket!==false) {
475 475
             fclose($this->socket);
476 476
         }
477 477
     }
@@ -498,19 +498,19 @@  discard block
 block discarded – undo
498 498
     public function setServer($host, $port = 0)
499 499
     {
500 500
         assert(is_string($host));
501
-        if ($host[0] == '/') {
502
-            $this->path = 'unix://' . $host;
501
+        if ($host[0]=='/') {
502
+            $this->path = 'unix://'.$host;
503 503
             return;
504 504
         }
505
-        if (substr($host, 0, 7) == 'unix://') {
505
+        if (substr($host, 0, 7)=='unix://') {
506 506
             $this->path = $host;
507 507
             return;
508 508
         }
509 509
 
510 510
         $this->host = $host;
511 511
         $port = intval($port);
512
-        assert(0 <= $port && $port < 65536);
513
-        $this->port = $port == 0 ? 9312 : $port;
512
+        assert(0<=$port && $port<65536);
513
+        $this->port = $port==0 ? 9312 : $port;
514 514
         $this->path = '';
515 515
     }
516 516
 
@@ -524,7 +524,7 @@  discard block
 block discarded – undo
524 524
 
525 525
     protected function send($handle, $data, $length)
526 526
     {
527
-        if (feof($handle) || fwrite($handle, $data, $length) !== $length) {
527
+        if (feof($handle) || fwrite($handle, $data, $length)!==$length) {
528 528
             $this->error = 'connection unexpectedly closed (timed out?)';
529 529
             $this->conn_error = true;
530 530
             return false;
@@ -555,7 +555,7 @@  discard block
 block discarded – undo
555 555
     /// connect to searchd server
556 556
     protected function connect()
557 557
     {
558
-        if ($this->socket !== false) {
558
+        if ($this->socket!==false) {
559 559
             // we are in persistent connection mode, so we have a socket
560 560
             // however, need to check whether it's still alive
561 561
             if (!@feof($this->socket)) {
@@ -578,7 +578,7 @@  discard block
 block discarded – undo
578 578
             $port = $this->port;
579 579
         }
580 580
 
581
-        if ($this->timeout <= 0) {
581
+        if ($this->timeout<=0) {
582 582
             $fp = @fsockopen($host, $port, $errno, $errstr);
583 583
         } else {
584 584
             $fp = @fsockopen($host, $port, $errno, $errstr, $this->timeout);
@@ -610,7 +610,7 @@  discard block
 block discarded – undo
610 610
         // check version
611 611
         list(, $v) = unpack('N*', fread($fp, 4));
612 612
         $v = (int)$v;
613
-        if ($v < 1) {
613
+        if ($v<1) {
614 614
             fclose($fp);
615 615
             $this->error = "expected searchd protocol version 1+, got version '$v'";
616 616
             return false;
@@ -626,10 +626,10 @@  discard block
 block discarded – undo
626 626
         $len = 0;
627 627
 
628 628
         $header = fread($fp, 8);
629
-        if (strlen($header) == 8) {
629
+        if (strlen($header)==8) {
630 630
             list($status, $ver, $len) = array_values(unpack('n2a/Nb', $header));
631 631
             $left = $len;
632
-            while ($left > 0 && !feof($fp)) {
632
+            while ($left>0 && !feof($fp)) {
633 633
                 $chunk = fread($fp, min(8192, $left));
634 634
                 if ($chunk) {
635 635
                     $response .= $chunk;
@@ -637,13 +637,13 @@  discard block
 block discarded – undo
637 637
                 }
638 638
             }
639 639
         }
640
-        if ($this->socket === false) {
640
+        if ($this->socket===false) {
641 641
             fclose($fp);
642 642
         }
643 643
 
644 644
         // check response
645 645
         $read = strlen($response);
646
-        if (!$response || $read != $len) {
646
+        if (!$response || $read!=$len) {
647 647
             $this->error = $len
648 648
                 ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
649 649
                 : 'received zero-sized searchd response';
@@ -651,26 +651,26 @@  discard block
 block discarded – undo
651 651
         }
652 652
 
653 653
         // check status
654
-        if ($status == SEARCHD_WARNING) {
654
+        if ($status==SEARCHD_WARNING) {
655 655
             list(, $wlen) = unpack('N*', substr($response, 0, 4));
656 656
             $this->warning = substr($response, 4, $wlen);
657 657
             return substr($response, 4 + $wlen);
658 658
         }
659
-        if ($status == SEARCHD_ERROR) {
660
-            $this->error = 'searchd error: ' . substr($response, 4);
659
+        if ($status==SEARCHD_ERROR) {
660
+            $this->error = 'searchd error: '.substr($response, 4);
661 661
             return false;
662 662
         }
663
-        if ($status == SEARCHD_RETRY) {
664
-            $this->error = 'temporary searchd error: ' . substr($response, 4);
663
+        if ($status==SEARCHD_RETRY) {
664
+            $this->error = 'temporary searchd error: '.substr($response, 4);
665 665
             return false;
666 666
         }
667
-        if ($status != SEARCHD_OK) {
667
+        if ($status!=SEARCHD_OK) {
668 668
             $this->error = "unknown status code '$status'";
669 669
             return false;
670 670
         }
671 671
 
672 672
         // check version
673
-        if ($ver < $client_ver) {
673
+        if ($ver<$client_ver) {
674 674
             $this->warning = sprintf(
675 675
                 'searchd command v.%d.%d older than client\'s v.%d.%d, some options might not work',
676 676
                 $ver >> 8,
@@ -693,15 +693,15 @@  discard block
 block discarded – undo
693 693
     {
694 694
         assert(is_int($offset));
695 695
         assert(is_int($limit));
696
-        assert($offset >= 0);
697
-        assert($limit > 0);
698
-        assert($max >= 0);
696
+        assert($offset>=0);
697
+        assert($limit>0);
698
+        assert($max>=0);
699 699
         $this->offset = $offset;
700 700
         $this->limit = $limit;
701
-        if ($max > 0) {
701
+        if ($max>0) {
702 702
             $this->max_matches = $max;
703 703
         }
704
-        if ($cutoff > 0) {
704
+        if ($cutoff>0) {
705 705
             $this->cutoff = $cutoff;
706 706
         }
707 707
     }
@@ -711,7 +711,7 @@  discard block
 block discarded – undo
711 711
     public function setMaxQueryTime($max)
712 712
     {
713 713
         assert(is_int($max));
714
-        assert($max >= 0);
714
+        assert($max>=0);
715 715
         $this->max_query_time = $max;
716 716
     }
717 717
 
@@ -723,21 +723,21 @@  discard block
 block discarded – undo
723 723
             E_USER_DEPRECATED
724 724
         );
725 725
         assert(
726
-            $mode == SPH_MATCH_ALL ||
727
-            $mode == SPH_MATCH_ANY ||
728
-            $mode == SPH_MATCH_PHRASE ||
729
-            $mode == SPH_MATCH_BOOLEAN ||
730
-            $mode == SPH_MATCH_EXTENDED ||
731
-            $mode == SPH_MATCH_FULLSCAN ||
732
-            $mode == SPH_MATCH_EXTENDED2
726
+            $mode==SPH_MATCH_ALL ||
727
+            $mode==SPH_MATCH_ANY ||
728
+            $mode==SPH_MATCH_PHRASE ||
729
+            $mode==SPH_MATCH_BOOLEAN ||
730
+            $mode==SPH_MATCH_EXTENDED ||
731
+            $mode==SPH_MATCH_FULLSCAN ||
732
+            $mode==SPH_MATCH_EXTENDED2
733 733
         );
734 734
         $this->mode = $mode;
735 735
     }
736 736
 
737 737
     /// set ranking mode
738
-    public function setRankingMode($ranker, $rankexpr='')
738
+    public function setRankingMode($ranker, $rankexpr = '')
739 739
     {
740
-        assert($ranker === 0 || $ranker >= 1 && $ranker < SPH_RANK_TOTAL);
740
+        assert($ranker===0 || $ranker>=1 && $ranker<SPH_RANK_TOTAL);
741 741
         assert(is_string($rankexpr));
742 742
         $this->ranker = $ranker;
743 743
         $this->rank_expr = $rankexpr;
@@ -746,16 +746,16 @@  discard block
 block discarded – undo
746 746
     /// set matches sorting mode
747 747
     public function setSortMode($mode, $sortby = '')
748 748
     {
749
-        assert (
750
-            $mode == SPH_SORT_RELEVANCE ||
751
-            $mode == SPH_SORT_ATTR_DESC ||
752
-            $mode == SPH_SORT_ATTR_ASC ||
753
-            $mode == SPH_SORT_TIME_SEGMENTS ||
754
-            $mode == SPH_SORT_EXTENDED ||
755
-            $mode == SPH_SORT_EXPR
749
+        assert(
750
+            $mode==SPH_SORT_RELEVANCE ||
751
+            $mode==SPH_SORT_ATTR_DESC ||
752
+            $mode==SPH_SORT_ATTR_ASC ||
753
+            $mode==SPH_SORT_TIME_SEGMENTS ||
754
+            $mode==SPH_SORT_EXTENDED ||
755
+            $mode==SPH_SORT_EXPR
756 756
         );
757 757
         assert(is_string($sortby));
758
-        assert($mode == SPH_SORT_RELEVANCE || strlen($sortby) > 0);
758
+        assert($mode==SPH_SORT_RELEVANCE || strlen($sortby)>0);
759 759
 
760 760
         $this->sort = $mode;
761 761
         $this->sort_by = $sortby;
@@ -799,7 +799,7 @@  discard block
 block discarded – undo
799 799
     {
800 800
         assert(is_numeric($min));
801 801
         assert(is_numeric($max));
802
-        assert($min <= $max);
802
+        assert($min<=$max);
803 803
         $this->min_id = $min;
804 804
         $this->max_id = $max;
805 805
     }
@@ -847,7 +847,7 @@  discard block
 block discarded – undo
847 847
         assert(is_string($attribute));
848 848
         assert(is_numeric($min));
849 849
         assert(is_numeric($max));
850
-        assert($min <= $max);
850
+        assert($min<=$max);
851 851
 
852 852
         $this->filters[] = array(
853 853
             'type' => SPH_FILTER_RANGE,
@@ -865,7 +865,7 @@  discard block
 block discarded – undo
865 865
         assert(is_string($attribute));
866 866
         assert(is_float($min));
867 867
         assert(is_float($max));
868
-        assert($min <= $max);
868
+        assert($min<=$max);
869 869
 
870 870
         $this->filters[] = array(
871 871
             'type' => SPH_FILTER_FLOATRANGE,
@@ -900,12 +900,12 @@  discard block
 block discarded – undo
900 900
         assert(is_string($attribute));
901 901
         assert(is_string($groupsort));
902 902
         assert(
903
-            $func == SPH_GROUPBY_DAY ||
904
-            $func == SPH_GROUPBY_WEEK ||
905
-            $func == SPH_GROUPBY_MONTH ||
906
-            $func == SPH_GROUPBY_YEAR ||
907
-            $func == SPH_GROUPBY_ATTR ||
908
-            $func == SPH_GROUPBY_ATTRPAIR
903
+            $func==SPH_GROUPBY_DAY ||
904
+            $func==SPH_GROUPBY_WEEK ||
905
+            $func==SPH_GROUPBY_MONTH ||
906
+            $func==SPH_GROUPBY_YEAR ||
907
+            $func==SPH_GROUPBY_ATTR ||
908
+            $func==SPH_GROUPBY_ATTRPAIR
909 909
         );
910 910
 
911 911
         $this->group_by = $attribute;
@@ -923,8 +923,8 @@  discard block
 block discarded – undo
923 923
     /// set distributed retries count and delay
924 924
     public function setRetries($count, $delay = 0)
925 925
     {
926
-        assert(is_int($count) && $count >= 0);
927
-        assert(is_int($delay) && $delay >= 0);
926
+        assert(is_int($count) && $count>=0);
927
+        assert(is_int($delay) && $delay>=0);
928 928
         $this->retry_count = $count;
929 929
         $this->retry_delay = $delay;
930 930
     }
@@ -981,12 +981,12 @@  discard block
 block discarded – undo
981 981
             'global_idf',
982 982
             'low_priority'
983 983
         );
984
-        $flags = array (
984
+        $flags = array(
985 985
             'reverse_scan' => array(0, 1),
986 986
             'sort_method' => array('pq', 'kbuffer'),
987 987
             'max_predicted_time' => array(0),
988 988
             'boolean_simplify' => array(true, false),
989
-            'idf' => array ('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
989
+            'idf' => array('normalized', 'plain', 'tfidf_normalized', 'tfidf_unnormalized'),
990 990
             'global_idf' => array(true, false),
991 991
             'low_priority' => array(true, false)
992 992
         );
@@ -994,32 +994,32 @@  discard block
 block discarded – undo
994 994
         assert(isset($flag_name, $known_names));
995 995
         assert(
996 996
             in_array($flag_value, $flags[$flag_name], true) ||
997
-            ($flag_name == 'max_predicted_time' && is_int($flag_value) && $flag_value >= 0)
997
+            ($flag_name=='max_predicted_time' && is_int($flag_value) && $flag_value>=0)
998 998
         );
999 999
 
1000
-        if ($flag_name == 'reverse_scan') {
1001
-            $this->query_flags = sphSetBit($this->query_flags, 0, $flag_value == 1);
1000
+        if ($flag_name=='reverse_scan') {
1001
+            $this->query_flags = sphSetBit($this->query_flags, 0, $flag_value==1);
1002 1002
         }
1003
-        if ($flag_name == 'sort_method') {
1004
-            $this->query_flags = sphSetBit($this->query_flags, 1, $flag_value == 'kbuffer');
1003
+        if ($flag_name=='sort_method') {
1004
+            $this->query_flags = sphSetBit($this->query_flags, 1, $flag_value=='kbuffer');
1005 1005
         }
1006
-        if ($flag_name == 'max_predicted_time') {
1007
-            $this->query_flags = sphSetBit($this->query_flags, 2, $flag_value > 0);
1006
+        if ($flag_name=='max_predicted_time') {
1007
+            $this->query_flags = sphSetBit($this->query_flags, 2, $flag_value>0);
1008 1008
             $this->predicted_time = (int)$flag_value;
1009 1009
         }
1010
-        if ($flag_name == 'boolean_simplify') {
1010
+        if ($flag_name=='boolean_simplify') {
1011 1011
             $this->query_flags = sphSetBit($this->query_flags, 3, $flag_value);
1012 1012
         }
1013
-        if ($flag_name == 'idf' && ($flag_value == 'normalized' || $flag_value == 'plain')) {
1014
-            $this->query_flags = sphSetBit($this->query_flags, 4, $flag_value == 'plain');
1013
+        if ($flag_name=='idf' && ($flag_value=='normalized' || $flag_value=='plain')) {
1014
+            $this->query_flags = sphSetBit($this->query_flags, 4, $flag_value=='plain');
1015 1015
         }
1016
-        if ($flag_name == 'global_idf') {
1016
+        if ($flag_name=='global_idf') {
1017 1017
             $this->query_flags = sphSetBit($this->query_flags, 5, $flag_value);
1018 1018
         }
1019
-        if ($flag_name == 'idf' && ($flag_value == 'tfidf_normalized' || $flag_value == 'tfidf_unnormalized')) {
1020
-            $this->query_flags = sphSetBit($this->query_flags, 6, $flag_value == 'tfidf_normalized');
1019
+        if ($flag_name=='idf' && ($flag_value=='tfidf_normalized' || $flag_value=='tfidf_unnormalized')) {
1020
+            $this->query_flags = sphSetBit($this->query_flags, 6, $flag_value=='tfidf_normalized');
1021 1021
         }
1022
-        if ($flag_name == 'low_priority') {
1022
+        if ($flag_name=='low_priority') {
1023 1023
             $this->query_flags = sphSetBit($this->query_flags, 8, $flag_value);
1024 1024
         }
1025 1025
     }
@@ -1030,8 +1030,8 @@  discard block
 block discarded – undo
1030 1030
         assert(is_string($orderby));
1031 1031
         assert(is_int($offset));
1032 1032
         assert(is_int($limit));
1033
-        assert($offset >= 0);
1034
-        assert($limit > 0);
1033
+        assert($offset>=0);
1034
+        assert($limit>0);
1035 1035
 
1036 1036
         $this->outer_order_by = $orderby;
1037 1037
         $this->outer_offset = $offset;
@@ -1096,7 +1096,7 @@  discard block
 block discarded – undo
1096 1096
 
1097 1097
         $this->error = $results[0]['error'];
1098 1098
         $this->warning = $results[0]['warning'];
1099
-        if ($results[0]['status'] == SEARCHD_ERROR) {
1099
+        if ($results[0]['status']==SEARCHD_ERROR) {
1100 1100
             return false;
1101 1101
         } else {
1102 1102
             return $results[0];
@@ -1120,24 +1120,24 @@  discard block
 block discarded – undo
1120 1120
 
1121 1121
         // build request
1122 1122
         $req = pack('NNNNN', $this->query_flags, $this->offset, $this->limit, $this->mode, $this->ranker);
1123
-        if ($this->ranker == SPH_RANK_EXPR) {
1124
-            $req .= pack('N', strlen($this->rank_expr)) . $this->rank_expr;
1123
+        if ($this->ranker==SPH_RANK_EXPR) {
1124
+            $req .= pack('N', strlen($this->rank_expr)).$this->rank_expr;
1125 1125
         }
1126 1126
         $req .= pack('N', $this->sort); // (deprecated) sort mode
1127
-        $req .= pack('N', strlen($this->sort_by)) . $this->sort_by;
1128
-        $req .= pack('N', strlen($query)) . $query; // query itself
1127
+        $req .= pack('N', strlen($this->sort_by)).$this->sort_by;
1128
+        $req .= pack('N', strlen($query)).$query; // query itself
1129 1129
         $req .= pack('N', count($this->weights)); // weights
1130 1130
         foreach ($this->weights as $weight) {
1131 1131
             $req .= pack('N', (int)$weight);
1132 1132
         }
1133
-        $req .= pack('N', strlen($index)) . $index; // indexes
1133
+        $req .= pack('N', strlen($index)).$index; // indexes
1134 1134
         $req .= pack('N', 1); // id64 range marker
1135
-        $req .= sphPackU64($this->min_id) . sphPackU64($this->max_id); // id64 range
1135
+        $req .= sphPackU64($this->min_id).sphPackU64($this->max_id); // id64 range
1136 1136
 
1137 1137
         // filters
1138 1138
         $req .= pack('N', count($this->filters));
1139 1139
         foreach ($this->filters as $filter) {
1140
-            $req .= pack('N', strlen($filter['attr'])) . $filter['attr'];
1140
+            $req .= pack('N', strlen($filter['attr'])).$filter['attr'];
1141 1141
             $req .= pack('N', $filter['type']);
1142 1142
             switch ($filter['type']) {
1143 1143
                 case SPH_FILTER_VALUES:
@@ -1147,13 +1147,13 @@  discard block
 block discarded – undo
1147 1147
                     }
1148 1148
                     break;
1149 1149
                 case SPH_FILTER_RANGE:
1150
-                    $req .= sphPackI64($filter['min']) . sphPackI64($filter['max']);
1150
+                    $req .= sphPackI64($filter['min']).sphPackI64($filter['max']);
1151 1151
                     break;
1152 1152
                 case SPH_FILTER_FLOATRANGE:
1153
-                    $req .= $this->packFloat($filter['min']) . $this->packFloat($filter['max']);
1153
+                    $req .= $this->packFloat($filter['min']).$this->packFloat($filter['max']);
1154 1154
                     break;
1155 1155
                 case SPH_FILTER_STRING:
1156
-                    $req .= pack('N', strlen($filter['value'])) . $filter['value'];
1156
+                    $req .= pack('N', strlen($filter['value'])).$filter['value'];
1157 1157
                     break;
1158 1158
                 default:
1159 1159
                     assert(0 && 'internal error: unhandled filter type');
@@ -1162,27 +1162,27 @@  discard block
 block discarded – undo
1162 1162
         }
1163 1163
 
1164 1164
         // group-by clause, max-matches count, group-sort clause, cutoff count
1165
-        $req .= pack('NN', $this->group_func, strlen($this->group_by)) . $this->group_by;
1165
+        $req .= pack('NN', $this->group_func, strlen($this->group_by)).$this->group_by;
1166 1166
         $req .= pack('N', $this->max_matches);
1167
-        $req .= pack('N', strlen($this->group_sort)) . $this->group_sort;
1167
+        $req .= pack('N', strlen($this->group_sort)).$this->group_sort;
1168 1168
         $req .= pack('NNN', $this->cutoff, $this->retry_count, $this->retry_delay);
1169
-        $req .= pack('N', strlen($this->group_distinct)) . $this->group_distinct;
1169
+        $req .= pack('N', strlen($this->group_distinct)).$this->group_distinct;
1170 1170
 
1171 1171
         // anchor point
1172 1172
         if (empty($this->anchor)) {
1173 1173
             $req .= pack('N', 0);
1174 1174
         } else {
1175
-            $a =& $this->anchor;
1175
+            $a = & $this->anchor;
1176 1176
             $req .= pack('N', 1);
1177
-            $req .= pack('N', strlen($a['attrlat'])) . $a['attrlat'];
1178
-            $req .= pack('N', strlen($a['attrlong'])) . $a['attrlong'];
1179
-            $req .= $this->packFloat($a['lat']) . $this->packFloat($a['long']);
1177
+            $req .= pack('N', strlen($a['attrlat'])).$a['attrlat'];
1178
+            $req .= pack('N', strlen($a['attrlong'])).$a['attrlong'];
1179
+            $req .= $this->packFloat($a['lat']).$this->packFloat($a['long']);
1180 1180
         }
1181 1181
 
1182 1182
         // per-index weights
1183 1183
         $req .= pack('N', count($this->index_weights));
1184 1184
         foreach ($this->index_weights as $idx => $weight) {
1185
-            $req .= pack('N', strlen($idx)) . $idx . pack('N', $weight);
1185
+            $req .= pack('N', strlen($idx)).$idx.pack('N', $weight);
1186 1186
         }
1187 1187
 
1188 1188
         // max query time
@@ -1191,16 +1191,16 @@  discard block
 block discarded – undo
1191 1191
         // per-field weights
1192 1192
         $req .= pack('N', count($this->field_weights));
1193 1193
         foreach ($this->field_weights as $field => $weight) {
1194
-            $req .= pack('N', strlen($field)) . $field . pack('N', $weight);
1194
+            $req .= pack('N', strlen($field)).$field.pack('N', $weight);
1195 1195
         }
1196 1196
 
1197 1197
         // comment
1198
-        $req .= pack('N', strlen($comment)) . $comment;
1198
+        $req .= pack('N', strlen($comment)).$comment;
1199 1199
 
1200 1200
         // attribute overrides
1201 1201
         $req .= pack('N', count($this->overrides));
1202 1202
         foreach ($this->overrides as $key => $entry) {
1203
-            $req .= pack('N', strlen($entry['attr'])) . $entry['attr'];
1203
+            $req .= pack('N', strlen($entry['attr'])).$entry['attr'];
1204 1204
             $req .= pack('NN', $entry['type'], count($entry['values']));
1205 1205
             foreach ($entry['values'] as $id => $val) {
1206 1206
                 assert(is_numeric($id));
@@ -1222,14 +1222,14 @@  discard block
 block discarded – undo
1222 1222
         }
1223 1223
 
1224 1224
         // select-list
1225
-        $req .= pack('N', strlen($this->select)) . $this->select;
1225
+        $req .= pack('N', strlen($this->select)).$this->select;
1226 1226
 
1227 1227
         // max_predicted_time
1228
-        if ($this->predicted_time > 0) {
1228
+        if ($this->predicted_time>0) {
1229 1229
             $req .= pack('N', (int)$this->predicted_time);
1230 1230
         }
1231 1231
 
1232
-        $req .= pack('N', strlen($this->outer_order_by)) . $this->outer_order_by;
1232
+        $req .= pack('N', strlen($this->outer_order_by)).$this->outer_order_by;
1233 1233
         $req .= pack('NN', $this->outer_offset, $this->outer_limit);
1234 1234
         if ($this->has_outer) {
1235 1235
             $req .= pack('N', 1);
@@ -1265,7 +1265,7 @@  discard block
 block discarded – undo
1265 1265
         $nreqs = count($this->reqs);
1266 1266
         $req = join('', $this->reqs);
1267 1267
         $len = 8 + strlen($req);
1268
-        $req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs) . $req; // add header
1268
+        $req = pack('nnNNN', SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs).$req; // add header
1269 1269
 
1270 1270
         if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_SEARCH))) {
1271 1271
             $this->mbPop();
@@ -1286,9 +1286,9 @@  discard block
 block discarded – undo
1286 1286
         $max = strlen($response); // max position for checks, to protect against broken responses
1287 1287
 
1288 1288
         $results = array();
1289
-        for ($ires = 0; $ires < $nreqs && $p < $max; $ires++) {
1289
+        for ($ires = 0; $ires<$nreqs && $p<$max; $ires++) {
1290 1290
             $results[] = array();
1291
-            $result =& $results[$ires];
1291
+            $result = & $results[$ires];
1292 1292
 
1293 1293
             $result['error'] = '';
1294 1294
             $result['warning'] = '';
@@ -1297,13 +1297,13 @@  discard block
 block discarded – undo
1297 1297
             list(, $status) = unpack('N*', substr($response, $p, 4));
1298 1298
             $p += 4;
1299 1299
             $result['status'] = $status;
1300
-            if ($status != SEARCHD_OK) {
1300
+            if ($status!=SEARCHD_OK) {
1301 1301
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1302 1302
                 $p += 4;
1303 1303
                 $message = substr($response, $p, $len);
1304 1304
                 $p += $len;
1305 1305
 
1306
-                if ($status == SEARCHD_WARNING) {
1306
+                if ($status==SEARCHD_WARNING) {
1307 1307
                     $result['warning'] = $message;
1308 1308
                 } else {
1309 1309
                     $result['error'] = $message;
@@ -1317,7 +1317,7 @@  discard block
 block discarded – undo
1317 1317
 
1318 1318
             list(, $nfields) = unpack('N*', substr($response, $p, 4));
1319 1319
             $p += 4;
1320
-            while ($nfields --> 0 && $p < $max) {
1320
+            while ($nfields-->0 && $p<$max) {
1321 1321
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1322 1322
                 $p += 4;
1323 1323
                 $fields[] = substr($response, $p, $len);
@@ -1327,7 +1327,7 @@  discard block
 block discarded – undo
1327 1327
 
1328 1328
             list(, $nattrs) = unpack('N*', substr($response, $p, 4));
1329 1329
             $p += 4;
1330
-            while ($nattrs --> 0 && $p < $max) {
1330
+            while ($nattrs-->0 && $p<$max) {
1331 1331
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1332 1332
                 $p += 4;
1333 1333
                 $attr = substr($response, $p, $len);
@@ -1346,7 +1346,7 @@  discard block
 block discarded – undo
1346 1346
 
1347 1347
             // read matches
1348 1348
             $idx = -1;
1349
-            while ($count --> 0 && $p < $max) {
1349
+            while ($count-->0 && $p<$max) {
1350 1350
                 // index into result array
1351 1351
                 $idx++;
1352 1352
 
@@ -1374,14 +1374,14 @@  discard block
 block discarded – undo
1374 1374
                 $attrvals = array();
1375 1375
                 foreach ($attrs as $attr => $type) {
1376 1376
                     // handle 64bit ints
1377
-                    if ($type == SPH_ATTR_BIGINT) {
1377
+                    if ($type==SPH_ATTR_BIGINT) {
1378 1378
                         $attrvals[$attr] = sphUnpackI64(substr($response, $p, 8));
1379 1379
                         $p += 8;
1380 1380
                         continue;
1381 1381
                     }
1382 1382
 
1383 1383
                     // handle floats
1384
-                    if ($type == SPH_ATTR_FLOAT) {
1384
+                    if ($type==SPH_ATTR_FLOAT) {
1385 1385
                         list(, $uval) = unpack('N*', substr($response, $p, 4));
1386 1386
                         $p += 4;
1387 1387
                         list(, $fval) = unpack('f*', pack('L', $uval));
@@ -1392,28 +1392,28 @@  discard block
 block discarded – undo
1392 1392
                     // handle everything else as unsigned ints
1393 1393
                     list(, $val) = unpack('N*', substr($response, $p, 4));
1394 1394
                     $p += 4;
1395
-                    if ($type == SPH_ATTR_MULTI) {
1395
+                    if ($type==SPH_ATTR_MULTI) {
1396 1396
                         $attrvals[$attr] = array();
1397 1397
                         $nvalues = $val;
1398
-                        while ($nvalues --> 0 && $p < $max) {
1398
+                        while ($nvalues-->0 && $p<$max) {
1399 1399
                             list(, $val) = unpack('N*', substr($response, $p, 4));
1400 1400
                             $p += 4;
1401 1401
                             $attrvals[$attr][] = sphFixUint($val);
1402 1402
                         }
1403
-                    } elseif ($type == SPH_ATTR_MULTI64) {
1403
+                    } elseif ($type==SPH_ATTR_MULTI64) {
1404 1404
                         $attrvals[$attr] = array();
1405 1405
                         $nvalues = $val;
1406
-                        while ($nvalues > 0 && $p < $max) {
1406
+                        while ($nvalues>0 && $p<$max) {
1407 1407
                             $attrvals[$attr][] = sphUnpackI64(substr($response, $p, 8));
1408 1408
                             $p += 8;
1409 1409
                             $nvalues -= 2;
1410 1410
                         }
1411
-                    } elseif ($type == SPH_ATTR_STRING) {
1411
+                    } elseif ($type==SPH_ATTR_STRING) {
1412 1412
                         $attrvals[$attr] = substr($response, $p, $val);
1413 1413
                         $p += $val;
1414
-                    } elseif ($type == SPH_ATTR_FACTORS) {
1414
+                    } elseif ($type==SPH_ATTR_FACTORS) {
1415 1415
                         $attrvals[$attr] = substr($response, $p, $val - 4);
1416
-                        $p += $val-4;
1416
+                        $p += $val - 4;
1417 1417
                     } else {
1418 1418
                         $attrvals[$attr] = sphFixUint($val);
1419 1419
                     }
@@ -1432,14 +1432,14 @@  discard block
 block discarded – undo
1432 1432
             $result['time'] = sprintf('%.3f', $msecs / 1000);
1433 1433
             $p += 16;
1434 1434
 
1435
-            while ($words --> 0 && $p < $max) {
1435
+            while ($words-->0 && $p<$max) {
1436 1436
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1437 1437
                 $p += 4;
1438 1438
                 $word = substr($response, $p, $len);
1439 1439
                 $p += $len;
1440 1440
                 list($docs, $hits) = array_values(unpack('N*N*', substr($response, $p, 8)));
1441 1441
                 $p += 8;
1442
-                $result['words'][$word] = array (
1442
+                $result['words'][$word] = array(
1443 1443
                     'docs' => sprintf('%u', $docs),
1444 1444
                     'hits' => sprintf('%u', $hits)
1445 1445
                 );
@@ -1574,23 +1574,23 @@  discard block
 block discarded – undo
1574 1574
             $flags |= 1024;
1575 1575
         }
1576 1576
         $req = pack('NN', 0, $flags); // mode=0, flags=$flags
1577
-        $req .= pack('N', strlen($index)) . $index; // req index
1578
-        $req .= pack('N', strlen($words)) . $words; // req words
1577
+        $req .= pack('N', strlen($index)).$index; // req index
1578
+        $req .= pack('N', strlen($words)).$words; // req words
1579 1579
 
1580 1580
         // options
1581
-        $req .= pack('N', strlen($opts['before_match'])) . $opts['before_match'];
1582
-        $req .= pack('N', strlen($opts['after_match'])) . $opts['after_match'];
1583
-        $req .= pack('N', strlen($opts['chunk_separator'])) . $opts['chunk_separator'];
1581
+        $req .= pack('N', strlen($opts['before_match'])).$opts['before_match'];
1582
+        $req .= pack('N', strlen($opts['after_match'])).$opts['after_match'];
1583
+        $req .= pack('N', strlen($opts['chunk_separator'])).$opts['chunk_separator'];
1584 1584
         $req .= pack('NN', (int)$opts['limit'], (int)$opts['around']);
1585 1585
         $req .= pack('NNN', (int)$opts['limit_passages'], (int)$opts['limit_words'], (int)$opts['start_passage_id']); // v.1.2
1586
-        $req .= pack('N', strlen($opts['html_strip_mode'])) . $opts['html_strip_mode'];
1587
-        $req .= pack('N', strlen($opts['passage_boundary'])) . $opts['passage_boundary'];
1586
+        $req .= pack('N', strlen($opts['html_strip_mode'])).$opts['html_strip_mode'];
1587
+        $req .= pack('N', strlen($opts['passage_boundary'])).$opts['passage_boundary'];
1588 1588
 
1589 1589
         // documents
1590 1590
         $req .= pack('N', count($docs));
1591 1591
         foreach ($docs as $doc) {
1592 1592
             assert(is_string($doc));
1593
-            $req .= pack('N', strlen($doc)) . $doc;
1593
+            $req .= pack('N', strlen($doc)).$doc;
1594 1594
         }
1595 1595
 
1596 1596
         ////////////////////////////
@@ -1598,7 +1598,7 @@  discard block
 block discarded – undo
1598 1598
         ////////////////////////////
1599 1599
 
1600 1600
         $len = strlen($req);
1601
-        $req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len) . $req; // add header
1601
+        $req = pack('nnN', SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len).$req; // add header
1602 1602
         if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_EXCERPT))) {
1603 1603
             $this->mbPop();
1604 1604
             return false;
@@ -1616,7 +1616,7 @@  discard block
 block discarded – undo
1616 1616
             list(, $len) = unpack('N*', substr($response, $pos, 4));
1617 1617
             $pos += 4;
1618 1618
 
1619
-            if ($pos + $len > $rlen) {
1619
+            if ($pos + $len>$rlen) {
1620 1620
                 $this->error = 'incomplete reply';
1621 1621
                 $this->mbPop();
1622 1622
                 return false;
@@ -1655,8 +1655,8 @@  discard block
 block discarded – undo
1655 1655
         /////////////////
1656 1656
 
1657 1657
         // v.1.0 req
1658
-        $req  = pack('N', strlen($query)) . $query; // req query
1659
-        $req .= pack('N', strlen($index)) . $index; // req index
1658
+        $req  = pack('N', strlen($query)).$query; // req query
1659
+        $req .= pack('N', strlen($index)).$index; // req index
1660 1660
         $req .= pack('N', (int)$hits);
1661 1661
 
1662 1662
         ////////////////////////////
@@ -1664,7 +1664,7 @@  discard block
 block discarded – undo
1664 1664
         ////////////////////////////
1665 1665
 
1666 1666
         $len = strlen($req);
1667
-        $req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len) . $req; // add header
1667
+        $req = pack('nnN', SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len).$req; // add header
1668 1668
         if (!$this->send($fp, $req, $len + 8) || !($response = $this->getResponse($fp, VER_COMMAND_KEYWORDS))) {
1669 1669
             $this->mbPop();
1670 1670
             return false;
@@ -1679,7 +1679,7 @@  discard block
 block discarded – undo
1679 1679
         $rlen = strlen($response);
1680 1680
         list(, $nwords) = unpack('N*', substr($response, $pos, 4));
1681 1681
         $pos += 4;
1682
-        for ($i = 0; $i < $nwords; $i++) {
1682
+        for ($i = 0; $i<$nwords; $i++) {
1683 1683
             list(, $len) = unpack('N*', substr($response, $pos, 4));
1684 1684
             $pos += 4;
1685 1685
             $tokenized = $len ? substr($response, $pos, $len) : '';
@@ -1702,7 +1702,7 @@  discard block
 block discarded – undo
1702 1702
                 $res[$i]['hits'] = $nhits;
1703 1703
             }
1704 1704
 
1705
-            if ($pos > $rlen) {
1705
+            if ($pos>$rlen) {
1706 1706
                 $this->error = 'incomplete reply';
1707 1707
                 $this->mbPop();
1708 1708
                 return false;
@@ -1715,8 +1715,8 @@  discard block
 block discarded – undo
1715 1715
 
1716 1716
     public function escapeString($string)
1717 1717
     {
1718
-        $from = array('\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=', '<');
1719
-        $to   = array('\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=', '\<');
1718
+        $from = array('\\', '(', ')', '|', '-', '!', '@', '~', '"', '&', '/', '^', '$', '=', '<');
1719
+        $to   = array('\\\\', '\(', '\)', '\|', '\-', '\!', '\@', '\~', '\"', '\&', '\/', '\^', '\$', '\=', '\<');
1720 1720
 
1721 1721
         return str_replace($from, $to, $string);
1722 1722
     }
@@ -1743,7 +1743,7 @@  discard block
 block discarded – undo
1743 1743
         foreach ($values as $id => $entry) {
1744 1744
             assert(is_numeric($id));
1745 1745
             assert(is_array($entry));
1746
-            assert(count($entry) == count($attrs));
1746
+            assert(count($entry)==count($attrs));
1747 1747
             foreach ($entry as $v) {
1748 1748
                 if ($mva) {
1749 1749
                     assert(is_array($v));
@@ -1758,12 +1758,12 @@  discard block
 block discarded – undo
1758 1758
 
1759 1759
         // build request
1760 1760
         $this->mbPush();
1761
-        $req = pack('N', strlen($index)) . $index;
1761
+        $req = pack('N', strlen($index)).$index;
1762 1762
 
1763 1763
         $req .= pack('N', count($attrs));
1764 1764
         $req .= pack('N', $ignorenonexistent ? 1 : 0);
1765 1765
         foreach ($attrs as $attr) {
1766
-            $req .= pack('N', strlen($attr)) . $attr;
1766
+            $req .= pack('N', strlen($attr)).$attr;
1767 1767
             $req .= pack('N', $mva ? 1 : 0);
1768 1768
         }
1769 1769
 
@@ -1787,7 +1787,7 @@  discard block
 block discarded – undo
1787 1787
         }
1788 1788
 
1789 1789
         $len = strlen($req);
1790
-        $req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len) . $req; // add header
1790
+        $req = pack('nnN', SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len).$req; // add header
1791 1791
         if (!$this->send($fp, $req, $len + 8)) {
1792 1792
             $this->mbPop();
1793 1793
             return -1;
@@ -1810,7 +1810,7 @@  discard block
 block discarded – undo
1810 1810
 
1811 1811
     public function open()
1812 1812
     {
1813
-        if ($this->socket !== false) {
1813
+        if ($this->socket!==false) {
1814 1814
             $this->error = 'already connected';
1815 1815
             return false;
1816 1816
         }
@@ -1829,7 +1829,7 @@  discard block
 block discarded – undo
1829 1829
 
1830 1830
     public function close()
1831 1831
     {
1832
-        if ($this->socket === false) {
1832
+        if ($this->socket===false) {
1833 1833
             $this->error = 'not connected';
1834 1834
             return false;
1835 1835
         }
@@ -1866,8 +1866,8 @@  discard block
 block discarded – undo
1866 1866
         $p += 8;
1867 1867
 
1868 1868
         $res = array();
1869
-        for ($i = 0; $i < $rows; $i++) {
1870
-            for ($j = 0; $j < $cols; $j++) {
1869
+        for ($i = 0; $i<$rows; $i++) {
1870
+            for ($j = 0; $j<$cols; $j++) {
1871 1871
                 list(, $len) = unpack('N*', substr($response, $p, 4));
1872 1872
                 $p += 4;
1873 1873
                 $res[$i][] = substr($response, $p, $len);
@@ -1898,7 +1898,7 @@  discard block
 block discarded – undo
1898 1898
         }
1899 1899
 
1900 1900
         $tag = -1;
1901
-        if (strlen($response) == 4) {
1901
+        if (strlen($response)==4) {
1902 1902
             list(, $tag) = unpack('N*', $response);
1903 1903
         } else {
1904 1904
             $this->error = 'unexpected response length';
Please login to merge, or discard this patch.