Skip to content

Commit 8fd7f02

Browse files
committed
Make cast_object handler required
Avoid subtle differences in behavior depending on whether the handler is absent or returns FAILURE. If you previously set cast_object to NULL, create a handler that always returns FAILURE instead.
1 parent 36935e4 commit 8fd7f02

File tree

8 files changed

+73
-86
lines changed

8 files changed

+73
-86
lines changed

UPGRADING.INTERNALS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ PHP 8.0 INTERNALS UPGRADE NOTES
1515
l. Some VM instructions switched to IS_TMP_VAR result instead of IS_VAR
1616
m. All internal functions must have arginfo
1717
n. zend_hash_sort compare function signature change
18+
o. cast_object() object handler is now required
1819

1920
2. Build system changes
2021
a. Abstract
@@ -110,6 +111,9 @@ PHP 8.0 INTERNALS UPGRADE NOTES
110111

111112
Previously compare_func_t was used, which accepted void pointers.
112113

114+
o. The cast_object() handler is now required, i.e. must be non-null. You can
115+
indicate that casting is not supported by always returning FAILURE.
116+
113117
========================
114118
2. Build system changes
115119
========================

Zend/zend_API.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -472,15 +472,12 @@ ZEND_API int ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest
472472
*dest = Z_STR_P(arg);
473473
} else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
474474
zend_object *zobj = Z_OBJ_P(arg);
475-
476-
if (Z_OBJ_HANDLER_P(arg, cast_object)) {
477-
zval obj;
478-
if (zobj->handlers->cast_object(zobj, &obj, IS_STRING) == SUCCESS) {
479-
OBJ_RELEASE(zobj);
480-
ZVAL_COPY_VALUE(arg, &obj);
481-
*dest = Z_STR_P(arg);
482-
return 1;
483-
}
475+
zval obj;
476+
if (zobj->handlers->cast_object(zobj, &obj, IS_STRING) == SUCCESS) {
477+
OBJ_RELEASE(zobj);
478+
ZVAL_COPY_VALUE(arg, &obj);
479+
*dest = Z_STR_P(arg);
480+
return 1;
484481
}
485482
return 0;
486483
} else {

Zend/zend_builtin_functions.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -664,11 +664,9 @@ ZEND_FUNCTION(define)
664664
}
665665
break;
666666
case IS_OBJECT:
667-
if (Z_OBJ_HT_P(val)->cast_object) {
668-
if (Z_OBJ_HT_P(val)->cast_object(Z_OBJ_P(val), &val_free, IS_STRING) == SUCCESS) {
669-
val = &val_free;
670-
break;
671-
}
667+
if (Z_OBJ_HT_P(val)->cast_object(Z_OBJ_P(val), &val_free, IS_STRING) == SUCCESS) {
668+
val = &val_free;
669+
break;
672670
}
673671
/* no break */
674672
default:

Zend/zend_object_handlers.c

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,50 +1572,46 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
15721572
zval casted;
15731573
if (Z_TYPE_P(o1) == IS_OBJECT) {
15741574
ZEND_ASSERT(Z_TYPE_P(o2) != IS_OBJECT);
1575-
if (Z_OBJ_HT_P(o1)->cast_object) {
1576-
zend_uchar target_type = (Z_TYPE_P(o2) == IS_FALSE || Z_TYPE_P(o2) == IS_TRUE)
1577-
? _IS_BOOL : Z_TYPE_P(o2);
1578-
if (Z_OBJ_HT_P(o1)->cast_object(Z_OBJ_P(o1), &casted, target_type) == FAILURE) {
1579-
// TODO: Less crazy.
1580-
if (target_type == IS_LONG || target_type == IS_DOUBLE) {
1581-
zend_error(E_NOTICE, "Object of class %s could not be converted to %s",
1582-
ZSTR_VAL(Z_OBJCE_P(o1)->name), zend_get_type_by_const(target_type));
1583-
if (target_type == IS_LONG) {
1584-
ZVAL_LONG(&casted, 1);
1585-
} else {
1586-
ZVAL_DOUBLE(&casted, 1.0);
1587-
}
1575+
zend_uchar target_type = (Z_TYPE_P(o2) == IS_FALSE || Z_TYPE_P(o2) == IS_TRUE)
1576+
? _IS_BOOL : Z_TYPE_P(o2);
1577+
if (Z_OBJ_HT_P(o1)->cast_object(Z_OBJ_P(o1), &casted, target_type) == FAILURE) {
1578+
// TODO: Less crazy.
1579+
if (target_type == IS_LONG || target_type == IS_DOUBLE) {
1580+
zend_error(E_NOTICE, "Object of class %s could not be converted to %s",
1581+
ZSTR_VAL(Z_OBJCE_P(o1)->name), zend_get_type_by_const(target_type));
1582+
if (target_type == IS_LONG) {
1583+
ZVAL_LONG(&casted, 1);
15881584
} else {
1589-
return 1;
1585+
ZVAL_DOUBLE(&casted, 1.0);
15901586
}
1587+
} else {
1588+
return 1;
15911589
}
1592-
int ret = zend_compare(&casted, o2);
1593-
zval_ptr_dtor(&casted);
1594-
return ret;
15951590
}
1591+
int ret = zend_compare(&casted, o2);
1592+
zval_ptr_dtor(&casted);
1593+
return ret;
15961594
} else {
15971595
ZEND_ASSERT(Z_TYPE_P(o2) == IS_OBJECT);
1598-
if (Z_OBJ_HT_P(o2)->cast_object) {
1599-
zend_uchar target_type = (Z_TYPE_P(o1) == IS_FALSE || Z_TYPE_P(o1) == IS_TRUE)
1600-
? _IS_BOOL : Z_TYPE_P(o1);
1601-
if (Z_OBJ_HT_P(o2)->cast_object(Z_OBJ_P(o2), &casted, target_type) == FAILURE) {
1602-
// TODO: Less crazy.
1603-
if (target_type == IS_LONG || target_type == IS_DOUBLE) {
1604-
zend_error(E_NOTICE, "Object of class %s could not be converted to %s",
1605-
ZSTR_VAL(Z_OBJCE_P(o2)->name), zend_get_type_by_const(target_type));
1606-
if (target_type == IS_LONG) {
1607-
ZVAL_LONG(&casted, 1);
1608-
} else {
1609-
ZVAL_DOUBLE(&casted, 1.0);
1610-
}
1596+
zend_uchar target_type = (Z_TYPE_P(o1) == IS_FALSE || Z_TYPE_P(o1) == IS_TRUE)
1597+
? _IS_BOOL : Z_TYPE_P(o1);
1598+
if (Z_OBJ_HT_P(o2)->cast_object(Z_OBJ_P(o2), &casted, target_type) == FAILURE) {
1599+
// TODO: Less crazy.
1600+
if (target_type == IS_LONG || target_type == IS_DOUBLE) {
1601+
zend_error(E_NOTICE, "Object of class %s could not be converted to %s",
1602+
ZSTR_VAL(Z_OBJCE_P(o2)->name), zend_get_type_by_const(target_type));
1603+
if (target_type == IS_LONG) {
1604+
ZVAL_LONG(&casted, 1);
16111605
} else {
1612-
return -1;
1606+
ZVAL_DOUBLE(&casted, 1.0);
16131607
}
1608+
} else {
1609+
return -1;
16141610
}
1615-
int ret = zend_compare(o1, &casted);
1616-
zval_ptr_dtor(&casted);
1617-
return ret;
16181611
}
1612+
int ret = zend_compare(o1, &casted);
1613+
zval_ptr_dtor(&casted);
1614+
return ret;
16191615
}
16201616
return ZEND_UNCOMPARABLE;
16211617
}

Zend/zend_object_handlers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ struct _zend_object_handlers {
158158
zend_object_get_method_t get_method; /* required */
159159
zend_object_get_constructor_t get_constructor; /* required */
160160
zend_object_get_class_name_t get_class_name; /* required */
161-
zend_object_cast_t cast_object; /* optional */
161+
zend_object_cast_t cast_object; /* required */
162162
zend_object_count_elements_t count_elements; /* optional */
163163
zend_object_get_debug_info_t get_debug_info; /* optional */
164164
zend_object_get_closure_t get_closure; /* optional */

Zend/zend_operators.c

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,11 @@ ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len) /* {
137137
/* {{{ convert_object_to_type: dst will be either ctype or UNDEF */
138138
#define convert_object_to_type(op, dst, ctype, conv_func) \
139139
ZVAL_UNDEF(dst); \
140-
if (Z_OBJ_HT_P(op)->cast_object) { \
141-
if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), dst, ctype) == FAILURE) { \
142-
zend_error(E_NOTICE, \
143-
"Object of class %s could not be converted to %s", ZSTR_VAL(Z_OBJCE_P(op)->name),\
144-
zend_get_type_by_const(ctype)); \
145-
} \
146-
}
140+
if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), dst, ctype) == FAILURE) { \
141+
zend_error(E_NOTICE, \
142+
"Object of class %s could not be converted to %s", ZSTR_VAL(Z_OBJCE_P(op)->name),\
143+
zend_get_type_by_const(ctype)); \
144+
} \
147145

148146
/* }}} */
149147

@@ -565,13 +563,10 @@ ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op) /* {{{ */
565563
break;
566564
case IS_OBJECT: {
567565
zval tmp;
568-
569-
if (Z_OBJ_HT_P(op)->cast_object) {
570-
if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), &tmp, IS_STRING) == SUCCESS) {
571-
zval_ptr_dtor(op);
572-
ZVAL_COPY_VALUE(op, &tmp);
573-
return;
574-
}
566+
if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), &tmp, IS_STRING) == SUCCESS) {
567+
zval_ptr_dtor(op);
568+
ZVAL_COPY_VALUE(op, &tmp);
569+
return;
575570
}
576571
if (!EG(exception)) {
577572
zend_throw_error(NULL, "Object of class %s could not be converted to string", ZSTR_VAL(Z_OBJCE_P(op)->name));
@@ -874,10 +869,8 @@ static zend_always_inline zend_string* __zval_get_string_func(zval *op, zend_boo
874869
NULL : ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED);
875870
case IS_OBJECT: {
876871
zval tmp;
877-
if (Z_OBJ_HT_P(op)->cast_object) {
878-
if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), &tmp, IS_STRING) == SUCCESS) {
879-
return Z_STR(tmp);
880-
}
872+
if (Z_OBJ_HT_P(op)->cast_object(Z_OBJ_P(op), &tmp, IS_STRING) == SUCCESS) {
873+
return Z_STR(tmp);
881874
}
882875
if (!EG(exception)) {
883876
zend_throw_error(NULL, "Object of class %s could not be converted to string", ZSTR_VAL(Z_OBJCE_P(op)->name));
@@ -2422,14 +2415,11 @@ ZEND_API int ZEND_FASTCALL zend_is_true(zval *op) /* {{{ */
24222415
ZEND_API int ZEND_FASTCALL zend_object_is_true(zval *op) /* {{{ */
24232416
{
24242417
zend_object *zobj = Z_OBJ_P(op);
2425-
2426-
if (zobj->handlers->cast_object) {
2427-
zval tmp;
2428-
if (zobj->handlers->cast_object(zobj, &tmp, _IS_BOOL) == SUCCESS) {
2429-
return Z_TYPE(tmp) == IS_TRUE;
2430-
}
2431-
zend_error(E_RECOVERABLE_ERROR, "Object of class %s could not be converted to bool", ZSTR_VAL(zobj->ce->name));
2418+
zval tmp;
2419+
if (zobj->handlers->cast_object(zobj, &tmp, _IS_BOOL) == SUCCESS) {
2420+
return Z_TYPE(tmp) == IS_TRUE;
24322421
}
2422+
zend_error(E_RECOVERABLE_ERROR, "Object of class %s could not be converted to bool", ZSTR_VAL(zobj->ce->name));
24332423
return 1;
24342424
}
24352425
/* }}} */

ext/ffi/ffi.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4659,6 +4659,11 @@ static HashTable *zend_fake_get_gc(zend_object *ob, zval **table, int *n) /* {{{
46594659
}
46604660
/* }}} */
46614661

4662+
static int zend_fake_cast_object(zend_object *obj, zval *result, int type)
4663+
{
4664+
return FAILURE;
4665+
}
4666+
46624667
static ZEND_COLD zend_never_inline void zend_ffi_use_after_free(void) /* {{{ */
46634668
{
46644669
zend_throw_error(zend_ffi_exception_ce, "Use after free()");
@@ -4911,7 +4916,7 @@ ZEND_MINIT_FUNCTION(ffi)
49114916
zend_ffi_handlers.unset_dimension = zend_fake_unset_dimension;
49124917
zend_ffi_handlers.get_method = zend_ffi_get_func;
49134918
zend_ffi_handlers.compare = NULL;
4914-
zend_ffi_handlers.cast_object = NULL;
4919+
zend_ffi_handlers.cast_object = zend_fake_cast_object;
49154920
zend_ffi_handlers.get_debug_info = NULL;
49164921
zend_ffi_handlers.get_closure = NULL;
49174922
zend_ffi_handlers.get_properties = zend_fake_get_properties;
@@ -4990,7 +4995,7 @@ ZEND_MINIT_FUNCTION(ffi)
49904995
zend_ffi_cdata_free_handlers.get_method = zend_fake_get_method;
49914996
zend_ffi_cdata_free_handlers.get_class_name = zend_ffi_cdata_get_class_name;
49924997
zend_ffi_cdata_free_handlers.compare = zend_ffi_cdata_compare_objects;
4993-
zend_ffi_cdata_free_handlers.cast_object = NULL;
4998+
zend_ffi_cdata_free_handlers.cast_object = zend_fake_cast_object;
49944999
zend_ffi_cdata_free_handlers.count_elements = NULL;
49955000
zend_ffi_cdata_free_handlers.get_debug_info = zend_ffi_free_get_debug_info;
49965001
zend_ffi_cdata_free_handlers.get_closure = NULL;
@@ -5020,7 +5025,7 @@ ZEND_MINIT_FUNCTION(ffi)
50205025
zend_ffi_ctype_handlers.get_method = zend_fake_get_method;
50215026
zend_ffi_ctype_handlers.get_class_name = zend_ffi_ctype_get_class_name;
50225027
zend_ffi_ctype_handlers.compare = zend_ffi_ctype_compare_objects;
5023-
zend_ffi_ctype_handlers.cast_object = NULL;
5028+
zend_ffi_ctype_handlers.cast_object = zend_fake_cast_object;
50245029
zend_ffi_ctype_handlers.count_elements = NULL;
50255030
zend_ffi_ctype_handlers.get_debug_info = zend_ffi_ctype_get_debug_info;
50265031
zend_ffi_ctype_handlers.get_closure = NULL;

ext/intl/collator/collator_convert.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,13 @@ zval* collator_convert_object_to_string( zval* obj, zval *rv )
224224
}
225225

226226
/* Try object's handlers. */
227-
if( Z_OBJ_HT_P(obj)->cast_object )
228-
{
229-
zstr = rv;
227+
zstr = rv;
230228

231-
if( Z_OBJ_HT_P(obj)->cast_object( Z_OBJ_P(obj), zstr, IS_STRING ) == FAILURE )
232-
{
233-
/* cast_object failed => bail out. */
234-
zval_ptr_dtor( zstr );
235-
COLLATOR_CONVERT_RETURN_FAILED( obj );
236-
}
229+
if( Z_OBJ_HT_P(obj)->cast_object( Z_OBJ_P(obj), zstr, IS_STRING ) == FAILURE )
230+
{
231+
/* cast_object failed => bail out. */
232+
zval_ptr_dtor( zstr );
233+
COLLATOR_CONVERT_RETURN_FAILED( obj );
237234
}
238235

239236
/* Object wasn't successfully converted => bail out. */

0 commit comments

Comments
 (0)