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 | Author: Israel Ekpo <iekpo@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php_solr.h"
18
19 #if PHP_VERSION_ID < 80000
solr_document_object_handler_clone(zval * zobject)20 PHP_SOLR_API zend_object *solr_document_object_handler_clone(zval *zobject)
21 {
22 zend_object *old_object = Z_OBJ_P(zobject);
23 #else
24 PHP_SOLR_API zend_object *solr_document_object_handler_clone(zend_object *old_object)
25 {
26 #endif
27 zend_object *new_object;
28 solr_document_t *doc_entry, *old_doc_entry;
29 long document_index = SOLR_UNIQUE_DOCUMENT_INDEX();
30
31 new_object = zend_objects_new(old_object->ce);
32 object_properties_init(new_object, old_object->ce);
33 zend_objects_clone_members(new_object, old_object);
34
35 #if PHP_VERSION_ID < 80000
36 if (solr_fetch_document_entry(zobject, &old_doc_entry) == FAILURE) {
37 #else
38 if (solr_fetch_document_entry(old_object, &old_doc_entry) == FAILURE) {
39 #endif
40 php_error_docref(NULL, E_ERROR, "Clone Failed: Unable to fetch document entry of the source document");
41 }
42
43 doc_entry = solr_init_document(document_index);
44 ZVAL_LONG(&(new_object->properties_table[0]), document_index);
45 /* Add the document entry to the directory of documents */
46 doc_entry->field_count = old_doc_entry->field_count;
47 doc_entry->document_boost = old_doc_entry->document_boost;
48
49 zend_hash_copy(doc_entry->fields, old_doc_entry->fields, (copy_ctor_func_t) field_copy_constructor);
50 zend_hash_copy(doc_entry->children, old_doc_entry->children, (copy_ctor_func_t) zval_add_ref);
51
52 return new_object;
53 }
54
55 PHP_SOLR_API void field_copy_constructor_ex(solr_field_list_t **original_field_queue_ptr)
56 {
57 solr_field_list_t *original_field_queue = *original_field_queue_ptr;
58 solr_field_value_t *ptr = original_field_queue->head;
59 if (ptr == NULL)
60 {
61 return;
62 }
63
64 solr_field_list_t *new_field_queue = (solr_field_list_t *) pemalloc(sizeof(solr_field_list_t), SOLR_DOCUMENT_FIELD_PERSISTENT);
65
66 new_field_queue->count = 0L;
67 new_field_queue->field_name = (solr_char_t *) pestrdup((char *) (original_field_queue)->field_name, SOLR_DOCUMENT_FIELD_PERSISTENT);
68 new_field_queue->head = NULL;
69 new_field_queue->last = NULL;
70 new_field_queue->field_boost = original_field_queue->field_boost;
71
72 while(ptr != NULL)
73 {
74 if (solr_document_insert_field_value(new_field_queue, ptr->field_value, 0.0) == FAILURE) {
75 php_error_docref(NULL, E_ERROR, "Unable to insert field value");
76 }
77
78 ptr = ptr->next;
79 }
80
81 *original_field_queue_ptr = new_field_queue;
82 }
83 /* {{{ void field_copy_constructor_zv(zval *field_queue_zv) */
84 PHP_SOLR_API void field_copy_constructor_zv(zval *field_queue_zv)
85 {
86 solr_field_list_t *original_field_queue = NULL;
87 original_field_queue = Z_PTR_P(field_queue_zv);
88 field_copy_constructor_ex(&original_field_queue);
89 ZVAL_PTR(field_queue_zv, original_field_queue);
90 }
91 /* }}} */
92
93
94 /* {{{ PHP_SOLR_API int solr_document_insert_field_value(solr_field_list_t *queue, const solr_char_t *field_value, double field_boost, int modifier) */
95 PHP_SOLR_API int solr_document_insert_field_value_ex(solr_field_list_t *queue, const solr_char_t *field_value, double field_boost, int modifier)
96 {
97 solr_field_value_t *new_entry = (solr_field_value_t *) pemalloc(sizeof(solr_field_value_t), SOLR_DOCUMENT_FIELD_PERSISTENT);
98
99 if (new_entry == NULL) {
100
101 return FAILURE;
102 }
103
104 new_entry->field_value = (solr_char_t *) pestrdup((char *) field_value, SOLR_DOCUMENT_FIELD_PERSISTENT);
105
106 if (new_entry->field_value == NULL) {
107
108 return FAILURE;
109 }
110
111 new_entry->next = NULL;
112 new_entry->modifier = modifier;
113
114 if (queue->head == NULL) {
115
116 /* This is the first and only item in the field list */
117 queue->head = new_entry;
118 queue->last = new_entry;
119
120 /* Update the field boost value */
121 if (field_boost > 0.0) {
122
123 queue->field_boost = field_boost;
124 }
125
126 } else { /* There are already entries in the list. */
127
128 /* Append to the end of the queue */
129 queue->last->next = new_entry;
130
131 /* Set the last item in the queue to the latest entry */
132 queue->last = new_entry;
133
134 /* Update the field boost value */
135 if (field_boost > 0.0) {
136
137 if (queue->field_boost > 0.0) {
138
139 queue->field_boost *= field_boost;
140
141 } else {
142
143 queue->field_boost = field_boost;
144 }
145 }
146 }
147
148 queue->count++;
149
150 return SUCCESS;
151 }
152 /* }}} */
153
154 /* {{{ PHP_SOLR_API solr_document_t *solr_init_document(long int document_index)
155 * create and allocate a solr_document_t with the specified index
156 */
157 PHP_SOLR_API solr_document_t *solr_init_document(long int document_index)
158 {
159 uint32_t nSize = SOLR_INITIAL_HASH_TABLE_SIZE;
160 solr_document_t *doc_ptr = NULL;
161 solr_document_t *doc_entry;
162
163 #ifdef PHP_7
164 doc_entry = pemalloc(sizeof(solr_document_t), SOLR_DOCUMENT_PERSISTENT);
165 #endif
166
167 doc_entry->document_index = document_index;
168 doc_entry->field_count = 0L;
169 doc_entry->document_boost = 0.0f;
170
171 /* Allocated memory for the fields HashTable using fast cache for HashTables */
172 ALLOC_HASHTABLE(doc_entry->fields);
173 ALLOC_HASHTABLE(doc_entry->children);
174
175 /* Initializing the hash table used for storing fields in this SolrDocument */
176 zend_hash_init(doc_entry->fields, nSize, NULL, (dtor_func_t) solr_destroy_field_list_ht_dtor, SOLR_DOCUMENT_FIELD_PERSISTENT);
177 zend_hash_init(doc_entry->children, nSize, NULL, ZVAL_PTR_DTOR, SOLR_DOCUMENT_FIELD_PERSISTENT);
178
179 /* Let's check one more time before insert into the HashTable */
180 if (zend_hash_index_exists(SOLR_GLOBAL(documents), document_index)) {
181 /* todo call dtor ? */
182 pefree(doc_entry->fields, SOLR_DOCUMENT_FIELD_PERSISTENT);
183
184 pefree(doc_entry->children, SOLR_DOCUMENT_FIELD_PERSISTENT);
185 return NULL;
186 }
187
188 /* Add the document entry to the directory of documents */
189 doc_ptr = zend_hash_index_update_ptr(SOLR_GLOBAL(documents), document_index, (void *) doc_entry);
190
191 /* Keep track of how many SolrDocument instances we currently have */
192 SOLR_GLOBAL(document_count)++;
193 return doc_ptr;
194 }
195 /* }}} */
196
197 /* {{{ PHP_SOLR_API solr_document_t *solr_input_doc_ctor(zval *objptr)
198 * constructor populate/allocate the new document after object instantiation
199 * and allocates hashtables for fields and children
200 */
201 PHP_SOLR_API solr_document_t *solr_input_doc_ctor(zval *objptr)
202 {
203 zend_ulong document_index = SOLR_UNIQUE_DOCUMENT_INDEX();
204 solr_document_t *solr_doc = NULL;
205
206 if ((solr_doc = solr_init_document(document_index)) == NULL)
207 {
208 return NULL;
209 }
210
211 /* Set the value of the internal id property */
212 zend_update_property_long(solr_ce_SolrInputDocument, OBJ_FOR_PROP(objptr), SOLR_INDEX_PROPERTY_NAME, sizeof(SOLR_INDEX_PROPERTY_NAME) - 1, document_index);
213
214 /* Overriding the default object handlers */
215 Z_OBJ_HT_P(objptr) = &solr_input_document_object_handlers;
216 return solr_doc;
217 }
218 /* }}} */
219
220 /* {{{ PHP_SOLR_API void solr_destroy_document_zv(zval *document)
221 * destory a solr_document within a zval (ZE 3)
222 */
223 PHP_SOLR_API void solr_destroy_document_zv(zval *document)
224 {
225 solr_document_t *doc_entry = (solr_document_t *) Z_PTR_P(document);
226
227 solr_destroy_document_ex(doc_entry);
228
229 pefree(doc_entry, SOLR_DOCUMENT_PERSISTENT);
230 }
231 /* }}} */
232
233 /* {{{ void solr_destroy_document(void *document)
234 * destory a solr_document_t
235 */
236 PHP_SOLR_API void solr_destroy_document_ex(solr_document_t *doc_entry)
237 {
238 /* Release all the field_lists one at a time with solr_destroy_field_list */
239 zend_hash_destroy(doc_entry->fields);
240
241 /* Deallocate memory for the fields HashTable */
242 pefree(doc_entry->fields, SOLR_DOCUMENT_FIELD_PERSISTENT);
243 if (doc_entry->children) {
244 zend_hash_destroy(doc_entry->children);
245 pefree(doc_entry->children, SOLR_DOCUMENT_FIELD_PERSISTENT);
246 }
247 }
248 /* }}} */
249
250 PHP_SOLR_API void solr_destroy_field_list_ht_dtor(zval *zv_field_entry)
251 {
252 solr_field_list_t *field_entry = Z_PTR_P(zv_field_entry);
253 solr_destroy_field_list(field_entry);
254 }
255
256 /* {{{ void solr_destroy_field_list(solr_field_list_t **field_entry_ptr) */
257 PHP_SOLR_API void solr_destroy_field_list(solr_field_list_t *field_entry)
258 {
259 solr_field_value_t *tmp = NULL;
260 solr_field_value_t *current_field_value = field_entry->head;
261
262 /* Go through the list and free all the values */
263 while(current_field_value != NULL) {
264
265 tmp = current_field_value->next;
266
267 pefree(current_field_value->field_value, SOLR_DOCUMENT_FIELD_PERSISTENT);
268
269 pefree(current_field_value, SOLR_DOCUMENT_FIELD_PERSISTENT);
270
271 current_field_value = tmp;
272 }
273
274 field_entry->head = NULL;
275
276 field_entry->last = NULL;
277
278 pefree(field_entry->field_name, SOLR_DOCUMENT_FIELD_PERSISTENT);
279
280 pefree(field_entry, SOLR_DOCUMENT_FIELD_PERSISTENT);
281 }
282 /* }}} */
283
284 /* Comparison functions for field entries */
285
286 /* {{{ int solr_compare_field_name(const void *a, const void *b) */
287 #if PHP_VERSION_ID < 80000
288 PHP_SOLR_API int solr_compare_field_name(const void *a, const void *b)
289 {
290 const Bucket *x = (Bucket *) a;
291 const Bucket *y = (Bucket *) b;
292 #else
293 PHP_SOLR_API int solr_compare_field_name(Bucket *x, Bucket *y)
294 {
295 #endif
296 const solr_field_list_t *first = (solr_field_list_t *) Z_PTR(x->val);
297 const solr_field_list_t *second = (solr_field_list_t *) Z_PTR(y->val);
298
299 const int diff = strcmp((char *) first->field_name, (char *) second->field_name);
300
301 const int result = ((diff > 0) ? 1 : ((diff < 0) ? -1 : 0));
302
303 return result;
304 }
305 /* }}} */
306
307 /* {{{ int solr_rcompare_field_name(const void *a, const void *b) */
308 #if PHP_VERSION_ID < 80000
309 PHP_SOLR_API int solr_rcompare_field_name(const void *a, const void *b)
310 #else
311 PHP_SOLR_API int solr_rcompare_field_name(Bucket *a, Bucket *b)
312 #endif
313 {
314 return (solr_compare_field_name(a, b) * -1);
315 }
316 /* }}} */
317
318 /* {{{ int solr_compare_field_value_count(const void *a, const void *b) */
319 #if PHP_VERSION_ID < 80000
320 PHP_SOLR_API int solr_compare_field_value_count(const void *a, const void *b)
321 {
322 const Bucket *x = (Bucket *) a;
323 const Bucket *y = (Bucket *) b;
324 #else
325 PHP_SOLR_API int solr_compare_field_value_count(Bucket *x, Bucket *y)
326 {
327 #endif
328 const solr_field_list_t *first = (solr_field_list_t *) Z_PTR(x->val);
329 const solr_field_list_t *second = (solr_field_list_t *) Z_PTR(y->val);
330
331 const int diff = first->count - second->count;
332
333 const int result = ((diff > 0) ? 1 : ((diff < 0) ? -1 : 0));
334
335 return result;
336 }
337 /* }}} */
338
339 /* {{{ int solr_rcompare_field_value_count(const void *a, const void *b) */
340 #if PHP_VERSION_ID < 80000
341 PHP_SOLR_API int solr_rcompare_field_value_count(const void *a, const void *b)
342 #else
343 PHP_SOLR_API int solr_rcompare_field_value_count(Bucket *a, Bucket *b)
344 #endif
345 {
346 return (solr_compare_field_value_count(a, b) * -1);
347 }
348 /* }}} */
349
350 /* {{{ int solr_compare_field_boost_value(const void *a, const void *b) */
351 #if PHP_VERSION_ID < 80000
352 PHP_SOLR_API int solr_compare_field_boost_value(const void *a, const void *b)
353 {
354 const Bucket *x = (Bucket *) a;
355 const Bucket *y = (Bucket *) b;
356 #else
357 PHP_SOLR_API int solr_compare_field_boost_value(Bucket *x, Bucket *y)
358 {
359 #endif
360
361 const solr_field_list_t *first = (solr_field_list_t *) Z_PTR(x->val);
362 const solr_field_list_t *second = (solr_field_list_t *) Z_PTR(y->val);
363
364 const double diff = first->field_boost - second->field_boost;
365
366 const int result = ((diff > 0.0) ? 1 : ((diff < 0.0) ? -1 : 0));
367
368 return result;
369 }
370 /* }}} */
371
372 /* {{{ int solr_rcompare_field_boost_value(const void *a, const void *b) */
373 #if PHP_VERSION_ID < 80000
374 PHP_SOLR_API int solr_rcompare_field_boost_value(const void *a, const void *b)
375 #else
376 PHP_SOLR_API int solr_rcompare_field_boost_value(Bucket *a, Bucket *b)
377 #endif
378 {
379 return (solr_compare_field_boost_value(a, b) * -1);
380 }
381 /* }}} */
382
383 /* {{{ PHP_SOLR_API void solr_create_document_field_object(solr_field_list_t *field_values, zval **field_obj) */
384 PHP_SOLR_API void solr_create_document_field_object(solr_field_list_t *field_values, zval **field_obj)
385 {
386 zval *doc_field = *field_obj;
387
388 solr_field_value_t *curr_ptr = NULL;
389 zval field_values_array_tmp;
390 zval *field_values_array = &field_values_array_tmp;
391
392 array_init(field_values_array);
393
394 curr_ptr = field_values->head;
395
396 while(curr_ptr != NULL) {
397
398 solr_char_t *current_value = curr_ptr->field_value;
399
400 add_next_index_string(field_values_array, current_value);
401
402 curr_ptr = curr_ptr->next;
403 }
404
405 object_init_ex(doc_field, solr_ce_SolrDocumentField);
406
407 zend_update_property_string(solr_ce_SolrDocumentField, OBJ_FOR_PROP(doc_field), SOLR_FIELD_NAME_PROPERTY_NAME, sizeof(SOLR_FIELD_NAME_PROPERTY_NAME)-1, field_values->field_name);
408 zend_update_property_double(solr_ce_SolrDocumentField, OBJ_FOR_PROP(doc_field), SOLR_FIELD_BOOST_PROPERTY_NAME, sizeof(SOLR_FIELD_BOOST_PROPERTY_NAME)-1, field_values->field_boost);
409 zend_update_property(solr_ce_SolrDocumentField, OBJ_FOR_PROP(doc_field), SOLR_FIELD_VALUES_PROPERTY_NAME, sizeof(SOLR_FIELD_VALUES_PROPERTY_NAME)-1, field_values_array);
410
411 zval_ptr_dtor(field_values_array);
412
413 Z_OBJ_HT_P(doc_field) = &solr_document_field_handlers;
414 }
415 /* }}} */
416
417 /* {{{ PHP_SOLR_API void solr_create_document_field_object(solr_field_list_t *field_values, zval **field_obj)
418 constructs the <doc> element when adding a document to the index */
419 PHP_SOLR_API void solr_add_doc_node(xmlNode *root_node, solr_document_t *doc_entry)
420 {
421 HashTable *document_fields = NULL;
422 xmlNode *solr_doc_node = NULL;
423 document_fields = doc_entry->fields;
424
425 solr_doc_node = xmlNewChild(root_node, NULL, (xmlChar *) "doc", NULL);
426
427 if (doc_entry->document_boost > 0.0f)
428 {
429 auto char tmp_buffer[256]; /* Scratch pad for converting numeric values to strings */
430 memset(tmp_buffer, 0, sizeof(tmp_buffer));
431 php_gcvt(doc_entry->document_boost, EG(precision), '.', 'e' , tmp_buffer);
432 xmlNewProp(solr_doc_node, (xmlChar *) "boost", (xmlChar *) tmp_buffer);
433 }
434
435 solr_generate_document_xml_from_fields(solr_doc_node, document_fields);
436 if (zend_hash_num_elements(doc_entry->children) > 0) {
437
438 SOLR_HASHTABLE_FOR_LOOP(doc_entry->children)
439 {
440 zval *doc_obj = NULL;
441 solr_document_t *child_doc_entry = NULL;
442 doc_obj = zend_hash_get_current_data(doc_entry->children);
443 if (solr_fetch_document_entry(OBJ_FOR_PROP(doc_obj), &child_doc_entry) == SUCCESS)
444 {
445 solr_add_doc_node(solr_doc_node, child_doc_entry);
446 }
447 }
448 }
449 }
450 /* }}} */
451
452
453 /* {{{ static void solr_generate_document_xml_from_fields(xmlNode *solr_doc_node, HashTable *document_fields) */
454 PHP_SOLR_API void solr_generate_document_xml_from_fields(xmlNode *solr_doc_node, HashTable *document_fields)
455 {
456 xmlDoc *doc_ptr = solr_doc_node->doc;
457
458 solr_char_t *doc_field_name;
459 zend_string *field_str = NULL;
460 solr_field_value_t *doc_field_value;
461 solr_field_list_t *field = NULL;
462 zend_ulong num_idx = 0L;
463 ZEND_HASH_FOREACH_KEY_PTR(document_fields, num_idx, field_str, field)
464 {
465 (void)num_idx;
466 zend_bool is_first_value = 1; /* Turn on first value flag */
467 xmlChar *modifier_string = NULL;
468
469 doc_field_name = field_str->val;
470 doc_field_value = field->head;
471
472 /* Loop through all the values for this field */
473 while(doc_field_value != NULL)
474 {
475 xmlChar *escaped_field_value = xmlEncodeEntitiesReentrant(doc_ptr, (xmlChar *) doc_field_value->field_value);
476
477 xmlNode *solr_field_node = xmlNewChild(solr_doc_node, NULL, (xmlChar *) "field", escaped_field_value);
478
479 xmlNewProp(solr_field_node, (xmlChar *) "name", (xmlChar *) doc_field_name);
480
481 if (field->modified) {
482 switch (doc_field_value->modifier) {
483 case SOLR_FIELD_VALUE_MOD_ADD:
484 modifier_string = (xmlChar *)"add";
485 break;
486 case SOLR_FIELD_VALUE_MOD_REMOVE:
487 modifier_string = (xmlChar *)"remove";
488 break;
489 case SOLR_FIELD_VALUE_MOD_REMOVEREGEX:
490 modifier_string = (xmlChar *)"removeregex";
491 break;
492 case SOLR_FIELD_VALUE_MOD_SET:
493 modifier_string = (xmlChar *)"set";
494 break;
495
496 case SOLR_FIELD_VALUE_MOD_INC:
497 modifier_string = (xmlChar *)"inc";
498 break;
499 case SOLR_FIELD_VALUE_MOD_NONE:default:
500 break;
501 }
502 if (modifier_string) {
503 xmlNewProp(solr_field_node, (xmlChar *) "update", modifier_string);
504 }
505 }
506
507 /* Set the boost attribute if this is the first value */
508 if (is_first_value && field->field_boost > 0.0f)
509 {
510 auto char tmp_boost_value_buffer[256];
511
512 memset(tmp_boost_value_buffer, 0, sizeof(tmp_boost_value_buffer));
513
514 php_gcvt(field->field_boost, EG(precision), '.', 'e' , tmp_boost_value_buffer);
515
516 xmlNewProp(solr_field_node, (xmlChar *) "boost", (xmlChar *) tmp_boost_value_buffer);
517
518 is_first_value = 0; /* Turn off the flag */
519 }
520
521 /* Release the memory allocated by xmlEncodeEntitiesReentrant */
522 xmlFree(escaped_field_value);
523
524 /* Grab the next value for this field if any */
525 doc_field_value = doc_field_value->next;
526
527 } /* while(doc_field_value != NULL) */
528 } ZEND_HASH_FOREACH_END(); /* end fields loop */
529 }
530 /* }}} */
531
532
533 PHP_SOLR_API void solr_document_get_field_names(INTERNAL_FUNCTION_PARAMETERS)
534 {
535 solr_document_t *doc_entry = NULL;
536
537 /* Retrieve the document entry for the SolrDocument instance */
538 if (solr_fetch_document_entry(OBJ_FOR_PROP(getThis()), &doc_entry) == SUCCESS)
539 {
540 HashTable *fields_ht = doc_entry->fields;
541
542 array_init(return_value);
543
544 SOLR_HASHTABLE_FOR_LOOP(fields_ht)
545 {
546 solr_field_list_t *field = NULL;
547 field = zend_hash_get_current_data_ptr(fields_ht);
548 add_next_index_string(return_value, (char *) field->field_name);
549 }
550 return;
551 }
552
553 RETURN_FALSE;
554 }
555
556 /*
557 * Local variables:
558 * tab-width: 4
559 * c-basic-offset: 4
560 * End:
561 * vim600: fdm=marker
562 * vim: noet sw=4 ts=4
563 */
564