1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors:                                                             |
14    |          Israel Ekpo <iekpo@php.net>                                 |
15    |          Omar Shaban <omars@php.net>                                 |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "php_solr.h"
20 
21 /** ************************************************************************ **/
22 /** DEFINITIONS FOR SOLR INPUT DOCUMENT METHODS                              **/
23 /** ************************************************************************ **/
24 
25 /* {{{ proto void SolrInputDocument::__construct()
26 	SolrInputDocument constructor */
PHP_METHOD(SolrInputDocument,__construct)27 PHP_METHOD(SolrInputDocument, __construct)
28 {
29     zval *objptr = getThis();
30     if (solr_input_doc_ctor(objptr) == NULL)
31     {
32         return;
33     }
34 }
35 
36 /* }}} */
37 
38 /* {{{ proto void SolrInputDocument::__destruct(void)
39 	Destructor for SolrInputDocument */
PHP_METHOD(SolrInputDocument,__destruct)40 PHP_METHOD(SolrInputDocument, __destruct)
41 {
42 	solr_document_t *doc_entry = NULL;
43 
44 	/* Retrieve the document entry for this SolrDocument */
45 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) 	{
46 
47 		zend_hash_index_del(SOLR_GLOBAL(documents), doc_entry->document_index);
48 
49 		/* Keep track of how many SolrDocument instances we currently have */
50 		SOLR_GLOBAL(document_count)--;
51 
52 		return ;
53 	}
54 }
55 /* }}} */
56 
57 /* {{{ proto void SolrInputDocument::__clone(void)
58   Clones the current object. Not to be called directly. */
PHP_METHOD(SolrInputDocument,__clone)59 PHP_METHOD(SolrInputDocument, __clone)
60 {
61     RETURN_NULL();
62 }
63 /* }}} */
64 
65 /* {{{ proto void SolrInputDocument::__sleep(void)
66  Should not be called directly. Serialization is not supported. */
PHP_METHOD(SolrInputDocument,__sleep)67 PHP_METHOD(SolrInputDocument, __sleep)
68 {
69 	solr_throw_exception_ex(solr_ce_SolrIllegalOperationException, SOLR_ERROR_1005, SOLR_FILE_LINE_FUNC, SOLR_ERROR_1005_MSG);
70 }
71 /* }}} */
72 
73 /* {{{ proto void SolrInputDocument::__wakeup(void)
74  Should not be called directly. Serialization is not supported. */
PHP_METHOD(SolrInputDocument,__wakeup)75 PHP_METHOD(SolrInputDocument, __wakeup)
76 {
77 	solr_throw_exception_ex(solr_ce_SolrIllegalOperationException, SOLR_ERROR_1005, SOLR_FILE_LINE_FUNC, SOLR_ERROR_1005_MSG);
78 }
79 /* }}} */
80 
81 /* {{{ proto bool SolrInputDocument::setBoost(float document_boost)
82    Sets the boost for the document. */
PHP_METHOD(SolrInputDocument,setBoost)83 PHP_METHOD(SolrInputDocument, setBoost)
84 {
85 	double boost_value = 0.0f;
86 	solr_document_t *doc_entry = NULL;
87 
88 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &boost_value) == FAILURE) {
89 
90 		RETURN_FALSE;
91 	}
92 
93 	/* If the submitted boost_value is negative. */
94 	if (boost_value < 0.0) {
95 
96 		RETURN_FALSE;
97 	}
98 
99 	/* Retrieve the document entry for this SolrDocument */
100 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) {
101 
102 		doc_entry->document_boost = boost_value;
103 
104 		RETURN_TRUE;
105 	}
106 
107 	RETURN_FALSE;
108 }
109 /* }}} */
110 
111 /* {{{ proto float SolrInputDocument::getBoost(void)
112    Retrieves the boost for the document. */
PHP_METHOD(SolrInputDocument,getBoost)113 PHP_METHOD(SolrInputDocument, getBoost)
114 {
115 	solr_document_t *doc_entry = NULL;
116 
117 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) {
118 
119 		RETURN_DOUBLE(doc_entry->document_boost);
120 	}
121 
122 	RETURN_FALSE;
123 }
124 /* }}} */
125 
126 /* {{{ proto bool SolrInputDocument::clear(void)
127    Discards all the fields and resets the document boost to zero. */
PHP_METHOD(SolrInputDocument,clear)128 PHP_METHOD(SolrInputDocument, clear)
129 {
130 	solr_document_t *doc_entry = NULL;
131 
132 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) 	{
133 
134 		doc_entry->document_boost = 0.0;
135 		doc_entry->field_count    = 0L;
136 
137 		zend_hash_clean(doc_entry->fields);
138 
139 		RETURN_TRUE;
140 	}
141 
142 	RETURN_FALSE;
143 }
144 /* }}} */
145 
146 /* {{{ proto bool SolrInputDocument::addField(string field_name, field_value [, float field_boost])
147    Adds a field to the document. Can be called multiple times. */
PHP_METHOD(SolrInputDocument,addField)148 PHP_METHOD(SolrInputDocument, addField)
149 {
150 	solr_char_t *field_name = NULL, *field_value = NULL;
151 	COMPAT_ARG_SIZE_T field_name_length  = 0, field_value_length = 0;
152 	solr_document_t *doc_entry = NULL;
153 	double field_boost = 0.0;
154 
155 
156 	/* Process the parameters passed to the method */
157 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|d", &field_name,
158 	        &field_name_length, &field_value, &field_value_length, &field_boost) == FAILURE) {
159 		RETURN_FALSE;
160 	}
161 
162 	if (!field_name_length) {
163 		RETURN_FALSE;
164 	}
165 
166 	/* Retrieve the document entry for the SolrDocument instance */
167 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS)
168 	{
169 		solr_field_list_t *field_values      = NULL;
170 
171 		/* If the field already exists in the SolrDocument instance append the value to the field list queue */
172 		if ((field_values = (solr_field_list_t *)zend_hash_str_find_ptr(doc_entry->fields, field_name, field_name_length)) != NULL) {
173 			if (solr_document_insert_field_value(field_values, (solr_char_t *)field_value, field_boost) == FAILURE) {
174 				RETURN_FALSE;
175 			}
176 		} else {
177 
178 			/* Otherwise, create a new one and add it to the hash table */
179 			field_values     = (solr_field_list_t *)  pemalloc(sizeof(solr_field_list_t), SOLR_DOCUMENT_FIELD_PERSISTENT);
180 
181 			memset(field_values, 0, sizeof(solr_field_list_t));
182 
183 			field_values->count       = 0L;
184 			field_values->field_boost = 0.0;
185 			field_values->field_name  = (solr_char_t *) pestrdup((char *)field_name, SOLR_DOCUMENT_FIELD_PERSISTENT);
186 			field_values->head        = NULL;
187 			field_values->last        = NULL;
188 
189 			if (solr_document_insert_field_value(field_values, field_value, field_boost) == FAILURE) {
190 				solr_destroy_field_list(field_values);
191 				RETURN_FALSE;
192 			}
193 
194 			if (zend_hash_str_add_ptr(doc_entry->fields, field_name, field_name_length,(void *) field_values) == NULL) {
195 				solr_destroy_field_list(field_values);
196 				RETURN_FALSE;
197 			}
198 
199 			/* Increment field count only when HEAD is added */
200 			doc_entry->field_count++;
201 		}
202 
203 		RETURN_TRUE;
204 	}
205 
206 	RETURN_FALSE;
207 }
208 /* }}} */
209 
210 /* {{{ proto bool SolrInputDocument::updateField(string fieldName, int modifier, string value) */
PHP_METHOD(SolrInputDocument,updateField)211 PHP_METHOD(SolrInputDocument, updateField)
212 {
213     solr_char_t *field_name = NULL, *field_value = NULL;
214     COMPAT_ARG_SIZE_T field_name_length = 0, field_value_len = 0;
215     solr_document_t *doc_entry;
216     solr_field_list_t *field;
217 
218     long modifier = 0L;
219 
220     if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &field_name, &field_name_length, &modifier, &field_value, &field_value_len) == FAILURE) {
221         return;
222     }
223 
224     if (!field_name_length || !field_value_len) {
225         RETURN_FALSE;
226     }
227 
228     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == FAILURE)  {
229         return;
230     }
231 
232     switch (modifier) {
233         case SOLR_FIELD_VALUE_MOD_ADD:
234         case SOLR_FIELD_VALUE_MOD_REMOVE:
235         case SOLR_FIELD_VALUE_MOD_REMOVEREGEX:
236         case SOLR_FIELD_VALUE_MOD_SET:
237         case SOLR_FIELD_VALUE_MOD_INC:
238             break;
239 
240         default:
241             solr_throw_exception_ex(solr_ce_SolrIllegalArgumentException, SOLR_ERROR_4003, SOLR_FILE_LINE_FUNC, SOLR_ERROR_4003_MSG);
242             RETURN_FALSE;
243     }
244 
245     if ((field = zend_hash_str_find_ptr(doc_entry->fields, field_name, field_name_length)) == NULL){
246         field = (solr_field_list_t *)pemalloc(sizeof(solr_field_list_t), SOLR_DOCUMENT_FIELD_PERSISTENT);
247         memset(field, 0, sizeof(solr_field_list_t));
248         field->field_name = pestrdup(field_name, SOLR_DOCUMENT_FIELD_PERSISTENT);
249         field->count = 1;
250         field->head = NULL;
251         field->last = NULL;
252         if (modifier > 0) {
253             field->modified = 1;
254         }
255         doc_entry->field_count++;
256         if (zend_hash_str_add_ptr(doc_entry->fields, field_name, field_name_length, field) == NULL) {
257             RETURN_FALSE;
258         }
259     } else if (field->modified == 0) {
260         solr_throw_exception_ex(solr_ce_SolrIllegalOperationException, SOLR_ERROR_4004, SOLR_FILE_LINE_FUNC, SOLR_ERROR_4004_MSG);
261         RETURN_FALSE;
262     }
263 
264     solr_document_insert_field_value_ex(field, field_value, 0.0, modifier);
265 }
266 
267 /* }}} */
268 /* {{{ proto bool SolrInputDocument::setFieldBoost(string fieldname, float boost_value)
269    Sets the boost for the specified field. */
PHP_METHOD(SolrInputDocument,setFieldBoost)270 PHP_METHOD(SolrInputDocument, setFieldBoost)
271 {
272 	solr_char_t *field_name = NULL;
273 	COMPAT_ARG_SIZE_T  field_name_length  = 0;
274 	double field_boost     = 0.0;
275 	solr_document_t *doc_entry = NULL;
276 
277 	/* Process the parameters passed to the default constructor */
278 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &field_name, &field_name_length, &field_boost) == FAILURE) {
279 
280 		RETURN_FALSE;
281 	}
282 
283 	if (!field_name_length) {
284 		RETURN_FALSE;
285 	}
286 
287 	if (field_boost < 0.0) {
288 		RETURN_FALSE;
289 	}
290 
291 	/* Retrieve the document entry for the SolrDocument instance */
292 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) 	{
293 
294 		solr_field_list_t *field_values = NULL;
295 
296 		/* If the field already exists in the SolrDocument instance append the value to the field list queue */
297 		if ((field_values = zend_hash_str_find_ptr(doc_entry->fields, field_name, field_name_length)) != NULL) {
298 			field_values->field_boost = field_boost;
299 			RETURN_TRUE;
300 		}
301 
302 		RETURN_FALSE;
303 	}
304 
305 	RETURN_FALSE;
306 }
307 /* }}} */
308 
309 /**
310  * {{{ proto bool SolrInputDocument::setVersion(int version)
311  * Enable optimistic concurrency using assertions  */
PHP_METHOD(SolrInputDocument,setVersion)312 PHP_METHOD(SolrInputDocument, setVersion)
313 {
314     long version = 0;
315     solr_document_t *doc_entry = NULL;
316     solr_field_list_t *field = NULL;
317     solr_char_t *field_name = "_version_";
318     COMPAT_ARG_SIZE_T field_name_length = sizeof("_version_");
319     char version_str[80];
320     zend_error_handling error_handling;
321 
322     zend_replace_error_handling(EH_THROW, solr_ce_SolrIllegalArgumentException, &error_handling);
323     if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &version) == FAILURE) {
324         zend_restore_error_handling(&error_handling);
325         return;
326     }
327     zend_restore_error_handling(&error_handling);
328 
329     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == FAILURE) {
330         return;
331     }
332 
333     if (zend_hash_str_find_ptr(doc_entry->fields, field_name, field_name_length)) {
334         zend_hash_str_del(doc_entry->fields, field_name, field_name_length);
335     }
336 
337     field = pemalloc(sizeof(solr_field_list_t), SOLR_DOCUMENT_FIELD_PERSISTENT);
338 
339     field->count = 0L;
340     field->field_boost = 0.0f;
341     field->field_name = pestrdup(field_name, SOLR_DOCUMENT_FIELD_PERSISTENT);
342     field->head = field->last = NULL;
343 
344     snprintf(version_str, 80, "%ld", version);
345 
346     solr_document_insert_field_value(field, version_str, 0.0);
347 
348     if (zend_hash_str_update_ptr(doc_entry->fields, field_name, field_name_length, field) == NULL) {
349         solr_throw_exception_ex(solr_ce_SolrException, SOLR_ERROR_1008, SOLR_FILE_LINE_FUNC, SOLR_ERROR_1008_MSG);
350         solr_destroy_field_list(field);
351         return;
352     }
353 
354     RETURN_TRUE;
355 }
356 /* }}} */
357 
358 /* {{{ proto int SolrInputDocument::getVersion( void ) */
PHP_METHOD(SolrInputDocument,getVersion)359 PHP_METHOD(SolrInputDocument, getVersion)
360 {
361     solr_document_t *doc_entry = NULL;
362     solr_char_t *field_name = "_version_";
363     COMPAT_ARG_SIZE_T field_name_length = sizeof("_version_");
364     solr_field_list_t *field = NULL;
365 
366     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == FAILURE) {
367         RETURN_NULL();
368     }
369     if ((field = zend_hash_str_find_ptr(doc_entry->fields, field_name, field_name_length)) != NULL) {
370         RETURN_LONG(atol(field->head->field_value));
371     }
372     RETURN_NULL();
373 }
374 /* }}} */
375 
376 /* {{{ proto float SolrInputDocument::getFieldBoost(string fieldname)
377    Returns the boost value for the specified field. */
PHP_METHOD(SolrInputDocument,getFieldBoost)378 PHP_METHOD(SolrInputDocument, getFieldBoost)
379 {
380 	solr_char_t *field_name = NULL;
381 	COMPAT_ARG_SIZE_T  field_name_length  = 0;
382 	solr_document_t *doc_entry = NULL;
383 
384 	/* Process the parameters passed to the default constructor */
385 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &field_name, &field_name_length) == FAILURE) {
386 		RETURN_FALSE;
387 	}
388 
389 	if (!field_name_length) {
390 		RETURN_FALSE;
391 	}
392 
393 	/* Retrieve the document entry for the SolrDocument instance */
394 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) 	{
395 
396 		solr_field_list_t *field_values = NULL;
397 
398 		if ((field_values = zend_hash_str_find_ptr(doc_entry->fields, field_name, field_name_length)) != NULL) {
399 			RETURN_DOUBLE(field_values->field_boost);
400 		}
401 		RETURN_FALSE;
402 	}
403 
404 	RETURN_FALSE;
405 }
406 /* }}} */
407 
408 /* {{{ proto array SolrInputDocument::getFieldNames(void)
409    Returns an array of all the field names in the document. */
PHP_METHOD(SolrInputDocument,getFieldNames)410 PHP_METHOD(SolrInputDocument, getFieldNames)
411 {
412 	solr_document_get_field_names(INTERNAL_FUNCTION_PARAM_PASSTHRU);
413 }
414 /* }}} */
415 
416 /* {{{ proto int SolrInputDocument::getFieldCount(void)
417    Returns the number of fields in document. Multivalued fields are only counted once. */
PHP_METHOD(SolrInputDocument,getFieldCount)418 PHP_METHOD(SolrInputDocument, getFieldCount)
419 {
420 	solr_document_t *doc_entry = NULL;
421 
422 	/* Retrieve the document entry for the SolrDocument instance */
423 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS)
424 	{
425 		RETURN_LONG(zend_hash_num_elements(doc_entry->fields));
426 	}
427 
428 	RETURN_FALSE;
429 }
430 /* }}} */
431 
432 /* {{{ proto SolrDocumentField SolrInputDocument::getField(string fieldname)
433    Returns the requested field. */
PHP_METHOD(SolrInputDocument,getField)434 PHP_METHOD(SolrInputDocument, getField)
435 {
436 	solr_char_t *field_name = NULL;
437 	COMPAT_ARG_SIZE_T  field_name_length = 0;
438 	solr_document_t *doc_entry = NULL;
439 	zend_string *field_str = NULL;
440 
441 	/* Process the parameters passed to the default constructor */
442 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &field_name, &field_name_length) == FAILURE) {
443 
444 		RETURN_FALSE;
445 	}
446 
447 	if (!field_name_length) {
448 		RETURN_FALSE;
449 	}
450 
451 	field_str = zend_string_init(field_name, field_name_length, SOLR_DOCUMENT_FIELD_PERSISTENT);
452 
453 	/* Retrieve the document entry for the SolrDocument instance */
454 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS)
455 	{
456 		solr_field_list_t *field_values = NULL;
457 
458 		if ((field_values = zend_hash_find_ptr(doc_entry->fields, field_str)) != NULL)
459 		{
460 			solr_create_document_field_object(field_values, &return_value);
461 			/* The field was retrieved, so we're done here */
462 			zend_string_release(field_str);
463 			return ;
464 		}
465 		goto return_false;
466 	}
467 return_false:
468 	zend_string_release(field_str);
469 	RETURN_FALSE;
470 }
471 /* }}} */
472 
473 /* {{{ proto array SolrInputDocument::toArray(void)
474    Returns an array representation of the object. */
PHP_METHOD(SolrInputDocument,toArray)475 PHP_METHOD(SolrInputDocument, toArray)
476 {
477 	solr_document_t *doc_entry = NULL;
478 	zval fields_array;
479 
480 	/* Retrieve the document entry for the SolrDocument instance */
481 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS)
482 	{
483 		HashTable *fields_ht;
484 		array_init(return_value);
485 		array_init(&fields_array);
486 		zend_hash_init(Z_ARRVAL(fields_array), zend_hash_num_elements(doc_entry->fields), NULL, ZVAL_PTR_DTOR, 0);
487 
488 		add_assoc_double(return_value, "document_boost", doc_entry->document_boost);
489 		add_assoc_long(return_value,   "field_count", doc_entry->field_count);
490 		add_assoc_zval(return_value,   "fields", &fields_array);
491 
492 		fields_ht = doc_entry->fields;
493 
494 		SOLR_HASHTABLE_FOR_LOOP(fields_ht)
495 		{
496 			solr_field_list_t *field = NULL;
497 			zval current_field;
498 			zval *current_field_ptr = &current_field;
499 
500 			field = zend_hash_get_current_data_ptr(fields_ht);
501 			/* create SolrDocumentField */
502 			solr_create_document_field_object(field, &current_field_ptr);
503 			/* create SolrDocumentField to the fields HT */
504 			add_next_index_zval(&fields_array, current_field_ptr);
505 		}
506 		/* We are done */
507 		return;
508 	}
509 
510 	RETURN_FALSE;
511 }
512 /* }}} */
513 
514 /* {{{ proto bool SolrInputDocument::fieldExists(string field_name)
515    Checks if the field name exists in the document. */
PHP_METHOD(SolrInputDocument,fieldExists)516 PHP_METHOD(SolrInputDocument, fieldExists)
517 {
518 	solr_char_t *field_name = NULL;
519 	COMPAT_ARG_SIZE_T  field_name_length = 0;
520 	solr_document_t *doc_entry = NULL;
521 
522 	/* Process the parameters passed to the default constructor */
523 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &field_name, &field_name_length) == FAILURE) {
524 		RETURN_FALSE;
525 	}
526 
527 	if (!field_name_length) {
528 		RETURN_FALSE;
529 	}
530 
531 	/* Retrieve the document entry for the SolrDocument instance */
532 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) {
533 		if (zend_hash_str_exists(doc_entry->fields, field_name, field_name_length)) {
534 			RETURN_TRUE;
535 		} else {
536 			RETURN_FALSE;
537 		}
538 	}
539 
540 	RETURN_FALSE;
541 }
542 /* }}} */
543 
544 /* {{{ proto bool SolrInputDocument::deleteField(string field_name)
545    Removes the request field from the document. */
PHP_METHOD(SolrInputDocument,deleteField)546 PHP_METHOD(SolrInputDocument, deleteField)
547 {
548 	solr_document_t *doc_entry = NULL;
549 	char *field_name;
550 	COMPAT_ARG_SIZE_T field_name_len = 0;
551 
552 	/* Process the parameters passed to the default constructor */
553 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &field_name, &field_name_len) == FAILURE) {
554 		RETURN_FALSE;
555 	}
556 
557 	if (!field_name_len) {
558 		RETURN_FALSE;
559 	}
560 
561 	/* Retrieve the document entry for the SolrDocument instance */
562 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS) {
563 		if (zend_hash_str_del(doc_entry->fields, field_name, field_name_len) == SUCCESS) {
564 			doc_entry->field_count--;
565 			RETURN_TRUE;
566 		}
567 		RETURN_FALSE;
568 	}
569 	RETURN_FALSE;
570 }
571 /* }}} */
572 
573 /* {{{ proto bool SolrInputDocument::sort(int sort_criterion [, int sort_direction])
574    Sorts the document fields by the specified criterion. */
PHP_METHOD(SolrInputDocument,sort)575 PHP_METHOD(SolrInputDocument, sort)
576 {
577 	long int order_by = 0L;
578 	long int sort_direction = SOLR_SORT_ASC;
579 	solr_document_t *doc_entry = NULL;
580 	int renumber = 0;
581 
582 	/* The pointer to the comparison function used by zend_qsort */
583 #if PHP_VERSION_ID < 80000
584 	compare_func_t comparison_function = NULL;
585 #else
586 	bucket_compare_func_t comparison_function = NULL;
587 #endif
588 
589 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &order_by, &sort_direction) == FAILURE) {
590 		RETURN_FALSE;
591 	}
592 
593 	/* Retrieve the document entry for the SolrDocument instance */
594 	if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == FAILURE) {
595 		RETURN_FALSE;
596 	}
597 
598 	/*  {{{ Select the appropriate comparison function */
599 	switch(order_by)
600 	{
601 		case  SOLR_SORT_FIELD_NAME : /* Sorting by field name */
602 		{
603 			switch(sort_direction)
604 			{
605 				case SOLR_SORT_ASC :
606 					comparison_function = solr_compare_field_name;
607 				break;
608 
609 				case SOLR_SORT_DESC :
610 					comparison_function = solr_rcompare_field_name;
611 				break;
612 			}
613 		}
614 		break; /* case  SOLR_SORT_FIELD_NAME */
615 
616 		case  SOLR_SORT_FIELD_VALUE_COUNT : /* Sorting by number of values per field */
617 		{
618 			switch(sort_direction)
619 			{
620 				case SOLR_SORT_ASC :
621 					comparison_function = solr_compare_field_value_count;
622 				break;
623 
624 				case SOLR_SORT_DESC :
625 					comparison_function = solr_rcompare_field_value_count;
626 				break;
627 			}
628 		}
629 		break; /* case  SOLR_SORT_FIELD_VALUE_COUNT */
630 
631 		case  SOLR_SORT_FIELD_BOOST_VALUE : /* Sorting by field boost values */
632 		{
633 			switch(sort_direction)
634 			{
635 				case SOLR_SORT_ASC :
636 					comparison_function = solr_compare_field_boost_value;
637 				break;
638 
639 				case SOLR_SORT_DESC :
640 					comparison_function = solr_rcompare_field_boost_value;
641 				break;
642 			}
643 		}
644 		break; /* case  SOLR_SORT_FIELD_BOOST_VALUE */
645 
646 		default : /* Undefined sort criteria */
647 
648 			RETURN_FALSE;
649 
650 		break;
651 
652 	} /* }}} switch(order_by) */
653 
654 	/* Undefined sort direction. It was not ASC or DESC */
655 	if (!comparison_function) {
656 
657 		RETURN_FALSE;
658 	}
659 
660 	zend_hash_sort(doc_entry->fields, comparison_function, renumber);
661 
662 	RETURN_TRUE;
663 }
664 /* }}} */
665 
666 /* {{{ proto bool SolrInputDocument::merge(SolrInputDocument source [, bool override])
667    Merges the source document to the current object. */
PHP_METHOD(SolrInputDocument,merge)668 PHP_METHOD(SolrInputDocument, merge)
669 {
670 	solr_document_t *destination_document  = NULL;
671 	solr_document_t *source_document       = NULL;
672 
673 	/* The destination SolrDocument instance */
674 	zval * destination_document_zval = getThis();
675 
676 	/* The source SolrDocument instance */
677 	zval *source_document_zval = NULL;
678 
679 	/* Should we skip fields that already exist in destination */
680 	zend_bool overwrite = (zend_bool) 0;
681 
682 	copy_ctor_func_t p_copy_ctor = (copy_ctor_func_t) field_copy_constructor;
683 
684 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &source_document_zval, solr_ce_SolrInputDocument, &overwrite) == FAILURE) {
685 
686 		RETURN_FALSE;
687 	}
688 
689 	if (solr_fetch_document_entry(OBJ_FOR_PROP(source_document_zval), &source_document) == FAILURE) {
690 
691 		RETURN_FALSE;
692 	}
693 
694 	if (solr_fetch_document_entry(OBJ_FOR_PROP(destination_document_zval), &destination_document) == FAILURE) {
695 
696 		RETURN_FALSE;
697 	}
698 
699 	if (zend_hash_num_elements(source_document->fields) == 0) {
700 
701 		php_error_docref(NULL, E_WARNING, "Source SolrInputDocument has no fields. Source documentId");
702 
703 		RETURN_FALSE;
704 	}
705 
706 	/* Copy the fields in the source HashTable to the destination HashTable */
707 	zend_hash_merge(destination_document->fields, source_document->fields, p_copy_ctor, overwrite);
708 
709 	/* Update the field count */
710 	destination_document->field_count = (uint32_t) zend_hash_num_elements(destination_document->fields);
711 
712 	RETURN_TRUE;
713 }
714 /* }}} */
715 
716 /* {{{ proto void SolrInputDocument::addChildDocument(SolrInputDocument child)
717    Adds a child document */
PHP_METHOD(SolrInputDocument,addChildDocument)718 PHP_METHOD(SolrInputDocument, addChildDocument)
719 {
720     zval *child_obj = NULL;
721     solr_document_t *solr_doc = NULL, *child_doc_entry = NULL;
722 
723     if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &child_obj, solr_ce_SolrInputDocument) == FAILURE)
724     {
725         RETURN_FALSE;
726     }
727 
728     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &solr_doc) == FAILURE)
729     {
730         solr_throw_exception_ex(solr_ce_SolrException, SOLR_ERROR_1008, SOLR_FILE_LINE_FUNC, "Internal Error: Unable to fetch document_entry.");
731         return;
732     }
733 
734     if (solr_fetch_document_entry(OBJ_FOR_PROP(child_obj), &child_doc_entry) == FAILURE)
735     {
736         solr_throw_exception_ex(solr_ce_SolrException, SOLR_ERROR_1008, SOLR_FILE_LINE_FUNC, "Internal Error: Unable to fetch document_entry for child document.");
737         return;
738     }
739 
740     /* SolrInputDocument must contain at least one field */
741     if (0 == zend_hash_num_elements(child_doc_entry->fields)) {
742         solr_throw_exception_ex(solr_ce_SolrIllegalArgumentException, SOLR_ERROR_4000, SOLR_FILE_LINE_FUNC, "Child document has no fields");
743         return;
744     }
745 
746     if (zend_hash_next_index_insert(solr_doc->children, child_obj) == NULL) {
747         solr_throw_exception_ex(solr_ce_SolrException, SOLR_ERROR_4000, SOLR_FILE_LINE_FUNC, "Internal Error: Unable to add child to the hashtable.");
748     } else {
749         Z_ADDREF_P(child_obj);
750     }
751 }
752 /* }}} */
753 
754 /* {{{ proto void SolrInputDocument::addChildDocuments(array)
755    Adds a child documents */
PHP_METHOD(SolrInputDocument,addChildDocuments)756 PHP_METHOD(SolrInputDocument, addChildDocuments)
757 {
758     HashTable *solr_input_docs;
759     solr_document_t *solr_doc = NULL;
760     zval *docs_array = NULL;
761     int num_input_docs = 0, curr_pos = 0;
762     size_t pos = 0U;
763     zval **input_docs = NULL, *current_input_doc = NULL;
764 
765     if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &docs_array) == FAILURE) {
766         return;
767     }
768 
769     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &solr_doc) == FAILURE)
770     {
771         solr_throw_exception_ex(solr_ce_SolrException, SOLR_ERROR_1008, SOLR_FILE_LINE_FUNC, "Internal Error: Unable to fetch document_entry.");
772     }
773 
774     solr_input_docs = Z_ARRVAL_P(docs_array);
775     num_input_docs = zend_hash_num_elements(solr_input_docs);
776 
777     if(!num_input_docs)
778     {
779         solr_throw_exception_ex(solr_ce_SolrIllegalArgumentException, SOLR_ERROR_4000, SOLR_FILE_LINE_FUNC, "The array parameter passed is empty");
780         return;
781     }
782 
783     /* This should be released if there is an error */
784     input_docs = (zval **) pemalloc((sizeof(zval *) * (num_input_docs + 1)), SOLR_DOCUMENT_PERSISTENT);
785 
786     memset(input_docs, 0, sizeof(zval *) * (num_input_docs + 1));
787 
788     /* Please check all the SolrInputDocument instances passed via the array */
789     SOLR_HASHTABLE_FOR_LOOP(solr_input_docs)
790     {
791         zval *solr_input_doc = NULL;
792         solr_document_t *doc_entry = NULL;
793         HashTable *document_fields;
794 
795         solr_input_doc = zend_hash_get_current_data(solr_input_docs);
796 
797         if (Z_TYPE_P(solr_input_doc) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(solr_input_doc), solr_ce_SolrInputDocument))
798         {
799             SOLR_FREE_DOC_ENTRIES(input_docs);
800 
801             solr_throw_exception_ex(solr_ce_SolrIllegalArgumentException, SOLR_ERROR_4000, SOLR_FILE_LINE_FUNC, "SolrInputDocument number %u is not a valid SolrInputDocument instance", (curr_pos + 1U));
802 
803             return;
804         }
805 
806         if (solr_fetch_document_entry(OBJ_FOR_PROP(solr_input_doc), &doc_entry) == FAILURE) {
807 
808             SOLR_FREE_DOC_ENTRIES(input_docs);
809 
810             solr_throw_exception_ex(solr_ce_SolrIllegalArgumentException, SOLR_ERROR_4000, SOLR_FILE_LINE_FUNC, "SolrInputDocument number %u is not valid. Object not present in HashTable", (curr_pos + 1U));
811 
812             return;
813         }
814 
815         document_fields = doc_entry->fields;
816 
817         /* SolrInputDocument must contain at least one field */
818         if (0 == zend_hash_num_elements(document_fields)) {
819 
820             SOLR_FREE_DOC_ENTRIES(input_docs);
821 
822             solr_throw_exception_ex(solr_ce_SolrIllegalArgumentException, SOLR_ERROR_4000, SOLR_FILE_LINE_FUNC, "SolrInputDocument number %u has no fields", (curr_pos + 1U));
823 
824             return;
825         }
826         input_docs[curr_pos] = solr_input_doc;
827 
828         curr_pos++;
829     }
830 
831     /* Grab the first (solr_document_t *) pointer */
832     current_input_doc = input_docs[pos];
833 
834     while(current_input_doc != NULL)
835     {
836         if (zend_hash_next_index_insert(solr_doc->children, current_input_doc) == NULL)
837         {
838             solr_throw_exception_ex(solr_ce_SolrIllegalArgumentException, SOLR_ERROR_4000, SOLR_FILE_LINE_FUNC, "SolrInputDocument number %u has no fields", (pos + 1U));
839             SOLR_FREE_DOC_ENTRIES(input_docs);
840             return;
841         }
842         /* todo possible leak */
843         Z_ADDREF_P(current_input_doc);
844         pos++;
845 
846         current_input_doc = input_docs[pos];
847     }
848 
849     SOLR_FREE_DOC_ENTRIES(input_docs);
850 }
851 /* }}} */
852 
853 /* {{{ proto array SolrInputDocument::getChildDocuments( void )
854      Returns child documents or null if none */
PHP_METHOD(SolrInputDocument,getChildDocuments)855 PHP_METHOD(SolrInputDocument, getChildDocuments)
856 {
857     solr_document_t *solr_doc = NULL;
858 
859     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &solr_doc) == FAILURE)
860     {
861         php_error_docref(NULL, E_ERROR, "Unable to fetch document entry for current object");
862     }
863 
864     if (zend_hash_num_elements(solr_doc->children) > 0)
865     {
866         array_init(return_value);
867         zend_hash_init(Z_ARRVAL_P(return_value), zend_hash_num_elements(solr_doc->children), NULL, ZVAL_PTR_DTOR, 0);
868         zend_hash_copy(Z_ARRVAL_P(return_value), solr_doc->children, (copy_ctor_func_t)zval_add_ref);
869     }
870 }
871 /* }}} */
872 
873 /* {{{ proto bool SolrInputDocument::hasChildDocuments (void)
874     Checks whether this document has got child documents */
PHP_METHOD(SolrInputDocument,hasChildDocuments)875 PHP_METHOD(SolrInputDocument, hasChildDocuments)
876 {
877     solr_document_t *solr_doc = NULL;
878 
879     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &solr_doc))
880     {
881         php_error_docref(NULL, E_ERROR, "Unable to fetch document entry for current object");
882     }
883 
884     if (zend_hash_num_elements(solr_doc->children) > 0)
885     {
886         RETURN_TRUE;
887     } else {
888         RETURN_FALSE;
889     }
890 }
891 /* }}} */
892 
893 /* {{{ proto int SolrInputDocument::getChildDocumentsCount (void)
894     Returns the number of child documents */
PHP_METHOD(SolrInputDocument,getChildDocumentsCount)895 PHP_METHOD(SolrInputDocument, getChildDocumentsCount)
896 {
897     solr_document_t *solr_doc = NULL;
898 
899     if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &solr_doc))
900     {
901         php_error_docref(NULL, E_ERROR, "Unable to fetch document entry for current object");
902     }
903 
904     ZVAL_LONG(return_value, zend_hash_num_elements(solr_doc->children));
905 }
906 /* }}} */
907 
908 /*
909  * Local variables:
910  * tab-width: 4
911  * c-basic-offset: 4
912  * indent-tabs-mode: t
913  * End:
914  * vim600: fdm=marker
915  * vim: noet sw=4 ts=4
916  */
917