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