Skip to content

Commit b02b812

Browse files
committed
Comparison cleanup:
- introduce zend_compare() that returns -1,0,1 dirctly (without intermediate zval) - remove compare_objects() object handler, and keep only compare() handler
1 parent e210061 commit b02b812

24 files changed

+221
-258
lines changed

UPGRADING.INTERNALS

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ PHP 8.0 INTERNALS UPGRADE NOTES
1010
g. zend_free_op type and should_free argument of zend_get_zval_ptr()
1111
h. zend_value_error()
1212
i. get_closure() object handler
13+
j. compare_objects() and compare() object handlers
1314

1415
2. Build system changes
1516
a. Abstract
@@ -76,6 +77,10 @@ PHP 8.0 INTERNALS UPGRADE NOTES
7677
`check_only`. If it is true, the handler is called to check whether the
7778
object is callable; in this case the handler should not throw an exception.
7879

80+
j. compare_objects() handler was removed. Extensions should use compare() object
81+
handler instead and check if both arguments are objects and have the same
82+
compare hanldler, using ZEND_COMPARE_OBJECTS_FALLBACK() macro.
83+
7984
========================
8085
2. Build system changes
8186
========================

Zend/zend_closures.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,10 @@ static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object
355355
}
356356
/* }}} */
357357

358-
static int zend_closure_compare_objects(zval *o1, zval *o2) /* {{{ */
358+
static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
359359
{
360-
return (Z_OBJ_P(o1) != Z_OBJ_P(o2));
360+
ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
361+
return Z_OBJ_P(o1) != Z_OBJ_P(o2);
361362
}
362363
/* }}} */
363364

@@ -639,7 +640,7 @@ void zend_register_closure_ce(void) /* {{{ */
639640
closure_handlers.get_property_ptr_ptr = zend_closure_get_property_ptr_ptr;
640641
closure_handlers.has_property = zend_closure_has_property;
641642
closure_handlers.unset_property = zend_closure_unset_property;
642-
closure_handlers.compare_objects = zend_closure_compare_objects;
643+
closure_handlers.compare = zend_closure_compare;
643644
closure_handlers.clone_obj = zend_closure_clone;
644645
closure_handlers.get_debug_info = zend_closure_get_debug_info;
645646
closure_handlers.get_closure = zend_closure_get_closure;

Zend/zend_iterators.c

-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ static const zend_object_handlers iterator_object_handlers = {
4444
NULL, /* method get */
4545
NULL, /* get ctor */
4646
NULL, /* get class name */
47-
NULL, /* compare */
4847
NULL, /* cast */
4948
NULL, /* count */
5049
NULL, /* get_debug_info */

Zend/zend_object_handlers.c

+32-9
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,33 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
15461546
{
15471547
zend_object *zobj1, *zobj2;
15481548

1549+
if (Z_TYPE_P(o1) != Z_TYPE_P(o2)) {
1550+
/* Object and non-object */
1551+
zval casted;
1552+
if (Z_TYPE_P(o1) == IS_OBJECT) {
1553+
ZEND_ASSERT(Z_TYPE_P(o2) != IS_OBJECT);
1554+
if (Z_OBJ_HT_P(o1)->cast_object) {
1555+
if (Z_OBJ_HT_P(o1)->cast_object(Z_OBJ_P(o1), &casted, ((Z_TYPE_P(o2) == IS_FALSE || Z_TYPE_P(o2) == IS_TRUE) ? _IS_BOOL : Z_TYPE_P(o2))) == FAILURE) {
1556+
return 1;
1557+
}
1558+
int ret = zend_compare(&casted, o2);
1559+
zval_ptr_dtor(&casted);
1560+
return ret;
1561+
}
1562+
} else {
1563+
ZEND_ASSERT(Z_TYPE_P(o2) == IS_OBJECT);
1564+
if (Z_OBJ_HT_P(o2)->cast_object) {
1565+
if (Z_OBJ_HT_P(o2)->cast_object(Z_OBJ_P(o2), &casted, ((Z_TYPE_P(o1) == IS_FALSE || Z_TYPE_P(o1) == IS_TRUE) ? _IS_BOOL : Z_TYPE_P(o1))) == FAILURE) {
1566+
return -1;
1567+
}
1568+
int ret = zend_compare(o1, &casted);
1569+
zval_ptr_dtor(&casted);
1570+
return ret;
1571+
}
1572+
}
1573+
return 1;
1574+
}
1575+
15491576
zobj1 = Z_OBJ_P(o1);
15501577
zobj2 = Z_OBJ_P(o2);
15511578

@@ -1582,15 +1609,12 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
15821609

15831610
if (Z_TYPE_P(p1) != IS_UNDEF) {
15841611
if (Z_TYPE_P(p2) != IS_UNDEF) {
1585-
zval result;
1612+
int ret;
15861613

1587-
if (compare_function(&result, p1, p2)==FAILURE) {
1588-
Z_UNPROTECT_RECURSION_P(o1);
1589-
return 1;
1590-
}
1591-
if (Z_LVAL(result) != 0) {
1614+
ret = zend_compare(p1, p2);
1615+
if (ret != 0) {
15921616
Z_UNPROTECT_RECURSION_P(o1);
1593-
return Z_LVAL(result);
1617+
return ret;
15941618
}
15951619
} else {
15961620
Z_UNPROTECT_RECURSION_P(o1);
@@ -1850,13 +1874,12 @@ ZEND_API const zend_object_handlers std_object_handlers = {
18501874
zend_std_get_method, /* get_method */
18511875
zend_std_get_constructor, /* get_constructor */
18521876
zend_std_get_class_name, /* get_class_name */
1853-
zend_std_compare_objects, /* compare_objects */
18541877
zend_std_cast_object_tostring, /* cast_object */
18551878
NULL, /* count_elements */
18561879
zend_std_get_debug_info, /* get_debug_info */
18571880
zend_std_get_closure, /* get_closure */
18581881
zend_std_get_gc, /* get_gc */
18591882
NULL, /* do_operation */
1860-
NULL, /* compare */
1883+
zend_std_compare_objects, /* compare */
18611884
NULL, /* get_properties_for */
18621885
};

Zend/zend_object_handlers.h

+11-3
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ typedef zend_object* (*zend_object_clone_obj_t)(zend_object *object);
124124
typedef zend_string *(*zend_object_get_class_name_t)(const zend_object *object);
125125

126126
typedef int (*zend_object_compare_t)(zval *object1, zval *object2);
127-
typedef int (*zend_object_compare_zvals_t)(zval *result, zval *op1, zval *op2);
128127

129128
/* Cast an object to some other type.
130129
* readobj and retval must point to distinct zvals.
@@ -161,14 +160,13 @@ struct _zend_object_handlers {
161160
zend_object_get_method_t get_method; /* required */
162161
zend_object_get_constructor_t get_constructor; /* required */
163162
zend_object_get_class_name_t get_class_name; /* required */
164-
zend_object_compare_t compare_objects; /* optional */
165163
zend_object_cast_t cast_object; /* optional */
166164
zend_object_count_elements_t count_elements; /* optional */
167165
zend_object_get_debug_info_t get_debug_info; /* optional */
168166
zend_object_get_closure_t get_closure; /* optional */
169167
zend_object_get_gc_t get_gc; /* required */
170168
zend_object_do_operation_t do_operation; /* optional */
171-
zend_object_compare_zvals_t compare; /* optional */
169+
zend_object_compare_t compare; /* required */
172170
zend_object_get_properties_for_t get_properties_for; /* optional */
173171
};
174172

@@ -241,6 +239,16 @@ ZEND_API HashTable *zend_get_properties_for(zval *obj, zend_prop_purpose purpose
241239
} \
242240
} while (0)
243241

242+
/* Fallback to default comparison implementation if the arguments aren't both objects
243+
* and have the same compare() handler. You'll likely want to use this unless you
244+
* explicitly wish to support comparisons between objects and non-objects. */
245+
#define ZEND_COMPARE_OBJECTS_FALLBACK(op1, op2) \
246+
if (Z_TYPE_P(op1) != IS_OBJECT || \
247+
Z_TYPE_P(op2) != IS_OBJECT || \
248+
Z_OBJ_HT_P(op1)->compare != Z_OBJ_HT_P(op2)->compare) { \
249+
return zend_std_compare_objects(op1, op2); \
250+
}
251+
244252
END_EXTERN_C()
245253

246254
#endif

0 commit comments

Comments
 (0)