Line data Source code
1 : /*
2 : +----------------------------------------------------------------------+
3 : | COLOPL PHP Backwards Compatibility Extension. |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) COLOPL, Inc. |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to the BSD-3-Clause license that is |
8 : | bundled with this package in the file LICENSE. |
9 : +----------------------------------------------------------------------+
10 : | Author: Go Kudo <g-kudo@colopl.co.jp> |
11 : +----------------------------------------------------------------------+
12 : */
13 :
14 : #ifdef HAVE_CONFIG_H
15 : # include "config.h"
16 : #endif
17 :
18 : #include "php_colopl_bc.h"
19 :
20 : #include "ext/standard/php_array.h"
21 : #include "ext/standard/php_string.h"
22 : #include "zend_exceptions.h"
23 : #include "zend_operators.h"
24 :
25 : /* zend_operators.c */
26 :
27 : #define COLOPL_BC_TYPE_PAIR(t1,t2) (((t1) << 4) | (t2))
28 :
29 : typedef struct _php_colopl_bc_snapshot_context {
30 : HashTable arrays;
31 : HashTable objects;
32 : } php_colopl_bc_snapshot_context;
33 :
34 : typedef struct _php_colopl_bc_diagnostic_error_state {
35 : int error_reporting;
36 : int user_error_handler_error_reporting;
37 : bool record_errors;
38 : } php_colopl_bc_diagnostic_error_state;
39 :
40 : typedef struct _php_colopl_bc_user_sort_zero_pair {
41 : uint32_t first;
42 : uint32_t second;
43 : } php_colopl_bc_user_sort_zero_pair;
44 :
45 : typedef struct _php_colopl_bc_user_sort_context {
46 : php_colopl_bc_user_sort_zero_pair *zero_pairs;
47 : uint32_t zero_pair_count;
48 : uint32_t zero_pair_capacity;
49 : } php_colopl_bc_user_sort_context;
50 :
51 12096 : static inline void php_colopl_bc_snapshot_context_init(php_colopl_bc_snapshot_context *ctx)
52 : {
53 12096 : zend_hash_init(&ctx->arrays, 8, NULL, NULL, 0);
54 12096 : zend_hash_init(&ctx->objects, 8, NULL, NULL, 0);
55 12096 : }
56 :
57 12096 : static inline void php_colopl_bc_snapshot_context_destroy(php_colopl_bc_snapshot_context *ctx)
58 : {
59 12096 : zend_hash_destroy(&ctx->objects);
60 12096 : zend_hash_destroy(&ctx->arrays);
61 12096 : }
62 :
63 10120 : static inline void php_colopl_bc_diagnostic_error_state_suppress(php_colopl_bc_diagnostic_error_state *state)
64 : {
65 10120 : state->error_reporting = EG(error_reporting);
66 10120 : state->user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
67 10120 : state->record_errors = EG(record_errors);
68 :
69 10120 : EG(error_reporting) = 0;
70 10120 : EG(user_error_handler_error_reporting) = 0;
71 10120 : EG(record_errors) = false;
72 10120 : }
73 :
74 10120 : static inline void php_colopl_bc_diagnostic_error_state_restore(php_colopl_bc_diagnostic_error_state *state)
75 : {
76 10120 : EG(record_errors) = state->record_errors;
77 10120 : EG(user_error_handler_error_reporting) = state->user_error_handler_error_reporting;
78 10120 : EG(error_reporting) = state->error_reporting;
79 10120 : }
80 :
81 10120 : static inline bool php_colopl_bc_clear_diagnostic_exception(void)
82 : {
83 10120 : if (EG(exception)) {
84 0 : zend_clear_exception();
85 :
86 0 : return true;
87 : }
88 :
89 10120 : return false;
90 : }
91 :
92 27812 : static inline bool php_colopl_bc_zval_contains_object(zval *op, HashTable *seen_arrays)
93 : {
94 : zend_array *array;
95 : zval *entry;
96 :
97 27812 : ZVAL_DEREF(op);
98 27812 : if (Z_TYPE_P(op) == IS_INDIRECT) {
99 0 : op = Z_INDIRECT_P(op);
100 0 : ZVAL_DEREF(op);
101 : }
102 :
103 27812 : switch (Z_TYPE_P(op)) {
104 1900 : case IS_OBJECT:
105 1900 : return true;
106 1368 : case IS_ARRAY:
107 1368 : array = Z_ARRVAL_P(op);
108 1368 : if (zend_hash_index_exists(seen_arrays, (zend_ulong)(uintptr_t) array)) {
109 44 : return false;
110 : }
111 1324 : zend_hash_index_add_empty_element(seen_arrays, (zend_ulong)(uintptr_t) array);
112 :
113 6232 : ZEND_HASH_FOREACH_VAL(array, entry) {
114 4912 : if (php_colopl_bc_zval_contains_object(entry, seen_arrays)) {
115 4 : return true;
116 : }
117 : } ZEND_HASH_FOREACH_END();
118 :
119 1320 : return false;
120 24544 : default:
121 24544 : return false;
122 : }
123 : }
124 :
125 11916 : static inline bool php_colopl_bc_zvals_contain_object(zval *op1, zval *op2)
126 : {
127 : HashTable seen_arrays;
128 : bool contains_object;
129 :
130 11916 : zend_hash_init(&seen_arrays, 8, NULL, NULL, 0);
131 11916 : contains_object =
132 22788 : php_colopl_bc_zval_contains_object(op1, &seen_arrays) ||
133 10872 : php_colopl_bc_zval_contains_object(op2, &seen_arrays);
134 11916 : zend_hash_destroy(&seen_arrays);
135 :
136 11916 : return contains_object;
137 : }
138 :
139 108 : static inline bool php_colopl_bc_zval_contains_child_array(zval *op)
140 : {
141 : zval *entry;
142 :
143 108 : ZVAL_DEREF(op);
144 108 : if (Z_TYPE_P(op) == IS_INDIRECT) {
145 0 : op = Z_INDIRECT_P(op);
146 0 : ZVAL_DEREF(op);
147 : }
148 :
149 108 : if (Z_TYPE_P(op) != IS_ARRAY) {
150 0 : return false;
151 : }
152 :
153 3192 : ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(op), entry) {
154 3092 : ZVAL_DEREF(entry);
155 3092 : if (Z_TYPE_P(entry) == IS_INDIRECT) {
156 0 : entry = Z_INDIRECT_P(entry);
157 0 : ZVAL_DEREF(entry);
158 : }
159 3092 : if (Z_TYPE_P(entry) == IS_ARRAY) {
160 8 : return true;
161 : }
162 : } ZEND_HASH_FOREACH_END();
163 :
164 100 : return false;
165 : }
166 :
167 24844 : static inline bool php_colopl_bc_zval_contains_recursive_array_ex(zval *op, HashTable *seen_arrays, HashTable *active_arrays)
168 : {
169 : zend_array *array;
170 : zend_ulong key;
171 : zval *entry;
172 24844 : bool contains_recursive_array = false;
173 :
174 24844 : ZVAL_DEREF(op);
175 24844 : if (Z_TYPE_P(op) == IS_INDIRECT) {
176 0 : op = Z_INDIRECT_P(op);
177 0 : ZVAL_DEREF(op);
178 : }
179 :
180 24844 : if (Z_TYPE_P(op) != IS_ARRAY) {
181 23624 : return false;
182 : }
183 :
184 1220 : array = Z_ARRVAL_P(op);
185 1220 : key = (zend_ulong)(uintptr_t) array;
186 1220 : if (zend_hash_index_exists(active_arrays, key)) {
187 0 : return true;
188 : }
189 1220 : if (zend_hash_index_exists(seen_arrays, key)) {
190 40 : return false;
191 : }
192 :
193 1180 : zend_hash_index_add_empty_element(active_arrays, key);
194 5884 : ZEND_HASH_FOREACH_VAL(array, entry) {
195 4704 : if (php_colopl_bc_zval_contains_recursive_array_ex(entry, seen_arrays, active_arrays)) {
196 0 : contains_recursive_array = true;
197 0 : break;
198 : }
199 : } ZEND_HASH_FOREACH_END();
200 1180 : zend_hash_index_del(active_arrays, key);
201 1180 : if (contains_recursive_array) {
202 0 : return true;
203 : }
204 1180 : zend_hash_index_add_empty_element(seen_arrays, key);
205 :
206 1180 : return false;
207 : }
208 :
209 100 : static inline bool php_colopl_bc_zval_contains_recursive_array(zval *op)
210 : {
211 : HashTable seen_arrays, active_arrays;
212 : bool contains_recursive_array;
213 :
214 100 : zend_hash_init(&seen_arrays, 8, NULL, NULL, 0);
215 100 : zend_hash_init(&active_arrays, 8, NULL, NULL, 0);
216 100 : contains_recursive_array = php_colopl_bc_zval_contains_recursive_array_ex(op, &seen_arrays, &active_arrays);
217 100 : zend_hash_destroy(&active_arrays);
218 100 : zend_hash_destroy(&seen_arrays);
219 :
220 100 : return contains_recursive_array;
221 : }
222 :
223 10020 : static inline bool php_colopl_bc_zvals_contain_recursive_array(zval *op1, zval *op2)
224 : {
225 : HashTable seen_arrays, active_arrays;
226 : bool contains_recursive_array;
227 :
228 10020 : zend_hash_init(&seen_arrays, 8, NULL, NULL, 0);
229 10020 : zend_hash_init(&active_arrays, 8, NULL, NULL, 0);
230 10020 : contains_recursive_array =
231 20040 : php_colopl_bc_zval_contains_recursive_array_ex(op1, &seen_arrays, &active_arrays) ||
232 10020 : php_colopl_bc_zval_contains_recursive_array_ex(op2, &seen_arrays, &active_arrays);
233 10020 : zend_hash_destroy(&active_arrays);
234 10020 : zend_hash_destroy(&seen_arrays);
235 :
236 10020 : return contains_recursive_array;
237 : }
238 :
239 24844 : static inline bool php_colopl_bc_snapshot_zval(zval *dst, zval *src, php_colopl_bc_snapshot_context *ctx)
240 : {
241 : zend_object *src_object, *object, *cached_obj;
242 : zend_array *src_array, *array, *cached_arr;
243 : zend_ulong num_key;
244 : zend_string *str_key;
245 : zval *entry, new_entry, *src_prop, *dst_prop;
246 : uint32_t i;
247 : ptrdiff_t offset;
248 :
249 24844 : ZVAL_DEREF(src);
250 :
251 24844 : switch (Z_TYPE_P(src)) {
252 1220 : case IS_ARRAY: {
253 1220 : src_array = Z_ARRVAL_P(src);
254 1220 : cached_arr = zend_hash_index_find_ptr(&ctx->arrays, (zend_ulong)(uintptr_t) src_array);
255 1220 : if (cached_arr != NULL) {
256 40 : GC_ADDREF(cached_arr);
257 40 : ZVAL_ARR(dst, cached_arr);
258 :
259 40 : return true;
260 : }
261 :
262 1180 : array = zend_new_array(zend_hash_num_elements(src_array));
263 1180 : ZVAL_ARR(dst, array);
264 1180 : zend_hash_index_update_ptr(&ctx->arrays, (zend_ulong)(uintptr_t) src_array, array);
265 :
266 5884 : ZEND_HASH_FOREACH_KEY_VAL(src_array, num_key, str_key, entry) {
267 4704 : if (Z_TYPE_P(entry) == IS_INDIRECT) {
268 0 : entry = Z_INDIRECT_P(entry);
269 : }
270 :
271 4704 : if (!php_colopl_bc_snapshot_zval(&new_entry, entry, ctx)) {
272 0 : zval_ptr_dtor(dst);
273 0 : ZVAL_UNDEF(dst);
274 :
275 0 : return false;
276 : }
277 :
278 4704 : if (str_key) {
279 2296 : zend_hash_update(array, str_key, &new_entry);
280 : } else {
281 2408 : zend_hash_index_update(array, num_key, &new_entry);
282 : }
283 : } ZEND_HASH_FOREACH_END();
284 :
285 1180 : return true;
286 : }
287 0 : case IS_OBJECT: {
288 0 : src_object = Z_OBJ_P(src);
289 0 : cached_obj = zend_hash_index_find_ptr(&ctx->objects, (zend_ulong)(uintptr_t) src_object);
290 0 : if (cached_obj != NULL) {
291 0 : GC_ADDREF(cached_obj);
292 0 : ZVAL_OBJ(dst, cached_obj);
293 :
294 0 : return true;
295 : }
296 :
297 0 : if (src_object->handlers != &std_object_handlers) {
298 0 : return false;
299 : }
300 :
301 0 : object = zend_objects_new(src_object->ce);
302 :
303 0 : zend_object_store_ctor_failed(object);
304 0 : ZVAL_OBJ(dst, object);
305 0 : zend_hash_index_update_ptr(&ctx->objects, (zend_ulong)(uintptr_t) src_object, object);
306 :
307 0 : for (i = 0; i < src_object->ce->default_properties_count; i++) {
308 0 : src_prop = src_object->properties_table + i;
309 0 : dst_prop = object->properties_table + i;
310 :
311 0 : ZVAL_UNDEF(dst_prop);
312 0 : Z_PROP_FLAG_P(dst_prop) = Z_PROP_FLAG_P(src_prop);
313 :
314 0 : if (Z_TYPE_P(src_prop) == IS_UNDEF) {
315 0 : continue;
316 : }
317 :
318 0 : if (!php_colopl_bc_snapshot_zval(dst_prop, src_prop, ctx)) {
319 0 : zval_ptr_dtor(dst);
320 0 : ZVAL_UNDEF(dst);
321 :
322 0 : return false;
323 : }
324 :
325 0 : Z_PROP_FLAG_P(dst_prop) = Z_PROP_FLAG_P(src_prop);
326 : }
327 :
328 0 : if (src_object->properties != NULL && zend_hash_num_elements(src_object->properties) > 0) {
329 0 : object->properties = zend_new_array(zend_hash_num_elements(src_object->properties));
330 0 : zend_hash_real_init_mixed(object->properties);
331 :
332 0 : ZEND_HASH_FOREACH_KEY_VAL(src_object->properties, num_key, str_key, entry) {
333 0 : if (Z_TYPE_P(entry) == IS_INDIRECT) {
334 0 : offset = Z_INDIRECT_P(entry) - src_object->properties_table;
335 0 : if (offset >= 0 && offset < src_object->ce->default_properties_count) {
336 0 : ZVAL_INDIRECT(&new_entry, object->properties_table + offset);
337 : } else {
338 0 : entry = Z_INDIRECT_P(entry);
339 0 : if (!php_colopl_bc_snapshot_zval(&new_entry, entry, ctx)) {
340 0 : zval_ptr_dtor(dst);
341 0 : ZVAL_UNDEF(dst);
342 :
343 0 : return false;
344 : }
345 : }
346 : } else {
347 0 : if (!php_colopl_bc_snapshot_zval(&new_entry, entry, ctx)) {
348 0 : zval_ptr_dtor(dst);
349 0 : ZVAL_UNDEF(dst);
350 :
351 0 : return false;
352 : }
353 0 : Z_PROP_FLAG_P(&new_entry) = Z_PROP_FLAG_P(entry);
354 : }
355 :
356 0 : if (str_key) {
357 0 : zend_hash_update(object->properties, str_key, &new_entry);
358 : } else {
359 0 : zend_hash_index_update(object->properties, num_key, &new_entry);
360 : }
361 : } ZEND_HASH_FOREACH_END();
362 : }
363 :
364 0 : return true;
365 : }
366 23624 : default:
367 23624 : ZVAL_DUP(dst, src);
368 23624 : return true;
369 : }
370 : }
371 :
372 80800 : static inline zval *php_colopl_bc_hash_get_ordered_value(HashTable *ht, uint32_t *pos, zend_ulong *num_key, zend_string **str_key)
373 : {
374 : zval *value;
375 : Bucket *bucket;
376 :
377 80800 : if (HT_IS_PACKED(ht)) {
378 55800 : while (*pos < ht->nNumUsed) {
379 : #if PHP_VERSION_ID >= 80200
380 55752 : value = ht->arPacked + *pos;
381 55752 : *num_key = *pos;
382 : #else
383 : bucket = ht->arData + *pos;
384 : value = &bucket->val;
385 : *num_key = bucket->h;
386 : #endif
387 55752 : *str_key = NULL;
388 55752 : (*pos)++;
389 55752 : if (Z_TYPE_P(value) != IS_UNDEF) {
390 55752 : return value;
391 : }
392 : }
393 : } else {
394 25000 : while (*pos < ht->nNumUsed) {
395 24992 : bucket = ht->arData + *pos;
396 24992 : (*pos)++;
397 24992 : if (Z_TYPE(bucket->val) != IS_UNDEF) {
398 24992 : *num_key = bucket->h;
399 24992 : *str_key = bucket->key;
400 24992 : return &bucket->val;
401 : }
402 : }
403 : }
404 :
405 56 : return NULL;
406 : }
407 :
408 68 : static inline void php_colopl_bc_user_sort_context_init(php_colopl_bc_user_sort_context *ctx)
409 : {
410 68 : ctx->zero_pairs = NULL;
411 68 : ctx->zero_pair_count = 0;
412 68 : ctx->zero_pair_capacity = 0;
413 68 : }
414 :
415 68 : static inline void php_colopl_bc_user_sort_context_destroy(php_colopl_bc_user_sort_context *ctx)
416 : {
417 68 : if (ctx->zero_pairs != NULL) {
418 64 : efree(ctx->zero_pairs);
419 : }
420 68 : }
421 :
422 7468 : static inline void php_colopl_bc_user_sort_context_record_zero_pair(Bucket *first, Bucket *second)
423 : {
424 7468 : php_colopl_bc_user_sort_context *ctx = COLOPL_BC_G(php74_user_sort_context);
425 : uint32_t first_pos, second_pos, new_capacity;
426 :
427 7468 : if (ctx == NULL) {
428 600 : return;
429 : }
430 :
431 6868 : first_pos = Z_EXTRA(first->val);
432 6868 : second_pos = Z_EXTRA(second->val);
433 6868 : if (first_pos == second_pos) {
434 0 : return;
435 : }
436 :
437 6868 : if (ctx->zero_pair_count == ctx->zero_pair_capacity) {
438 248 : new_capacity = ctx->zero_pair_capacity == 0 ? 16 : ctx->zero_pair_capacity * 2;
439 248 : ctx->zero_pairs = erealloc(ctx->zero_pairs, sizeof(php_colopl_bc_user_sort_zero_pair) * new_capacity);
440 248 : ctx->zero_pair_capacity = new_capacity;
441 : }
442 :
443 6868 : ctx->zero_pairs[ctx->zero_pair_count].first = first_pos;
444 6868 : ctx->zero_pairs[ctx->zero_pair_count].second = second_pos;
445 6868 : ctx->zero_pair_count++;
446 : }
447 :
448 2984 : static inline bool php_colopl_bc_hash_get_ordered_entry_at(HashTable *ht, uint32_t target_pos, zval **value, zend_ulong *num_key, zend_string **str_key)
449 : {
450 2984 : uint32_t pos = 0, ordered_pos = 0;
451 :
452 71464 : while ((*value = php_colopl_bc_hash_get_ordered_value(ht, &pos, num_key, str_key)) != NULL) {
453 71464 : if (ordered_pos == target_pos) {
454 2984 : return true;
455 : }
456 68480 : ordered_pos++;
457 : }
458 :
459 0 : return false;
460 : }
461 :
462 1348 : static inline bool php_colopl_bc_user_sort_pair_values_identical(zval *original, uint32_t first_pos, uint32_t second_pos)
463 : {
464 : zend_ulong first_num_key, second_num_key;
465 : zend_string *first_str_key, *second_str_key;
466 : zval *first, *second;
467 :
468 1348 : if (Z_ISUNDEF_P(original)) {
469 0 : return false;
470 : }
471 :
472 1348 : if (!php_colopl_bc_hash_get_ordered_entry_at(Z_ARRVAL_P(original), first_pos, &first, &first_num_key, &first_str_key) ||
473 1348 : !php_colopl_bc_hash_get_ordered_entry_at(Z_ARRVAL_P(original), second_pos, &second, &second_num_key, &second_str_key)) {
474 0 : return false;
475 : }
476 :
477 1348 : return zend_is_identical(first, second);
478 : }
479 :
480 240 : static inline bool php_colopl_bc_user_sort_find_final_position_by_key(zval *legacy, zend_ulong original_num_key, zend_string *original_str_key, uint32_t *final_pos)
481 : {
482 : zend_ulong num_key;
483 : zend_string *str_key;
484 : zval *value;
485 240 : uint32_t pos = 0;
486 :
487 240 : *final_pos = 0;
488 5440 : while ((value = php_colopl_bc_hash_get_ordered_value(Z_ARRVAL_P(legacy), &pos, &num_key, &str_key)) != NULL) {
489 : (void) value;
490 :
491 5440 : if (original_str_key != NULL) {
492 2304 : if (str_key != NULL && zend_string_equals(str_key, original_str_key)) {
493 144 : return true;
494 : }
495 3136 : } else if (str_key == NULL && num_key == original_num_key) {
496 96 : return true;
497 : }
498 :
499 5200 : (*final_pos)++;
500 : }
501 :
502 0 : return false;
503 : }
504 :
505 48 : static inline bool php_colopl_bc_user_sort_find_final_position_by_value(zval *legacy, zval *original_value, uint32_t *final_pos)
506 : {
507 : zend_ulong num_key;
508 : zend_string *str_key;
509 : zval *value;
510 48 : uint32_t pos = 0;
511 :
512 48 : *final_pos = 0;
513 1568 : while ((value = php_colopl_bc_hash_get_ordered_value(Z_ARRVAL_P(legacy), &pos, &num_key, &str_key)) != NULL) {
514 : (void) num_key;
515 : (void) str_key;
516 :
517 1568 : if (zend_is_identical(value, original_value)) {
518 48 : return true;
519 : }
520 :
521 1520 : (*final_pos)++;
522 : }
523 :
524 0 : return false;
525 : }
526 :
527 288 : static inline bool php_colopl_bc_user_sort_find_final_position(zval *legacy, zval *original, uint32_t original_pos, bool renumber, uint32_t *final_pos)
528 : {
529 : zend_ulong num_key;
530 : zend_string *str_key;
531 : zval *value;
532 :
533 288 : if (Z_ISUNDEF_P(original) ||
534 288 : !php_colopl_bc_hash_get_ordered_entry_at(Z_ARRVAL_P(original), original_pos, &value, &num_key, &str_key)) {
535 0 : return false;
536 : }
537 :
538 288 : if (renumber) {
539 48 : return php_colopl_bc_user_sort_find_final_position_by_value(legacy, value, final_pos);
540 : }
541 :
542 240 : return php_colopl_bc_user_sort_find_final_position_by_key(legacy, num_key, str_key, final_pos);
543 : }
544 :
545 68 : static inline bool php_colopl_bc_user_sort_context_detect_incompatible_order(php_colopl_bc_user_sort_context *ctx, zval *legacy, zval *original, bool renumber)
546 : {
547 : uint32_t num_elements, i, first_pos, second_pos, first_final_pos, second_final_pos;
548 :
549 68 : if (ctx->zero_pair_count == 0) {
550 4 : return false;
551 : }
552 :
553 64 : num_elements = zend_hash_num_elements(Z_ARRVAL_P(legacy));
554 64 : if (num_elements == 0) {
555 0 : return false;
556 : }
557 :
558 1484 : for (i = 0; i < ctx->zero_pair_count; i++) {
559 1468 : first_pos = ctx->zero_pairs[i].first;
560 1468 : second_pos = ctx->zero_pairs[i].second;
561 1468 : if (first_pos >= num_elements || second_pos >= num_elements) {
562 0 : continue;
563 : }
564 :
565 1468 : if (renumber && php_colopl_bc_user_sort_pair_values_identical(original, first_pos, second_pos)) {
566 1324 : continue;
567 : }
568 :
569 144 : if (!php_colopl_bc_user_sort_find_final_position(legacy, original, first_pos, renumber, &first_final_pos) ||
570 144 : !php_colopl_bc_user_sort_find_final_position(legacy, original, second_pos, renumber, &second_final_pos)) {
571 0 : continue;
572 : }
573 :
574 144 : if ((first_pos < second_pos && first_final_pos > second_final_pos) ||
575 48 : (first_pos > second_pos && first_final_pos < second_final_pos)) {
576 48 : return true;
577 : }
578 : }
579 :
580 16 : return false;
581 : }
582 :
583 100 : static inline bool php_colopl_bc_same_snapshot_array_pair_seen(HashTable *seen_arrays, zend_array *legacy_array, zend_array *native_array)
584 : {
585 : uintptr_t pair[2];
586 :
587 100 : pair[0] = (uintptr_t) legacy_array;
588 100 : pair[1] = (uintptr_t) native_array;
589 :
590 100 : if (zend_hash_str_exists(seen_arrays, (char *) pair, sizeof(pair))) {
591 0 : return true;
592 : }
593 100 : zend_hash_str_add_empty_element(seen_arrays, (char *) pair, sizeof(pair));
594 :
595 100 : return false;
596 : }
597 :
598 1172 : static inline bool php_colopl_bc_same_snapshot_value_ex(zval *legacy, zval *native, php_colopl_bc_snapshot_context *ctx, HashTable *seen_arrays)
599 : {
600 : zend_object *snapshot;
601 : zend_array *legacy_array, *native_array;
602 : zend_ulong legacy_num_key, native_num_key;
603 : zend_string *legacy_str_key, *native_str_key;
604 : zval *legacy_value, *native_value;
605 : uint32_t legacy_pos, native_pos;
606 :
607 1172 : ZVAL_DEREF(legacy);
608 1172 : ZVAL_DEREF(native);
609 :
610 1172 : if (Z_TYPE_P(legacy) == IS_OBJECT && Z_TYPE_P(native) == IS_OBJECT) {
611 0 : snapshot = zend_hash_index_find_ptr(&ctx->objects, (zend_ulong)(uintptr_t) Z_OBJ_P(legacy));
612 0 : if (snapshot != NULL && snapshot == Z_OBJ_P(native)) {
613 0 : return true;
614 : }
615 : }
616 :
617 1172 : if (Z_TYPE_P(legacy) != IS_ARRAY || Z_TYPE_P(native) != IS_ARRAY) {
618 1072 : return zend_is_identical(legacy, native);
619 : }
620 :
621 100 : legacy_array = Z_ARRVAL_P(legacy);
622 100 : native_array = Z_ARRVAL_P(native);
623 100 : if (php_colopl_bc_same_snapshot_array_pair_seen(seen_arrays, legacy_array, native_array)) {
624 0 : return true;
625 : }
626 :
627 100 : if (zend_hash_num_elements(legacy_array) != zend_hash_num_elements(native_array)) {
628 0 : return false;
629 : }
630 :
631 100 : legacy_pos = native_pos = 0;
632 1164 : while ((legacy_value = php_colopl_bc_hash_get_ordered_value(legacy_array, &legacy_pos, &legacy_num_key, &legacy_str_key)) != NULL) {
633 1136 : native_value = php_colopl_bc_hash_get_ordered_value(native_array, &native_pos, &native_num_key, &native_str_key);
634 :
635 1136 : if (native_value == NULL) {
636 0 : return false;
637 : }
638 :
639 1136 : if (legacy_str_key != NULL || native_str_key != NULL) {
640 436 : if (legacy_str_key == NULL || native_str_key == NULL) {
641 64 : return false;
642 : }
643 372 : if (!zend_string_equals(legacy_str_key, native_str_key)) {
644 0 : return false;
645 : }
646 700 : } else if (legacy_num_key != native_num_key) {
647 0 : return false;
648 : }
649 :
650 1072 : if (!php_colopl_bc_same_snapshot_value_ex(legacy_value, native_value, ctx, seen_arrays)) {
651 8 : return false;
652 : }
653 : }
654 :
655 28 : return php_colopl_bc_hash_get_ordered_value(native_array, &native_pos, &native_num_key, &native_str_key) == NULL;
656 : }
657 :
658 100 : static inline bool php_colopl_bc_same_snapshot_value(zval *legacy, zval *native, php_colopl_bc_snapshot_context *ctx)
659 : {
660 : HashTable seen_arrays;
661 : bool same;
662 :
663 100 : zend_hash_init(&seen_arrays, 8, NULL, NULL, 0);
664 100 : same = php_colopl_bc_same_snapshot_value_ex(legacy, native, ctx, &seen_arrays);
665 100 : zend_hash_destroy(&seen_arrays);
666 :
667 100 : return same;
668 : }
669 :
670 0 : static inline void php_colopl_bc_convert_object_to_type(zval *op, zval *dst, uint32_t ctype)
671 : {
672 0 : ZVAL_UNDEF(dst);
673 0 : if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), dst, ctype) == FAILURE) {
674 0 : zend_error(
675 : E_WARNING,
676 : "Object of class %s could not be converted to %s",
677 0 : ZSTR_VAL(Z_OBJCE_P(op)->name),
678 : zend_get_type_by_const(ctype)
679 : );
680 : }
681 0 : }
682 :
683 7704 : static zend_never_inline zval* ZEND_FASTCALL _php_colopl_bc_zendi_convert_scalar_to_number_silent(zval *op, zval *holder)
684 : {
685 7704 : switch (Z_TYPE_P(op)) {
686 0 : case IS_NULL:
687 : case IS_FALSE:
688 0 : ZVAL_LONG(holder, 0);
689 0 : return holder;
690 0 : case IS_TRUE:
691 0 : ZVAL_LONG(holder, 1);
692 0 : return holder;
693 3532 : case IS_STRING:
694 3532 : if ((Z_TYPE_INFO_P(holder) = is_numeric_string(Z_STRVAL_P(op), Z_STRLEN_P(op), &Z_LVAL_P(holder), &Z_DVAL_P(holder), 1)) == 0) {
695 1096 : ZVAL_LONG(holder, 0);
696 : }
697 3532 : return holder;
698 0 : case IS_RESOURCE:
699 0 : ZVAL_LONG(holder, Z_RES_HANDLE_P(op));
700 0 : return holder;
701 0 : case IS_OBJECT:
702 0 : php_colopl_bc_convert_object_to_type(op, holder, _IS_NUMBER);
703 0 : if (UNEXPECTED(EG(exception)) ||
704 0 : UNEXPECTED(Z_TYPE_P(holder) != IS_LONG && Z_TYPE_P(holder) != IS_DOUBLE)) {
705 0 : ZVAL_LONG(holder, 1);
706 : }
707 0 : return holder;
708 4172 : case IS_LONG:
709 : case IS_DOUBLE:
710 : default:
711 4172 : return op;
712 : }
713 : }
714 :
715 15120 : static int legacy_compare_fast(zval *op1, zval *op2)
716 : {
717 15120 : int converted = 0;
718 : zval op1_copy, op2_copy;
719 :
720 : while (1) {
721 18972 : switch (COLOPL_BC_TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
722 8880 : case COLOPL_BC_TYPE_PAIR(IS_LONG, IS_LONG):
723 8880 : return Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)<Z_LVAL_P(op2)?-1:0);
724 :
725 200 : case COLOPL_BC_TYPE_PAIR(IS_DOUBLE, IS_LONG):
726 200 : return ZEND_NORMALIZE_BOOL(Z_DVAL_P(op1) - (double)Z_LVAL_P(op2));
727 :
728 200 : case COLOPL_BC_TYPE_PAIR(IS_LONG, IS_DOUBLE):
729 200 : return ZEND_NORMALIZE_BOOL((double)Z_LVAL_P(op1) - Z_DVAL_P(op2));
730 :
731 60 : case COLOPL_BC_TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
732 60 : if (Z_DVAL_P(op1) == Z_DVAL_P(op2)) {
733 60 : return 0;
734 : } else {
735 0 : return ZEND_NORMALIZE_BOOL(Z_DVAL_P(op1) - Z_DVAL_P(op2));
736 : }
737 :
738 80 : case COLOPL_BC_TYPE_PAIR(IS_ARRAY, IS_ARRAY):
739 80 : return zend_compare_arrays(op1, op2);
740 :
741 100 : case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_NULL):
742 : case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_FALSE):
743 : case COLOPL_BC_TYPE_PAIR(IS_FALSE, IS_NULL):
744 : case COLOPL_BC_TYPE_PAIR(IS_FALSE, IS_FALSE):
745 : case COLOPL_BC_TYPE_PAIR(IS_TRUE, IS_TRUE):
746 100 : return 0;
747 :
748 20 : case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_TRUE):
749 20 : return -1;
750 :
751 20 : case COLOPL_BC_TYPE_PAIR(IS_TRUE, IS_NULL):
752 20 : return 1;
753 :
754 60 : case COLOPL_BC_TYPE_PAIR(IS_TRUE, IS_OBJECT):
755 60 : return zval_is_true(op2) ? 0 : 1;
756 :
757 60 : case COLOPL_BC_TYPE_PAIR(IS_FALSE, IS_OBJECT):
758 60 : return zval_is_true(op2) ? -1 : 0;
759 :
760 60 : case COLOPL_BC_TYPE_PAIR(IS_OBJECT, IS_TRUE):
761 60 : return zval_is_true(op1) ? 0 : -1;
762 :
763 60 : case COLOPL_BC_TYPE_PAIR(IS_OBJECT, IS_FALSE):
764 60 : return zval_is_true(op1) ? 1 : 0;
765 :
766 1584 : case COLOPL_BC_TYPE_PAIR(IS_STRING, IS_STRING):
767 1584 : if (Z_STR_P(op1) == Z_STR_P(op2)) {
768 772 : return 0;
769 : }
770 812 : return zendi_smart_strcmp(Z_STR_P(op1), Z_STR_P(op2));
771 :
772 100 : case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_STRING):
773 100 : return Z_STRLEN_P(op2) == 0 ? 0 : -1;
774 :
775 100 : case COLOPL_BC_TYPE_PAIR(IS_STRING, IS_NULL):
776 100 : return Z_STRLEN_P(op1) == 0 ? 0 : 1;
777 :
778 60 : case COLOPL_BC_TYPE_PAIR(IS_OBJECT, IS_NULL):
779 60 : return 1;
780 :
781 60 : case COLOPL_BC_TYPE_PAIR(IS_NULL, IS_OBJECT):
782 60 : return -1;
783 :
784 7268 : default:
785 7268 : if (Z_ISREF_P(op1)) {
786 0 : op1 = Z_REFVAL_P(op1);
787 0 : continue;
788 7268 : } else if (Z_ISREF_P(op2)) {
789 0 : op2 = Z_REFVAL_P(op2);
790 0 : continue;
791 : }
792 :
793 7268 : if (Z_TYPE_P(op1) == IS_OBJECT
794 864 : && Z_TYPE_P(op2) == IS_OBJECT
795 180 : && Z_OBJ_P(op1) == Z_OBJ_P(op2)) {
796 60 : return 0;
797 7208 : } else if (Z_TYPE_P(op1) == IS_OBJECT) {
798 804 : return Z_OBJ_HANDLER_P(op1, compare)(op1, op2);
799 6404 : } else if (Z_TYPE_P(op2) == IS_OBJECT) {
800 672 : return Z_OBJ_HANDLER_P(op2, compare)(op1, op2);
801 : }
802 :
803 5732 : if (!converted) {
804 5012 : if (Z_TYPE_P(op1) < IS_TRUE) {
805 360 : return zval_is_true(op2) ? -1 : 0;
806 4652 : } else if (Z_TYPE_P(op1) == IS_TRUE) {
807 240 : return zval_is_true(op2) ? 0 : 1;
808 4412 : } else if (Z_TYPE_P(op2) < IS_TRUE) {
809 340 : return zval_is_true(op1) ? 1 : 0;
810 4072 : } else if (Z_TYPE_P(op2) == IS_TRUE) {
811 220 : return zval_is_true(op1) ? 0 : -1;
812 : } else {
813 3852 : op1 = _php_colopl_bc_zendi_convert_scalar_to_number_silent(op1, &op1_copy);
814 3852 : op2 = _php_colopl_bc_zendi_convert_scalar_to_number_silent(op2, &op2_copy);
815 3852 : if (EG(exception)) {
816 0 : return 1; /* to stop comparison of arrays */
817 : }
818 3852 : converted = 1;
819 : }
820 720 : } else if (Z_TYPE_P(op1)==IS_ARRAY) {
821 360 : return 1;
822 360 : } else if (Z_TYPE_P(op2)==IS_ARRAY) {
823 360 : return -1;
824 : } else {
825 0 : ZEND_UNREACHABLE();
826 : zend_throw_error(NULL, "Unsupported operand types");
827 : return 1;
828 : }
829 : }
830 : }
831 : }
832 :
833 11916 : static int legacy_compare_slow(zval *op1, zval *op2)
834 : {
835 : php_colopl_bc_snapshot_context snapshot_context;
836 : php_colopl_bc_diagnostic_error_state error_state;
837 : zval native_op1, native_op2;
838 : bool have_native_snapshot, native_failed;
839 : int bc, native;
840 :
841 11916 : if (COLOPL_BC_G(php74_compare_mode) <= COLOPL_BC_PHP74_COMPARE_MODE_SILENT) {
842 0 : return legacy_compare_fast(op1, op2);
843 : }
844 :
845 11916 : ZVAL_UNDEF(&native_op1);
846 11916 : ZVAL_UNDEF(&native_op2);
847 11916 : php_colopl_bc_snapshot_context_init(&snapshot_context);
848 11916 : have_native_snapshot = !php_colopl_bc_zvals_contain_object(op1, op2) &&
849 20040 : !php_colopl_bc_zvals_contain_recursive_array(op1, op2) &&
850 31956 : php_colopl_bc_snapshot_zval(&native_op1, op1, &snapshot_context) &&
851 10020 : php_colopl_bc_snapshot_zval(&native_op2, op2, &snapshot_context)
852 : ;
853 :
854 11916 : bc = legacy_compare_fast(op1, op2);
855 :
856 11916 : if (have_native_snapshot && !EG(exception)) {
857 10020 : php_colopl_bc_diagnostic_error_state_suppress(&error_state);
858 10020 : native = zend_compare(&native_op1, &native_op2);
859 10020 : native_failed = php_colopl_bc_clear_diagnostic_exception();
860 10020 : php_colopl_bc_diagnostic_error_state_restore(&error_state);
861 : } else {
862 1896 : native = bc;
863 1896 : native_failed = false;
864 : }
865 :
866 11916 : if (have_native_snapshot && !native_failed && native != bc) {
867 812 : if (COLOPL_BC_G(php74_compare_mode) & COLOPL_BC_PHP74_COMPARE_MODE_LOG) {
868 808 : php_log_err_with_severity("Incompatible compare detected", LOG_NOTICE);
869 : }
870 812 : if (COLOPL_BC_G(php74_compare_mode) & COLOPL_BC_PHP74_COMPARE_MODE_DEPRECATED) {
871 808 : php_error_docref(NULL, E_DEPRECATED, "Incompatible compare detected");
872 : }
873 : }
874 :
875 11916 : if (!Z_ISUNDEF(native_op2)) {
876 10020 : zval_ptr_dtor(&native_op2);
877 : }
878 :
879 11916 : if (!Z_ISUNDEF(native_op1)) {
880 10020 : zval_ptr_dtor(&native_op1);
881 : }
882 :
883 11916 : php_colopl_bc_snapshot_context_destroy(&snapshot_context);
884 :
885 11916 : return bc;
886 : }
887 :
888 258 : PHP_INI_MH(OnUpdateCompareMode)
889 : {
890 258 : if (OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) {
891 0 : return FAILURE;
892 : }
893 :
894 258 : if (COLOPL_BC_G(php74_compare_mode) <= COLOPL_BC_PHP74_COMPARE_MODE_SILENT) {
895 162 : COLOPL_BC_G(php74_compare_func) = legacy_compare_fast;
896 : } else {
897 96 : COLOPL_BC_G(php74_compare_func) = legacy_compare_slow;
898 : }
899 :
900 258 : return SUCCESS;
901 : }
902 :
903 15120 : int php_colopl_bc_compare(zval *op1, zval *op2)
904 : {
905 15120 : return COLOPL_BC_G(php74_compare_func)(op1, op2);
906 : }
907 :
908 0 : static zend_always_inline bool php_colopl_bc_fast_equal_check_long(zval *op1, zval *op2)
909 : {
910 0 : if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
911 0 : return Z_LVAL_P(op1) == Z_LVAL_P(op2);
912 : }
913 0 : return php_colopl_bc_compare(op1, op2) == 0;
914 : }
915 :
916 8 : static zend_always_inline bool php_colopl_bc_fast_equal_check_string(zval *op1, zval *op2)
917 : {
918 8 : if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
919 0 : return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
920 : }
921 8 : return php_colopl_bc_compare(op1, op2) == 0;
922 : }
923 :
924 8 : static zend_always_inline bool php_colopl_bc_fast_equal_check_function(zval *op1, zval *op2)
925 : {
926 8 : if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
927 0 : if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
928 0 : return Z_LVAL_P(op1) == Z_LVAL_P(op2);
929 0 : } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
930 0 : return ((double)Z_LVAL_P(op1)) == Z_DVAL_P(op2);
931 : }
932 8 : } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
933 0 : if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
934 0 : return Z_DVAL_P(op1) == Z_DVAL_P(op2);
935 0 : } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
936 0 : return Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2));
937 : }
938 8 : } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
939 8 : if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
940 4 : return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
941 : }
942 : }
943 4 : return php_colopl_bc_compare(op1, op2) == 0;
944 : }
945 :
946 : /* array.c */
947 :
948 2792 : static int php_colopl_bc_array_key_compare(Bucket *f, Bucket *s)
949 : {
950 : zend_uchar t;
951 : zend_long l1, l2;
952 : double d;
953 :
954 2792 : if (f->key == NULL) {
955 40 : if (s->key == NULL) {
956 0 : return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
957 : } else {
958 40 : l1 = (zend_long)f->h;
959 40 : t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
960 40 : if (t == IS_LONG) {
961 : /* pass */
962 40 : } else if (t == IS_DOUBLE) {
963 0 : return ZEND_NORMALIZE_BOOL((double)l1 - d);
964 : } else {
965 40 : l2 = 0;
966 : }
967 : }
968 : } else {
969 2752 : if (s->key) {
970 2704 : return zendi_smart_strcmp(f->key, s->key);
971 : } else {
972 48 : l2 = (zend_long)s->h;
973 48 : t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
974 48 : if (t == IS_LONG) {
975 : /* pass */
976 48 : } else if (t == IS_DOUBLE) {
977 0 : return ZEND_NORMALIZE_BOOL(d - (double)l2);
978 : } else {
979 48 : l1 = 0;
980 : }
981 : }
982 : }
983 88 : return ZEND_NORMALIZE_BOOL(l1 - l2);
984 : }
985 :
986 1412 : static int php_colopl_bc_array_reverse_key_compare(Bucket *a, Bucket *b)
987 : {
988 1412 : return php_colopl_bc_array_key_compare(b, a);
989 : }
990 :
991 0 : static int php_colopl_bc_array_key_compare_numeric(Bucket *f, Bucket *s)
992 : {
993 0 : if (f->key == NULL && s->key == NULL) {
994 0 : return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
995 : } else {
996 : double d1, d2;
997 0 : if (f->key) {
998 0 : d1 = zend_strtod(f->key->val, NULL);
999 : } else {
1000 0 : d1 = (double)(zend_long)f->h;
1001 : }
1002 0 : if (s->key) {
1003 0 : d2 = zend_strtod(s->key->val, NULL);
1004 : } else {
1005 0 : d2 = (double)(zend_long)s->h;
1006 : }
1007 0 : return ZEND_NORMALIZE_BOOL(d1 - d2);
1008 : }
1009 : }
1010 :
1011 0 : static int php_colopl_bc_array_reverse_key_compare_numeric(Bucket *a, Bucket *b)
1012 : {
1013 0 : return php_colopl_bc_array_key_compare_numeric(b, a);
1014 : }
1015 :
1016 0 : static int php_colopl_bc_array_key_compare_string_case(Bucket *f, Bucket *s)
1017 : {
1018 : const char *s1, *s2;
1019 : size_t l1, l2;
1020 : char buf1[MAX_LENGTH_OF_LONG + 1];
1021 : char buf2[MAX_LENGTH_OF_LONG + 1];
1022 :
1023 0 : if (f->key) {
1024 0 : s1 = f->key->val;
1025 0 : l1 = f->key->len;
1026 : } else {
1027 0 : s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
1028 0 : l1 = buf1 + sizeof(buf1) - 1 - s1;
1029 : }
1030 0 : if (s->key) {
1031 0 : s2 = s->key->val;
1032 0 : l2 = s->key->len;
1033 : } else {
1034 0 : s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
1035 0 : l2 = buf2 + sizeof(buf2) - 1 - s1;
1036 : }
1037 0 : return zend_binary_strcasecmp_l(s1, l1, s2, l2);
1038 : }
1039 :
1040 0 : static int php_colopl_bc_array_reverse_key_compare_string_case(Bucket *a, Bucket *b)
1041 : {
1042 0 : return php_colopl_bc_array_key_compare_string_case(b, a);
1043 : }
1044 :
1045 0 : static int php_colopl_bc_array_key_compare_string(Bucket *f, Bucket *s)
1046 : {
1047 : const char *s1, *s2;
1048 : size_t l1, l2;
1049 : char buf1[MAX_LENGTH_OF_LONG + 1];
1050 : char buf2[MAX_LENGTH_OF_LONG + 1];
1051 :
1052 0 : if (f->key) {
1053 0 : s1 = f->key->val;
1054 0 : l1 = f->key->len;
1055 : } else {
1056 0 : s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
1057 0 : l1 = buf1 + sizeof(buf1) - 1 - s1;
1058 : }
1059 0 : if (s->key) {
1060 0 : s2 = s->key->val;
1061 0 : l2 = s->key->len;
1062 : } else {
1063 0 : s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
1064 0 : l2 = buf2 + sizeof(buf2) - 1 - s2;
1065 : }
1066 0 : return zend_binary_strcmp(s1, l1, s2, l2);
1067 : }
1068 :
1069 0 : static int php_colopl_bc_array_reverse_key_compare_string(Bucket *a, Bucket *b)
1070 : {
1071 0 : return php_colopl_bc_array_key_compare_string(b, a);
1072 : }
1073 :
1074 0 : static int php_colopl_bc_array_key_compare_string_natural_general(Bucket *f, Bucket *s, int fold_case)
1075 : {
1076 : const char *s1, *s2;
1077 : size_t l1, l2;
1078 : char buf1[MAX_LENGTH_OF_LONG + 1];
1079 : char buf2[MAX_LENGTH_OF_LONG + 1];
1080 :
1081 0 : if (f->key) {
1082 0 : s1 = f->key->val;
1083 0 : l1 = f->key->len;
1084 : } else {
1085 0 : s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
1086 0 : l1 = buf1 + sizeof(buf1) - 1 - s1;
1087 : }
1088 0 : if (s->key) {
1089 0 : s2 = s->key->val;
1090 0 : l2 = s->key->len;
1091 : } else {
1092 0 : s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
1093 0 : l2 = buf2 + sizeof(buf2) - 1 - s1;
1094 : }
1095 0 : return strnatcmp_ex(s1, l1, s2, l2, fold_case);
1096 : }
1097 :
1098 0 : static int php_colopl_bc_array_key_compare_string_natural_case(Bucket *a, Bucket *b)
1099 : {
1100 0 : return php_colopl_bc_array_key_compare_string_natural_general(a, b, 1);
1101 : }
1102 :
1103 0 : static int php_colopl_bc_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b)
1104 : {
1105 0 : return php_colopl_bc_array_key_compare_string_natural_general(b, a, 1);
1106 : }
1107 :
1108 0 : static int php_colopl_bc_array_key_compare_string_natural(Bucket *a, Bucket *b)
1109 : {
1110 0 : return php_colopl_bc_array_key_compare_string_natural_general(a, b, 0);
1111 : }
1112 :
1113 0 : static int php_colopl_bc_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b)
1114 : {
1115 0 : return php_colopl_bc_array_key_compare_string_natural_general(b, a, 0);
1116 : }
1117 :
1118 0 : static int php_colopl_bc_array_key_compare_string_locale(Bucket *f, Bucket *s)
1119 : {
1120 : const char *s1, *s2;
1121 : char buf1[MAX_LENGTH_OF_LONG + 1];
1122 : char buf2[MAX_LENGTH_OF_LONG + 1];
1123 :
1124 0 : if (f->key) {
1125 0 : s1 = f->key->val;
1126 : } else {
1127 0 : s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
1128 : }
1129 0 : if (s->key) {
1130 0 : s2 = s->key->val;
1131 : } else {
1132 0 : s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
1133 : }
1134 0 : return strcoll(s1, s2);
1135 : }
1136 :
1137 0 : static int php_colopl_bc_array_reverse_key_compare_string_locale(Bucket *a, Bucket *b)
1138 : {
1139 0 : return php_colopl_bc_array_key_compare_string_locale(b, a);
1140 : }
1141 :
1142 9276 : static int php_colopl_bc_array_data_compare(Bucket *f, Bucket *s)
1143 : {
1144 9276 : zval *first = &f->val;
1145 9276 : zval *second = &s->val;
1146 :
1147 9276 : if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
1148 0 : first = Z_INDIRECT_P(first);
1149 : }
1150 9276 : if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
1151 0 : second = Z_INDIRECT_P(second);
1152 : }
1153 :
1154 9276 : return php_colopl_bc_compare(first, second);
1155 : }
1156 :
1157 3120 : static int php_colopl_bc_array_reverse_data_compare(Bucket *a, Bucket *b)
1158 : {
1159 3120 : return php_colopl_bc_array_data_compare(a, b) * -1;
1160 : }
1161 :
1162 0 : static int php_colopl_bc_array_data_compare_numeric(Bucket *f, Bucket *s)
1163 : {
1164 0 : zval *first = &f->val;
1165 0 : zval *second = &s->val;
1166 :
1167 0 : if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
1168 0 : first = Z_INDIRECT_P(first);
1169 : }
1170 0 : if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
1171 0 : second = Z_INDIRECT_P(second);
1172 : }
1173 :
1174 0 : return numeric_compare_function(first, second);
1175 : }
1176 :
1177 0 : static int php_colopl_bc_array_reverse_data_compare_numeric(Bucket *a, Bucket *b)
1178 : {
1179 0 : return php_colopl_bc_array_data_compare_numeric(b, a);
1180 : }
1181 :
1182 0 : static int php_colopl_bc_array_data_compare_string_case(Bucket *f, Bucket *s)
1183 : {
1184 0 : zval *first = &f->val;
1185 0 : zval *second = &s->val;
1186 :
1187 0 : if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
1188 0 : first = Z_INDIRECT_P(first);
1189 : }
1190 0 : if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
1191 0 : second = Z_INDIRECT_P(second);
1192 : }
1193 :
1194 0 : return string_case_compare_function(first, second);
1195 : }
1196 :
1197 0 : static int php_colopl_bc_array_reverse_data_compare_string_case(Bucket *a, Bucket *b)
1198 : {
1199 0 : return php_colopl_bc_array_data_compare_string_case(b, a);
1200 : }
1201 :
1202 8 : static int php_colopl_bc_array_data_compare_string(Bucket *f, Bucket *s)
1203 : {
1204 8 : zval *first = &f->val;
1205 8 : zval *second = &s->val;
1206 :
1207 8 : if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
1208 0 : first = Z_INDIRECT_P(first);
1209 : }
1210 8 : if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
1211 0 : second = Z_INDIRECT_P(second);
1212 : }
1213 :
1214 8 : return string_compare_function(first, second);
1215 : }
1216 :
1217 0 : static int php_colopl_bc_array_reverse_data_compare_string(Bucket *a, Bucket *b)
1218 : {
1219 0 : return php_colopl_bc_array_data_compare_string(b, a);
1220 : }
1221 :
1222 0 : static int php_colopl_bc_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case)
1223 : {
1224 : zend_string *tmp_str1, *tmp_str2;
1225 0 : zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
1226 0 : zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
1227 :
1228 0 : int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
1229 :
1230 0 : zend_tmp_string_release(tmp_str1);
1231 0 : zend_tmp_string_release(tmp_str2);
1232 0 : return result;
1233 : }
1234 :
1235 0 : static int php_colopl_bc_array_natural_compare(Bucket *a, Bucket *b)
1236 : {
1237 0 : return php_colopl_bc_array_natural_general_compare(a, b, 0);
1238 : }
1239 :
1240 0 : static int php_colopl_bc_array_reverse_natural_compare(Bucket *a, Bucket *b)
1241 : {
1242 0 : return php_colopl_bc_array_natural_general_compare(b, a, 0);
1243 : }
1244 :
1245 0 : static int php_colopl_bc_array_natural_case_compare(Bucket *a, Bucket *b)
1246 : {
1247 0 : return php_colopl_bc_array_natural_general_compare(a, b, 1);
1248 : }
1249 :
1250 0 : static int php_colopl_bc_array_reverse_natural_case_compare(Bucket *a, Bucket *b)
1251 : {
1252 0 : return php_colopl_bc_array_natural_general_compare(b, a, 1);
1253 : }
1254 :
1255 0 : static int php_colopl_bc_array_data_compare_string_locale(Bucket *f, Bucket *s)
1256 : {
1257 0 : zval *first = &f->val;
1258 0 : zval *second = &s->val;
1259 :
1260 0 : if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
1261 0 : first = Z_INDIRECT_P(first);
1262 : }
1263 0 : if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
1264 0 : second = Z_INDIRECT_P(second);
1265 : }
1266 :
1267 0 : return string_locale_compare_function(first, second);
1268 : }
1269 :
1270 0 : static int php_colopl_bc_array_reverse_data_compare_string_locale(Bucket *a, Bucket *b)
1271 : {
1272 0 : return php_colopl_bc_array_data_compare_string_locale(b, a);
1273 : }
1274 :
1275 5224 : static int php_colopl_bc_array_user_compare(Bucket *f, Bucket *s)
1276 : {
1277 : zend_long ret;
1278 : zval args[2], retval;
1279 : int result;
1280 :
1281 5224 : ZVAL_COPY(&args[0], &f->val);
1282 5224 : ZVAL_COPY(&args[1], &s->val);
1283 :
1284 5224 : COLOPL_BC_G(user_compare_fci).param_count = 2;
1285 5224 : COLOPL_BC_G(user_compare_fci).params = args;
1286 5224 : COLOPL_BC_G(user_compare_fci).retval = &retval;
1287 5224 : if (zend_call_function(&COLOPL_BC_G(user_compare_fci), &COLOPL_BC_G(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1288 5224 : ret = zval_get_long(&retval);
1289 5224 : result = ZEND_NORMALIZE_BOOL(ret);
1290 :
1291 5224 : zval_ptr_dtor(&retval);
1292 5224 : zval_ptr_dtor(&args[1]);
1293 5224 : zval_ptr_dtor(&args[0]);
1294 5224 : if (result == 0) {
1295 5220 : php_colopl_bc_user_sort_context_record_zero_pair(f, s);
1296 : }
1297 :
1298 5224 : return result;
1299 : } else {
1300 0 : zval_ptr_dtor(&args[1]);
1301 0 : zval_ptr_dtor(&args[0]);
1302 0 : php_colopl_bc_user_sort_context_record_zero_pair(f, s);
1303 :
1304 0 : result = 0;
1305 : }
1306 :
1307 0 : return result;
1308 : }
1309 :
1310 2248 : static int php_colopl_bc_array_user_key_compare(Bucket *f, Bucket *s)
1311 : {
1312 : zval args[2];
1313 : zval retval;
1314 : zend_long result;
1315 :
1316 2248 : if (f->key == NULL) {
1317 1464 : ZVAL_LONG(&args[0], f->h);
1318 : } else {
1319 784 : ZVAL_STR_COPY(&args[0], f->key);
1320 : }
1321 2248 : if (s->key == NULL) {
1322 1464 : ZVAL_LONG(&args[1], s->h);
1323 : } else {
1324 784 : ZVAL_STR_COPY(&args[1], s->key);
1325 : }
1326 :
1327 2248 : COLOPL_BC_G(user_compare_fci).param_count = 2;
1328 2248 : COLOPL_BC_G(user_compare_fci).params = args;
1329 2248 : COLOPL_BC_G(user_compare_fci).retval = &retval;;
1330 2248 : if (zend_call_function(&COLOPL_BC_G(user_compare_fci), &COLOPL_BC_G(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
1331 2248 : result = zval_get_long(&retval);
1332 2248 : zval_ptr_dtor(&retval);
1333 : } else {
1334 0 : result = 0;
1335 : }
1336 :
1337 2248 : zval_ptr_dtor(&args[0]);
1338 2248 : zval_ptr_dtor(&args[1]);
1339 :
1340 2248 : result = ZEND_NORMALIZE_BOOL(result);
1341 2248 : if (result == 0) {
1342 2248 : php_colopl_bc_user_sort_context_record_zero_pair(f, s);
1343 : }
1344 :
1345 2248 : return result;
1346 : }
1347 :
1348 44 : static bucket_compare_func_t php_colopl_bc_get_key_compare_func(zend_long sort_type, int reverse)
1349 : {
1350 44 : switch (sort_type & ~PHP_SORT_FLAG_CASE) {
1351 0 : case PHP_SORT_NUMERIC:
1352 0 : if (reverse) {
1353 0 : return php_colopl_bc_array_reverse_key_compare_numeric;
1354 : } else {
1355 0 : return php_colopl_bc_array_key_compare_numeric;
1356 : }
1357 : break;
1358 :
1359 0 : case PHP_SORT_STRING:
1360 0 : if (sort_type & PHP_SORT_FLAG_CASE) {
1361 0 : if (reverse) {
1362 0 : return php_colopl_bc_array_reverse_key_compare_string_case;
1363 : } else {
1364 0 : return php_colopl_bc_array_key_compare_string_case;
1365 : }
1366 : } else {
1367 0 : if (reverse) {
1368 0 : return php_colopl_bc_array_reverse_key_compare_string;
1369 : } else {
1370 0 : return php_colopl_bc_array_key_compare_string;
1371 : }
1372 : }
1373 : break;
1374 :
1375 0 : case PHP_SORT_NATURAL:
1376 0 : if (sort_type & PHP_SORT_FLAG_CASE) {
1377 0 : if (reverse) {
1378 0 : return php_colopl_bc_array_reverse_key_compare_string_natural_case;
1379 : } else {
1380 0 : return php_colopl_bc_array_key_compare_string_natural_case;
1381 : }
1382 : } else {
1383 0 : if (reverse) {
1384 0 : return php_colopl_bc_array_reverse_key_compare_string_natural;
1385 : } else {
1386 0 : return php_colopl_bc_array_key_compare_string_natural;
1387 : }
1388 : }
1389 : break;
1390 :
1391 0 : case PHP_SORT_LOCALE_STRING:
1392 0 : if (reverse) {
1393 0 : return php_colopl_bc_array_reverse_key_compare_string_locale;
1394 : } else {
1395 0 : return php_colopl_bc_array_key_compare_string_locale;
1396 : }
1397 : break;
1398 :
1399 44 : case PHP_SORT_REGULAR:
1400 : default:
1401 44 : if (reverse) {
1402 24 : return php_colopl_bc_array_reverse_key_compare;
1403 : } else {
1404 20 : return php_colopl_bc_array_key_compare;
1405 : }
1406 : break;
1407 : }
1408 : return NULL;
1409 : }
1410 :
1411 100 : static bucket_compare_func_t php_colopl_bc_get_data_compare_func(zend_long sort_type, int reverse)
1412 : {
1413 100 : switch (sort_type & ~PHP_SORT_FLAG_CASE) {
1414 0 : case PHP_SORT_NUMERIC:
1415 0 : if (reverse) {
1416 0 : return php_colopl_bc_array_reverse_data_compare_numeric;
1417 : } else {
1418 0 : return php_colopl_bc_array_data_compare_numeric;
1419 : }
1420 : break;
1421 :
1422 8 : case PHP_SORT_STRING:
1423 8 : if (sort_type & PHP_SORT_FLAG_CASE) {
1424 0 : if (reverse) {
1425 0 : return php_colopl_bc_array_reverse_data_compare_string_case;
1426 : } else {
1427 0 : return php_colopl_bc_array_data_compare_string_case;
1428 : }
1429 : } else {
1430 8 : if (reverse) {
1431 0 : return php_colopl_bc_array_reverse_data_compare_string;
1432 : } else {
1433 8 : return php_colopl_bc_array_data_compare_string;
1434 : }
1435 : }
1436 : break;
1437 :
1438 0 : case PHP_SORT_NATURAL:
1439 0 : if (sort_type & PHP_SORT_FLAG_CASE) {
1440 0 : if (reverse) {
1441 0 : return php_colopl_bc_array_reverse_natural_case_compare;
1442 : } else {
1443 0 : return php_colopl_bc_array_natural_case_compare;
1444 : }
1445 : } else {
1446 0 : if (reverse) {
1447 0 : return php_colopl_bc_array_reverse_natural_compare;
1448 : } else {
1449 0 : return php_colopl_bc_array_natural_compare;
1450 : }
1451 : }
1452 : break;
1453 :
1454 0 : case PHP_SORT_LOCALE_STRING:
1455 0 : if (reverse) {
1456 0 : return php_colopl_bc_array_reverse_data_compare_string_locale;
1457 : } else {
1458 0 : return php_colopl_bc_array_data_compare_string_locale;
1459 : }
1460 : break;
1461 :
1462 92 : case PHP_SORT_REGULAR:
1463 : default:
1464 92 : if (reverse) {
1465 40 : return php_colopl_bc_array_reverse_data_compare;
1466 : } else {
1467 52 : return php_colopl_bc_array_data_compare;
1468 : }
1469 : break;
1470 : }
1471 : return NULL;
1472 : }
1473 :
1474 : #define COLOPL_BC_MULTISORT_ORDER 0
1475 : #define COLOPL_BC_MULTISORT_TYPE 1
1476 : #define COLOPL_BC_MULTISORT_LAST 2
1477 :
1478 1496 : int php_colopl_bc_multisort_compare(const void *a, const void *b)
1479 : {
1480 1496 : Bucket *ab = *(Bucket **)a;
1481 1496 : Bucket *bb = *(Bucket **)b;
1482 : int r;
1483 : zend_long result;
1484 :
1485 1496 : r = 0;
1486 : do {
1487 2992 : result = COLOPL_BC_G(multisort_func)[r](&ab[r], &bb[r]);
1488 2992 : if (result != 0) {
1489 0 : return result > 0 ? 1 : -1;
1490 : }
1491 2992 : r++;
1492 2992 : } while (Z_TYPE(ab[r].val) != IS_UNDEF);
1493 :
1494 1496 : return 0;
1495 : }
1496 :
1497 : #define COLOPL_BC_MULTISORT_ABORT \
1498 : efree(func); \
1499 : efree(arrays); \
1500 : return;
1501 :
1502 596 : static void colopl_bc_array_bucket_p_swap(void *p, void *q)
1503 : {
1504 : Bucket *t;
1505 596 : Bucket **f = (Bucket **)p;
1506 596 : Bucket **g = (Bucket **)q;
1507 :
1508 596 : t = *f;
1509 596 : *f = *g;
1510 596 : *g = t;
1511 596 : }
1512 :
1513 8 : static inline void php_colopl_bc_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
1514 : {
1515 : zval *value, /* value to check for */
1516 : *array, /* array to check in */
1517 : *entry; /* pointer to array entry */
1518 : zend_ulong num_idx;
1519 : zend_string *str_idx;
1520 8 : bool strict = 0; /* strict comparison or not */
1521 :
1522 8 : ZEND_PARSE_PARAMETERS_START(2, 3)
1523 8 : Z_PARAM_ZVAL(value)
1524 8 : Z_PARAM_ARRAY(array)
1525 8 : Z_PARAM_OPTIONAL
1526 8 : Z_PARAM_BOOL(strict)
1527 8 : ZEND_PARSE_PARAMETERS_END();
1528 :
1529 8 : if (strict) {
1530 0 : if (Z_TYPE_P(value) == IS_LONG) {
1531 0 : ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1532 0 : ZVAL_DEREF(entry);
1533 0 : if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
1534 0 : if (behavior == 0) {
1535 0 : RETURN_TRUE;
1536 : } else {
1537 0 : if (str_idx) {
1538 0 : RETURN_STR_COPY(str_idx);
1539 : } else {
1540 0 : RETURN_LONG(num_idx);
1541 : }
1542 : }
1543 : }
1544 : } ZEND_HASH_FOREACH_END();
1545 : } else {
1546 0 : ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1547 0 : ZVAL_DEREF(entry);
1548 0 : if (fast_is_identical_function(value, entry)) {
1549 0 : if (behavior == 0) {
1550 0 : RETURN_TRUE;
1551 : } else {
1552 0 : if (str_idx) {
1553 0 : RETURN_STR_COPY(str_idx);
1554 : } else {
1555 0 : RETURN_LONG(num_idx);
1556 : }
1557 : }
1558 : }
1559 : } ZEND_HASH_FOREACH_END();
1560 : }
1561 : } else {
1562 8 : if (Z_TYPE_P(value) == IS_LONG) {
1563 0 : ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1564 0 : if (php_colopl_bc_fast_equal_check_long(value, entry)) {
1565 0 : if (behavior == 0) {
1566 0 : RETURN_TRUE;
1567 : } else {
1568 0 : if (str_idx) {
1569 0 : RETURN_STR_COPY(str_idx);
1570 : } else {
1571 0 : RETURN_LONG(num_idx);
1572 : }
1573 : }
1574 : }
1575 : } ZEND_HASH_FOREACH_END();
1576 8 : } else if (Z_TYPE_P(value) == IS_STRING) {
1577 8 : ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1578 8 : if (php_colopl_bc_fast_equal_check_string(value, entry)) {
1579 8 : if (behavior == 0) {
1580 4 : RETURN_TRUE;
1581 : } else {
1582 4 : if (str_idx) {
1583 4 : RETURN_STR_COPY(str_idx);
1584 : } else {
1585 0 : RETURN_LONG(num_idx);
1586 : }
1587 : }
1588 : }
1589 : } ZEND_HASH_FOREACH_END();
1590 : } else {
1591 0 : ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
1592 0 : if (php_colopl_bc_fast_equal_check_function(value, entry)) {
1593 0 : if (behavior == 0) {
1594 0 : RETURN_TRUE;
1595 : } else {
1596 0 : if (str_idx) {
1597 0 : RETURN_STR_COPY(str_idx);
1598 : } else {
1599 0 : RETURN_LONG(num_idx);
1600 : }
1601 : }
1602 : }
1603 : } ZEND_HASH_FOREACH_END();
1604 : }
1605 : }
1606 :
1607 0 : RETURN_FALSE;
1608 : }
1609 :
1610 : #define PHP_COLOPL_BC_ARRAY_CMP_FUNC_VARS \
1611 : zend_fcall_info old_user_compare_fci; \
1612 : zend_fcall_info_cache old_user_compare_fci_cache \
1613 :
1614 : #define PHP_COLOPL_BC_ARRAY_CMP_FUNC_BACKUP() \
1615 : old_user_compare_fci = COLOPL_BC_G(user_compare_fci); \
1616 : old_user_compare_fci_cache = COLOPL_BC_G(user_compare_fci_cache); \
1617 : COLOPL_BC_G(user_compare_fci_cache) = empty_fcall_info_cache; \
1618 :
1619 : #define PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE() \
1620 : zend_release_fcall_info_cache(&COLOPL_BC_G(user_compare_fci_cache)); \
1621 : COLOPL_BC_G(user_compare_fci) = old_user_compare_fci; \
1622 : COLOPL_BC_G(user_compare_fci_cache) = old_user_compare_fci_cache; \
1623 :
1624 120 : static inline void php_colopl_bc_report_incompatible_sort(void)
1625 : {
1626 120 : if (COLOPL_BC_G(php74_sort_mode) & COLOPL_BC_PHP74_SORT_MODE_LOG) {
1627 96 : php_log_err_with_severity("Incompatible sort detected", LOG_NOTICE);
1628 : }
1629 :
1630 120 : if (COLOPL_BC_G(php74_sort_mode) & COLOPL_BC_PHP74_SORT_MODE_DEPRECATED) {
1631 96 : php_error_docref(NULL, E_DEPRECATED, "Incompatible sort detected");
1632 : }
1633 120 : }
1634 :
1635 216 : static inline void php_colopl_bc_hash_sort(HashTable *ht, bucket_compare_func_t compare_func, bool renumber, bool compare_func_may_call_user_code)
1636 : {
1637 : (void) compare_func_may_call_user_code;
1638 :
1639 216 : zend_hash_sort_ex(ht, zend_sort, compare_func, renumber);
1640 216 : }
1641 :
1642 216 : static void legacy_hash_sort_fast(INTERNAL_FUNCTION_PARAMETERS, zval *array, bucket_compare_func_t compare_func, bool renumber, bool compare_func_may_call_user_code)
1643 : {
1644 216 : php_colopl_bc_hash_sort(Z_ARRVAL_P(array), compare_func, renumber, compare_func_may_call_user_code);
1645 216 : }
1646 :
1647 180 : static void legacy_hash_sort_slow(INTERNAL_FUNCTION_PARAMETERS, zval *array, bucket_compare_func_t compare_func, bool renumber, bool compare_func_may_call_user_code)
1648 : {
1649 : php_colopl_bc_snapshot_context snapshot_context;
1650 : php_colopl_bc_diagnostic_error_state error_state;
1651 : php_colopl_bc_user_sort_context user_sort_context, *old_user_sort_context;
1652 : zend_internal_function *fn;
1653 : zval legacy, native, original, user_sort_original;
1654 : HashTable seen_arrays;
1655 180 : bool have_native_snapshot, native_failed = false,
1656 180 : may_call_user_code = compare_func_may_call_user_code, track_user_sort = compare_func_may_call_user_code,
1657 180 : user_sort_incompatible = false;
1658 : char *fnname_ptr;
1659 :
1660 180 : if (COLOPL_BC_G(php74_sort_mode) <= COLOPL_BC_PHP74_SORT_MODE_SILENT) {
1661 0 : legacy_hash_sort_fast(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, compare_func, renumber, compare_func_may_call_user_code);
1662 0 : return;
1663 : }
1664 :
1665 180 : ZVAL_UNDEF(&native);
1666 180 : ZVAL_UNDEF(&user_sort_original);
1667 180 : ZVAL_DUP(&legacy, array);
1668 180 : php_colopl_bc_snapshot_context_init(&snapshot_context);
1669 :
1670 180 : if (!may_call_user_code) {
1671 112 : zend_hash_init(&seen_arrays, 8, NULL, NULL, 0);
1672 112 : may_call_user_code = php_colopl_bc_zval_contains_object(array, &seen_arrays);
1673 112 : zend_hash_destroy(&seen_arrays);
1674 : }
1675 :
1676 180 : if (!may_call_user_code) {
1677 108 : may_call_user_code = php_colopl_bc_zval_contains_child_array(array);
1678 : }
1679 :
1680 180 : if (!may_call_user_code) {
1681 100 : may_call_user_code = php_colopl_bc_zval_contains_recursive_array(array);
1682 : }
1683 :
1684 180 : have_native_snapshot = !may_call_user_code && php_colopl_bc_snapshot_zval(&native, array, &snapshot_context);
1685 :
1686 180 : if (track_user_sort) {
1687 68 : ZVAL_DUP(&user_sort_original, array);
1688 68 : SEPARATE_ARRAY(&user_sort_original);
1689 68 : php_colopl_bc_user_sort_context_init(&user_sort_context);
1690 68 : old_user_sort_context = COLOPL_BC_G(php74_user_sort_context);
1691 68 : COLOPL_BC_G(php74_user_sort_context) = &user_sort_context;
1692 : }
1693 :
1694 180 : legacy_hash_sort_fast(INTERNAL_FUNCTION_PARAM_PASSTHRU, &legacy, compare_func, renumber, compare_func_may_call_user_code);
1695 :
1696 180 : if (track_user_sort) {
1697 68 : COLOPL_BC_G(php74_user_sort_context) = old_user_sort_context;
1698 68 : if (!EG(exception)) {
1699 68 : user_sort_incompatible = php_colopl_bc_user_sort_context_detect_incompatible_order(&user_sort_context, &legacy, &user_sort_original, renumber);
1700 : }
1701 68 : php_colopl_bc_user_sort_context_destroy(&user_sort_context);
1702 68 : if (!Z_ISUNDEF(user_sort_original)) {
1703 68 : zval_ptr_dtor(&user_sort_original);
1704 : }
1705 : }
1706 :
1707 : /* Get native PHP bundled (internal) function ptr */
1708 180 : fnname_ptr = execute_data->func->internal_function.function_name->val;
1709 : /*
1710 : * Extracting last piece of namespaces, they're always identical a native function name
1711 : *
1712 : * e.g.)
1713 : * Colopl\ColoplBc\Php74\*****
1714 : * ^^^^^
1715 : */
1716 4140 : while (strchr(fnname_ptr, '\\')) {
1717 3960 : ++fnname_ptr;
1718 : }
1719 180 : fn = zend_hash_str_find_ptr(EG(function_table), fnname_ptr, strlen(fnname_ptr));
1720 180 : if (EXPECTED(fn != NULL) && have_native_snapshot && !EG(exception)) {
1721 100 : ZVAL_COPY_VALUE(&original, array);
1722 100 : ZVAL_COPY_VALUE(array, &native);
1723 :
1724 100 : php_colopl_bc_diagnostic_error_state_suppress(&error_state);
1725 100 : fn->handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
1726 100 : native_failed = php_colopl_bc_clear_diagnostic_exception();
1727 100 : php_colopl_bc_diagnostic_error_state_restore(&error_state);
1728 100 : ZVAL_COPY_VALUE(&native, array);
1729 100 : ZVAL_COPY_VALUE(array, &original);
1730 :
1731 100 : if (!native_failed && !php_colopl_bc_same_snapshot_value(&legacy, &native, &snapshot_context)) {
1732 72 : php_colopl_bc_report_incompatible_sort();
1733 : }
1734 : }
1735 :
1736 180 : if (user_sort_incompatible && !EG(exception)) {
1737 48 : php_colopl_bc_report_incompatible_sort();
1738 : }
1739 :
1740 180 : if (!Z_ISUNDEF(native)) {
1741 100 : zval_ptr_dtor(&native);
1742 : }
1743 :
1744 180 : php_colopl_bc_snapshot_context_destroy(&snapshot_context);
1745 :
1746 180 : zval_ptr_dtor(array);
1747 180 : ZVAL_ARR(array, Z_ARRVAL(legacy));
1748 : }
1749 :
1750 258 : PHP_INI_MH(OnUpdateSortMode)
1751 : {
1752 258 : if (OnUpdateLong(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) {
1753 0 : return FAILURE;
1754 : }
1755 :
1756 258 : if (COLOPL_BC_G(php74_sort_mode) <= 0) {
1757 142 : COLOPL_BC_G(php74_hash_sort_func) = legacy_hash_sort_fast;
1758 : } else {
1759 116 : COLOPL_BC_G(php74_hash_sort_func) = legacy_hash_sort_slow;
1760 : }
1761 :
1762 258 : return SUCCESS;
1763 : }
1764 :
1765 80 : static void php_colopl_bc_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, zend_bool renumber)
1766 : {
1767 : zval *array, arr;
1768 : PHP_COLOPL_BC_ARRAY_CMP_FUNC_VARS;
1769 :
1770 80 : PHP_COLOPL_BC_ARRAY_CMP_FUNC_BACKUP();
1771 :
1772 80 : ZEND_PARSE_PARAMETERS_START(2, 2)
1773 80 : Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
1774 80 : Z_PARAM_FUNC(COLOPL_BC_G(user_compare_fci), COLOPL_BC_G(user_compare_fci_cache))
1775 80 : ZEND_PARSE_PARAMETERS_END_EX( PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE(); return );
1776 :
1777 80 : if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
1778 0 : PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE();
1779 0 : RETURN_TRUE;
1780 : }
1781 :
1782 80 : if (COLOPL_BC_G(php74_sort_mode) <= COLOPL_BC_PHP74_SORT_MODE_SILENT) {
1783 12 : SEPARATE_ARRAY(array);
1784 12 : legacy_hash_sort_fast(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, compare_func, renumber, true);
1785 12 : PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE();
1786 12 : RETURN_TRUE;
1787 : }
1788 :
1789 : /* Copy array, so the diagnostic path can compare the legacy result. */
1790 68 : ZVAL_DUP(&arr, array);
1791 :
1792 68 : COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, &arr, compare_func, renumber, true);
1793 :
1794 68 : zval_ptr_dtor(array);
1795 68 : ZVAL_ARR(array, Z_ARRVAL(arr));
1796 :
1797 68 : PHP_COLOPL_BC_ARRAY_CMP_FUNC_RESTORE();
1798 68 : RETURN_TRUE;
1799 : }
1800 :
1801 20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_ksort)
1802 : {
1803 : zval *array;
1804 20 : zend_long sort_type = PHP_SORT_REGULAR;
1805 : bucket_compare_func_t cmp;
1806 :
1807 20 : ZEND_PARSE_PARAMETERS_START(1, 2)
1808 20 : Z_PARAM_ARRAY_EX(array, 0, 1)
1809 20 : Z_PARAM_OPTIONAL
1810 20 : Z_PARAM_LONG(sort_type)
1811 20 : ZEND_PARSE_PARAMETERS_END();
1812 :
1813 20 : cmp = php_colopl_bc_get_key_compare_func(sort_type, 0);
1814 :
1815 20 : COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0, false);
1816 :
1817 20 : RETURN_TRUE;
1818 : }
1819 :
1820 24 : PHP_FUNCTION(Colopl_ColoplBc_Php74_krsort)
1821 : {
1822 : zval *array;
1823 24 : zend_long sort_type = PHP_SORT_REGULAR;
1824 : bucket_compare_func_t cmp;
1825 :
1826 24 : ZEND_PARSE_PARAMETERS_START(1, 2)
1827 24 : Z_PARAM_ARRAY_EX(array, 0, 1)
1828 24 : Z_PARAM_OPTIONAL
1829 24 : Z_PARAM_LONG(sort_type)
1830 24 : ZEND_PARSE_PARAMETERS_END();
1831 :
1832 24 : cmp = php_colopl_bc_get_key_compare_func(sort_type, 1);
1833 :
1834 24 : COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0, false);
1835 :
1836 24 : RETURN_TRUE;
1837 : }
1838 :
1839 20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_asort)
1840 : {
1841 : zval *array;
1842 20 : zend_long sort_type = PHP_SORT_REGULAR;
1843 : bucket_compare_func_t cmp;
1844 :
1845 20 : ZEND_PARSE_PARAMETERS_START(1, 2)
1846 20 : Z_PARAM_ARRAY_EX(array, 0, 1)
1847 20 : Z_PARAM_OPTIONAL
1848 20 : Z_PARAM_LONG(sort_type)
1849 20 : ZEND_PARSE_PARAMETERS_END();
1850 :
1851 20 : cmp = php_colopl_bc_get_data_compare_func(sort_type, 0);
1852 :
1853 20 : COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0, false);
1854 :
1855 20 : RETURN_TRUE;
1856 : }
1857 :
1858 20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_arsort)
1859 : {
1860 : zval *array;
1861 20 : zend_long sort_type = PHP_SORT_REGULAR;
1862 : bucket_compare_func_t cmp;
1863 :
1864 20 : ZEND_PARSE_PARAMETERS_START(1, 2)
1865 20 : Z_PARAM_ARRAY_EX(array, 0, 1)
1866 20 : Z_PARAM_OPTIONAL
1867 20 : Z_PARAM_LONG(sort_type)
1868 20 : ZEND_PARSE_PARAMETERS_END();
1869 :
1870 20 : cmp = php_colopl_bc_get_data_compare_func(sort_type, 1);
1871 :
1872 20 : COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 0, false);
1873 :
1874 20 : RETURN_TRUE;
1875 : }
1876 :
1877 32 : PHP_FUNCTION(Colopl_ColoplBc_Php74_sort)
1878 : {
1879 : zval *array;
1880 32 : zend_long sort_type = PHP_SORT_REGULAR;
1881 : bucket_compare_func_t cmp;
1882 :
1883 32 : ZEND_PARSE_PARAMETERS_START(1, 2)
1884 32 : Z_PARAM_ARRAY_EX(array, 0, 1)
1885 32 : Z_PARAM_OPTIONAL
1886 32 : Z_PARAM_LONG(sort_type)
1887 32 : ZEND_PARSE_PARAMETERS_END();
1888 :
1889 32 : cmp = php_colopl_bc_get_data_compare_func(sort_type, 0);
1890 :
1891 32 : COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 1, false);
1892 :
1893 32 : RETURN_TRUE;
1894 : }
1895 :
1896 20 : PHP_FUNCTION(Colopl_ColoplBc_Php74_rsort)
1897 : {
1898 : zval *array;
1899 20 : zend_long sort_type = PHP_SORT_REGULAR;
1900 : bucket_compare_func_t cmp;
1901 :
1902 20 : ZEND_PARSE_PARAMETERS_START(1, 2)
1903 20 : Z_PARAM_ARRAY_EX(array, 0, 1)
1904 20 : Z_PARAM_OPTIONAL
1905 20 : Z_PARAM_LONG(sort_type)
1906 20 : ZEND_PARSE_PARAMETERS_END();
1907 :
1908 20 : cmp = php_colopl_bc_get_data_compare_func(sort_type, 1);
1909 :
1910 20 : COLOPL_BC_G(php74_hash_sort_func)(INTERNAL_FUNCTION_PARAM_PASSTHRU, array, cmp, 1, false);
1911 :
1912 20 : RETURN_TRUE;
1913 : }
1914 :
1915 32 : PHP_FUNCTION(Colopl_ColoplBc_Php74_usort)
1916 : {
1917 32 : php_colopl_bc_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_colopl_bc_array_user_compare, 1);
1918 32 : }
1919 :
1920 24 : PHP_FUNCTION(Colopl_ColoplBc_Php74_uasort)
1921 : {
1922 24 : php_colopl_bc_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_colopl_bc_array_user_compare, 0);
1923 24 : }
1924 :
1925 24 : PHP_FUNCTION(Colopl_ColoplBc_Php74_uksort)
1926 : {
1927 24 : php_colopl_bc_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_colopl_bc_array_user_key_compare, 0);
1928 24 : }
1929 :
1930 4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_in_array)
1931 : {
1932 4 : php_colopl_bc_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1933 4 : }
1934 :
1935 4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_search)
1936 : {
1937 4 : php_colopl_bc_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1938 4 : }
1939 :
1940 4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_keys)
1941 : {
1942 : zval *input, /* Input array */
1943 4 : *search_value = NULL, /* Value to search for */
1944 : *entry, /* An entry in the input array */
1945 : new_val; /* New value */
1946 4 : bool strict = 0; /* do strict comparison */
1947 : zend_ulong num_idx;
1948 : zend_string *str_idx;
1949 : zend_array *arrval;
1950 : zend_ulong elem_count;
1951 :
1952 4 : ZEND_PARSE_PARAMETERS_START(1, 3)
1953 4 : Z_PARAM_ARRAY(input)
1954 4 : Z_PARAM_OPTIONAL
1955 4 : Z_PARAM_ZVAL(search_value)
1956 4 : Z_PARAM_BOOL(strict)
1957 4 : ZEND_PARSE_PARAMETERS_END();
1958 4 : arrval = Z_ARRVAL_P(input);
1959 4 : elem_count = zend_hash_num_elements(arrval);
1960 :
1961 : /* Base case: empty input */
1962 4 : if (!elem_count) {
1963 0 : RETURN_COPY(input);
1964 : }
1965 :
1966 : /* Initialize return array */
1967 4 : if (search_value != NULL) {
1968 4 : array_init(return_value);
1969 :
1970 4 : if (strict) {
1971 0 : ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
1972 0 : ZVAL_DEREF(entry);
1973 0 : if (fast_is_identical_function(search_value, entry)) {
1974 0 : if (str_idx) {
1975 0 : ZVAL_STR_COPY(&new_val, str_idx);
1976 : } else {
1977 0 : ZVAL_LONG(&new_val, num_idx);
1978 : }
1979 0 : zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
1980 : }
1981 : } ZEND_HASH_FOREACH_END();
1982 : } else {
1983 12 : ZEND_HASH_FOREACH_KEY_VAL(arrval, num_idx, str_idx, entry) {
1984 8 : if (php_colopl_bc_fast_equal_check_function(search_value, entry)) {
1985 8 : if (str_idx) {
1986 8 : ZVAL_STR_COPY(&new_val, str_idx);
1987 : } else {
1988 0 : ZVAL_LONG(&new_val, num_idx);
1989 : }
1990 8 : zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
1991 : }
1992 : } ZEND_HASH_FOREACH_END();
1993 : }
1994 : } else {
1995 0 : array_init_size(return_value, elem_count);
1996 0 : zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
1997 0 : ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
1998 0 : if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
1999 : /* Optimistic case: range(0..n-1) for vector-like packed array */
2000 0 : zend_ulong lval = 0;
2001 :
2002 0 : for (; lval < elem_count; ++lval) {
2003 0 : ZEND_HASH_FILL_SET_LONG(lval);
2004 0 : ZEND_HASH_FILL_NEXT();
2005 : }
2006 : } else {
2007 : /* Go through input array and add keys to the return array */
2008 0 : ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
2009 0 : if (str_idx) {
2010 0 : ZEND_HASH_FILL_SET_STR_COPY(str_idx);
2011 : } else {
2012 0 : ZEND_HASH_FILL_SET_LONG(num_idx);
2013 : }
2014 0 : ZEND_HASH_FILL_NEXT();
2015 : } ZEND_HASH_FOREACH_END();
2016 : }
2017 0 : } ZEND_HASH_FILL_END();
2018 : }
2019 : }
2020 :
2021 : #if PHP_VERSION_ID < 80200
2022 : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_multisort)
2023 : {
2024 : zval* args;
2025 : zval** arrays;
2026 : Bucket** indirect;
2027 : uint32_t idx;
2028 : Bucket* p;
2029 : HashTable* hash;
2030 : int argc;
2031 : int array_size;
2032 : int num_arrays = 0;
2033 : int parse_state[COLOPL_BC_MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
2034 : int sort_order = PHP_SORT_ASC;
2035 : int sort_type = PHP_SORT_REGULAR;
2036 : int i, k, n;
2037 : bucket_compare_func_t *func;
2038 :
2039 : ZEND_PARSE_PARAMETERS_START(1, -1)
2040 : Z_PARAM_VARIADIC('+', args, argc)
2041 : ZEND_PARSE_PARAMETERS_END();
2042 :
2043 : /* Allocate space for storing pointers to input arrays and sort flags. */
2044 : arrays = (zval **)ecalloc(argc, sizeof(zval *));
2045 : for (i = 0; i < COLOPL_BC_MULTISORT_LAST; i++) {
2046 : parse_state[i] = 0;
2047 : }
2048 : func = COLOPL_BC_G(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
2049 :
2050 : /* Here we go through the input arguments and parse them. Each one can
2051 : * be either an array or a sort flag which follows an array. If not
2052 : * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
2053 : * accordingly. There can't be two sort flags of the same type after an
2054 : * array, and the very first argument has to be an array. */
2055 : for (i = 0; i < argc; i++) {
2056 : zval *arg = &args[i];
2057 :
2058 : ZVAL_DEREF(arg);
2059 : if (Z_TYPE_P(arg) == IS_ARRAY) {
2060 : SEPARATE_ARRAY(arg);
2061 : /* We see the next array, so we update the sort flags of
2062 : * the previous array and reset the sort flags. */
2063 : if (i > 0) {
2064 : COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
2065 : sort_order = PHP_SORT_ASC;
2066 : sort_type = PHP_SORT_REGULAR;
2067 : }
2068 : arrays[num_arrays++] = arg;
2069 :
2070 : /* Next one may be an array or a list of sort flags. */
2071 : for (k = 0; k < COLOPL_BC_MULTISORT_LAST; k++) {
2072 : parse_state[k] = 1;
2073 : }
2074 : } else if (Z_TYPE_P(arg) == IS_LONG) {
2075 : switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
2076 : case PHP_SORT_ASC:
2077 : case PHP_SORT_DESC:
2078 : /* flag allowed here */
2079 : if (parse_state[COLOPL_BC_MULTISORT_ORDER] == 1) {
2080 : /* Save the flag and make sure then next arg is not the current flag. */
2081 : sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
2082 : parse_state[COLOPL_BC_MULTISORT_ORDER] = 0;
2083 : } else {
2084 : zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
2085 : COLOPL_BC_MULTISORT_ABORT;
2086 : }
2087 : break;
2088 :
2089 : case PHP_SORT_REGULAR:
2090 : case PHP_SORT_NUMERIC:
2091 : case PHP_SORT_STRING:
2092 : case PHP_SORT_NATURAL:
2093 : case PHP_SORT_LOCALE_STRING:
2094 : /* flag allowed here */
2095 : if (parse_state[COLOPL_BC_MULTISORT_TYPE] == 1) {
2096 : /* Save the flag and make sure then next arg is not the current flag. */
2097 : sort_type = (int)Z_LVAL_P(arg);
2098 : parse_state[COLOPL_BC_MULTISORT_TYPE] = 0;
2099 : } else {
2100 : zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
2101 : COLOPL_BC_MULTISORT_ABORT;
2102 : }
2103 : break;
2104 :
2105 : default:
2106 : zend_argument_value_error(i + 1, "must be a valid sort flag");
2107 : COLOPL_BC_MULTISORT_ABORT;
2108 : break;
2109 :
2110 : }
2111 : } else {
2112 : zend_argument_type_error(i + 1, "must be an array or a sort flag");
2113 : COLOPL_BC_MULTISORT_ABORT;
2114 : }
2115 : }
2116 : /* Take care of the last array sort flags. */
2117 : COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
2118 :
2119 : /* Make sure the arrays are of the same size. */
2120 : array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
2121 : for (i = 0; i < num_arrays; i++) {
2122 : if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
2123 : zend_value_error("Array sizes are inconsistent");
2124 : COLOPL_BC_MULTISORT_ABORT;
2125 : }
2126 : }
2127 :
2128 : /* If all arrays are empty we don't need to do anything. */
2129 : if (array_size < 1) {
2130 : efree(func);
2131 : efree(arrays);
2132 : RETURN_TRUE;
2133 : }
2134 :
2135 : /* Create the indirection array. This array is of size MxN, where
2136 : * M is the number of entries in each input array and N is the number
2137 : * of the input arrays + 1. The last column is NULL to indicate the end
2138 : * of the row. */
2139 : indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
2140 : for (i = 0; i < array_size; i++) {
2141 : indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
2142 : }
2143 : for (i = 0; i < num_arrays; i++) {
2144 : k = 0;
2145 : for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
2146 : p = Z_ARRVAL_P(arrays[i])->arData + idx;
2147 : if (Z_TYPE(p->val) == IS_UNDEF) continue;
2148 : indirect[k][i] = *p;
2149 : k++;
2150 : }
2151 : }
2152 : for (k = 0; k < array_size; k++) {
2153 : ZVAL_UNDEF(&indirect[k][num_arrays].val);
2154 : }
2155 :
2156 : /* Do the actual sort magic - bada-bim, bada-boom. */
2157 : zend_sort(indirect, array_size, sizeof(Bucket *), php_colopl_bc_multisort_compare, (swap_func_t)colopl_bc_array_bucket_p_swap);
2158 :
2159 : /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
2160 : for (i = 0; i < num_arrays; i++) {
2161 : int repack;
2162 :
2163 : hash = Z_ARRVAL_P(arrays[i]);
2164 : hash->nNumUsed = array_size;
2165 : hash->nInternalPointer = 0;
2166 : repack = !(HT_FLAGS(hash) & HASH_FLAG_PACKED);
2167 :
2168 : for (n = 0, k = 0; k < array_size; k++) {
2169 : hash->arData[k] = indirect[k][i];
2170 : if (hash->arData[k].key == NULL) {
2171 : hash->arData[k].h = n++;
2172 : } else {
2173 : repack = 0;
2174 : }
2175 : }
2176 : hash->nNextFreeElement = array_size;
2177 : if (repack) {
2178 : zend_hash_to_packed(hash);
2179 : } else if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
2180 : zend_hash_rehash(hash);
2181 : }
2182 : }
2183 :
2184 : /* Clean up. */
2185 : for (i = 0; i < array_size; i++) {
2186 : efree(indirect[i]);
2187 : }
2188 : efree(indirect);
2189 : efree(func);
2190 : efree(arrays);
2191 : RETURN_TRUE;
2192 : }
2193 : #else
2194 4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_array_multisort)
2195 : {
2196 : zval* args;
2197 : zval** arrays;
2198 : Bucket** indirect;
2199 : uint32_t idx;
2200 : HashTable* hash;
2201 : int argc;
2202 : int array_size;
2203 4 : int num_arrays = 0;
2204 : int parse_state[COLOPL_BC_MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
2205 4 : int sort_order = PHP_SORT_ASC;
2206 4 : int sort_type = PHP_SORT_REGULAR;
2207 : int i, k, n;
2208 : bucket_compare_func_t *func;
2209 :
2210 4 : ZEND_PARSE_PARAMETERS_START(1, -1)
2211 4 : Z_PARAM_VARIADIC('+', args, argc)
2212 4 : ZEND_PARSE_PARAMETERS_END();
2213 :
2214 : /* Allocate space for storing pointers to input arrays and sort flags. */
2215 4 : arrays = (zval **)ecalloc(argc, sizeof(zval *));
2216 12 : for (i = 0; i < COLOPL_BC_MULTISORT_LAST; i++) {
2217 8 : parse_state[i] = 0;
2218 : }
2219 4 : func = COLOPL_BC_G(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
2220 :
2221 : /* Here we go through the input arguments and parse them. Each one can
2222 : * be either an array or a sort flag which follows an array. If not
2223 : * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
2224 : * accordingly. There can't be two sort flags of the same type after an
2225 : * array, and the very first argument has to be an array. */
2226 12 : for (i = 0; i < argc; i++) {
2227 8 : zval *arg = &args[i];
2228 :
2229 8 : ZVAL_DEREF(arg);
2230 8 : if (Z_TYPE_P(arg) == IS_ARRAY) {
2231 8 : SEPARATE_ARRAY(arg);
2232 : /* We see the next array, so we update the sort flags of
2233 : * the previous array and reset the sort flags. */
2234 8 : if (i > 0) {
2235 4 : COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
2236 4 : sort_order = PHP_SORT_ASC;
2237 4 : sort_type = PHP_SORT_REGULAR;
2238 : }
2239 8 : arrays[num_arrays++] = arg;
2240 :
2241 : /* Next one may be an array or a list of sort flags. */
2242 24 : for (k = 0; k < COLOPL_BC_MULTISORT_LAST; k++) {
2243 16 : parse_state[k] = 1;
2244 : }
2245 0 : } else if (Z_TYPE_P(arg) == IS_LONG) {
2246 0 : switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
2247 0 : case PHP_SORT_ASC:
2248 : case PHP_SORT_DESC:
2249 : /* flag allowed here */
2250 0 : if (parse_state[COLOPL_BC_MULTISORT_ORDER] == 1) {
2251 : /* Save the flag and make sure then next arg is not the current flag. */
2252 0 : sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
2253 0 : parse_state[COLOPL_BC_MULTISORT_ORDER] = 0;
2254 : } else {
2255 0 : zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
2256 0 : COLOPL_BC_MULTISORT_ABORT;
2257 : }
2258 0 : break;
2259 :
2260 0 : case PHP_SORT_REGULAR:
2261 : case PHP_SORT_NUMERIC:
2262 : case PHP_SORT_STRING:
2263 : case PHP_SORT_NATURAL:
2264 : case PHP_SORT_LOCALE_STRING:
2265 : /* flag allowed here */
2266 0 : if (parse_state[COLOPL_BC_MULTISORT_TYPE] == 1) {
2267 : /* Save the flag and make sure then next arg is not the current flag. */
2268 0 : sort_type = (int)Z_LVAL_P(arg);
2269 0 : parse_state[COLOPL_BC_MULTISORT_TYPE] = 0;
2270 : } else {
2271 0 : zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
2272 0 : COLOPL_BC_MULTISORT_ABORT;
2273 : }
2274 0 : break;
2275 :
2276 0 : default:
2277 0 : zend_argument_value_error(i + 1, "must be a valid sort flag");
2278 0 : COLOPL_BC_MULTISORT_ABORT;
2279 : break;
2280 :
2281 : }
2282 : } else {
2283 0 : zend_argument_type_error(i + 1, "must be an array or a sort flag");
2284 0 : COLOPL_BC_MULTISORT_ABORT;
2285 : }
2286 : }
2287 : /* Take care of the last array sort flags. */
2288 4 : COLOPL_BC_G(multisort_func)[num_arrays - 1] = php_colopl_bc_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
2289 :
2290 : /* Make sure the arrays are of the same size. */
2291 4 : array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
2292 12 : for (i = 0; i < num_arrays; i++) {
2293 8 : if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
2294 0 : zend_value_error("Array sizes are inconsistent");
2295 0 : COLOPL_BC_MULTISORT_ABORT;
2296 : }
2297 : }
2298 :
2299 : /* If all arrays are empty we don't need to do anything. */
2300 4 : if (array_size < 1) {
2301 0 : efree(func);
2302 0 : efree(arrays);
2303 0 : RETURN_TRUE;
2304 : }
2305 :
2306 : /* Create the indirection array. This array is of size MxN, where
2307 : * M is the number of entries in each input array and N is the number
2308 : * of the input arrays + 1. The last column is UNDEF to indicate the end
2309 : * of the row. It also stores the original position for stable sorting. */
2310 4 : indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
2311 404 : for (i = 0; i < array_size; i++) {
2312 400 : indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
2313 : }
2314 12 : for (i = 0; i < num_arrays; i++) {
2315 8 : k = 0;
2316 8 : if (HT_IS_PACKED(Z_ARRVAL_P(arrays[i]))) {
2317 8 : zval *zv = Z_ARRVAL_P(arrays[i])->arPacked;
2318 808 : for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, zv++) {
2319 800 : if (Z_TYPE_P(zv) == IS_UNDEF) continue;
2320 800 : ZVAL_COPY_VALUE(&indirect[k][i].val, zv);
2321 800 : indirect[k][i].h = idx;
2322 800 : indirect[k][i].key = NULL;
2323 800 : k++;
2324 : }
2325 : } else {
2326 0 : Bucket *p = Z_ARRVAL_P(arrays[i])->arData;
2327 0 : for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++, p++) {
2328 0 : if (Z_TYPE(p->val) == IS_UNDEF) continue;
2329 0 : indirect[k][i] = *p;
2330 0 : k++;
2331 : }
2332 : }
2333 : }
2334 404 : for (k = 0; k < array_size; k++) {
2335 400 : ZVAL_UNDEF(&indirect[k][num_arrays].val);
2336 400 : Z_EXTRA_P(&indirect[k][num_arrays].val) = k;
2337 : }
2338 :
2339 : /* Do the actual sort magic - bada-bim, bada-boom. */
2340 4 : zend_sort(indirect, array_size, sizeof(Bucket *), php_colopl_bc_multisort_compare, (swap_func_t)colopl_bc_array_bucket_p_swap);
2341 :
2342 : /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
2343 12 : for (i = 0; i < num_arrays; i++) {
2344 8 : hash = Z_ARRVAL_P(arrays[i]);
2345 8 : hash->nNumUsed = array_size;
2346 8 : hash->nNextFreeElement = array_size;
2347 8 : hash->nInternalPointer = 0;
2348 8 : if (HT_IS_PACKED(hash)) {
2349 808 : for (k = 0; k < array_size; k++) {
2350 800 : ZVAL_COPY_VALUE(&hash->arPacked[k], &indirect[k][i].val);
2351 : }
2352 : } else {
2353 0 : int repack = 1;
2354 :
2355 0 : for (n = 0, k = 0; k < array_size; k++) {
2356 0 : hash->arData[k] = indirect[k][i];
2357 0 : if (hash->arData[k].key == NULL) {
2358 0 : hash->arData[k].h = n++;
2359 : } else {
2360 0 : repack = 0;
2361 : }
2362 : }
2363 0 : if (repack) {
2364 0 : zend_hash_to_packed(hash);
2365 : }
2366 : }
2367 : }
2368 :
2369 : /* Clean up. */
2370 404 : for (i = 0; i < array_size; i++) {
2371 400 : efree(indirect[i]);
2372 : }
2373 4 : efree(indirect);
2374 4 : efree(func);
2375 4 : efree(arrays);
2376 4 : RETURN_TRUE;
2377 : }
2378 : #endif
2379 :
2380 : /* BinaryOps */
2381 :
2382 1184 : PHP_FUNCTION(Colopl_ColoplBc_Php74_eq)
2383 : {
2384 : zval *op1, *op2;
2385 :
2386 1184 : ZEND_PARSE_PARAMETERS_START(2, 2)
2387 1184 : Z_PARAM_ZVAL(op1)
2388 1184 : Z_PARAM_ZVAL(op2)
2389 1184 : ZEND_PARSE_PARAMETERS_END();
2390 :
2391 1184 : RETURN_BOOL(php_colopl_bc_compare(op1, op2) == 0);
2392 : }
2393 :
2394 4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_neq)
2395 : {
2396 : zval *op1, *op2;
2397 :
2398 4 : ZEND_PARSE_PARAMETERS_START(2, 2)
2399 4 : Z_PARAM_ZVAL(op1)
2400 4 : Z_PARAM_ZVAL(op2)
2401 4 : ZEND_PARSE_PARAMETERS_END();
2402 :
2403 4 : RETURN_BOOL(php_colopl_bc_compare(op1, op2) != 0);
2404 : }
2405 :
2406 1160 : PHP_FUNCTION(Colopl_ColoplBc_Php74_lt)
2407 : {
2408 : zval *op1, *op2;
2409 :
2410 1160 : ZEND_PARSE_PARAMETERS_START(2, 2)
2411 1160 : Z_PARAM_ZVAL(op1)
2412 1160 : Z_PARAM_ZVAL(op2)
2413 1160 : ZEND_PARSE_PARAMETERS_END();
2414 :
2415 1160 : RETURN_BOOL(php_colopl_bc_compare(op1, op2) < 0);
2416 : }
2417 :
2418 1160 : PHP_FUNCTION(Colopl_ColoplBc_Php74_lte)
2419 : {
2420 : zval *op1, *op2;
2421 :
2422 1160 : ZEND_PARSE_PARAMETERS_START(2, 2)
2423 1160 : Z_PARAM_ZVAL(op1)
2424 1160 : Z_PARAM_ZVAL(op2)
2425 1160 : ZEND_PARSE_PARAMETERS_END();
2426 :
2427 1160 : RETURN_BOOL(php_colopl_bc_compare(op1, op2) <= 0);
2428 : }
2429 :
2430 1160 : PHP_FUNCTION(Colopl_ColoplBc_Php74_gt)
2431 : {
2432 : zval *op1, *op2;
2433 :
2434 1160 : ZEND_PARSE_PARAMETERS_START(2, 2)
2435 1160 : Z_PARAM_ZVAL(op1)
2436 1160 : Z_PARAM_ZVAL(op2)
2437 1160 : ZEND_PARSE_PARAMETERS_END();
2438 :
2439 1160 : RETURN_BOOL(php_colopl_bc_compare(op2, op1) < 0);
2440 : }
2441 :
2442 1160 : PHP_FUNCTION(Colopl_ColoplBc_Php74_gte)
2443 : {
2444 : zval *op1, *op2;
2445 :
2446 1160 : ZEND_PARSE_PARAMETERS_START(2, 2)
2447 1160 : Z_PARAM_ZVAL(op1)
2448 1160 : Z_PARAM_ZVAL(op2)
2449 1160 : ZEND_PARSE_PARAMETERS_END();
2450 :
2451 1160 : RETURN_BOOL(php_colopl_bc_compare(op2, op1) <= 0);
2452 : }
2453 :
2454 4 : PHP_FUNCTION(Colopl_ColoplBc_Php74_spaceship)
2455 : {
2456 : zval *op1, *op2;
2457 :
2458 4 : ZEND_PARSE_PARAMETERS_START(2, 2)
2459 4 : Z_PARAM_ZVAL(op1)
2460 4 : Z_PARAM_ZVAL(op2)
2461 4 : ZEND_PARSE_PARAMETERS_END();
2462 :
2463 4 : RETURN_LONG(php_colopl_bc_compare(op1, op2));
2464 : }
|