Skip to content

Commit 5e7adf7

Browse files
committed
Allow overriding (array) cast using cast_object handler
Previously this was only possible by changing get_properties, which also impacts other behaviors. This change required an adjustment in compare_function, which compares objects and non-objects by casting the object to the type of the non-object. However, as an exception, arrays are treated as null.
1 parent f48ee1f commit 5e7adf7

File tree

5 files changed

+123
-95
lines changed

5 files changed

+123
-95
lines changed

Zend/zend_object_handlers.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,21 @@ ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int ty
16871687
}
16881688
}
16891689
return FAILURE;
1690+
case IS_ARRAY:
1691+
{
1692+
HashTable *obj_ht = Z_OBJ_HT_P(readobj)->get_properties(readobj);
1693+
if (obj_ht) {
1694+
/* fast copy */
1695+
obj_ht = zend_proptable_to_symtable(obj_ht,
1696+
(Z_OBJCE_P(readobj)->default_properties_count ||
1697+
Z_OBJ_P(readobj)->handlers != &std_object_handlers ||
1698+
GC_IS_RECURSIVE(obj_ht)));
1699+
ZVAL_ARR(writeobj, obj_ht);
1700+
} else {
1701+
array_init(writeobj);
1702+
}
1703+
return SUCCESS;
1704+
}
16901705
case _IS_BOOL:
16911706
ZVAL_TRUE(writeobj);
16921707
return SUCCESS;

Zend/zend_operators.c

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -610,27 +610,13 @@ ZEND_API void ZEND_FASTCALL convert_to_array(zval *op) /* {{{ */
610610
if (Z_OBJCE_P(op) == zend_ce_closure) {
611611
convert_scalar_to_array(op);
612612
} else {
613-
if (Z_OBJ_HT_P(op)->get_properties) {
614-
HashTable *obj_ht = Z_OBJ_HT_P(op)->get_properties(op);
615-
if (obj_ht) {
616-
/* fast copy */
617-
obj_ht = zend_proptable_to_symtable(obj_ht,
618-
(Z_OBJCE_P(op)->default_properties_count ||
619-
Z_OBJ_P(op)->handlers != &std_object_handlers ||
620-
GC_IS_RECURSIVE(obj_ht)));
621-
zval_ptr_dtor(op);
622-
ZVAL_ARR(op, obj_ht);
623-
return;
624-
}
625-
} else {
626-
zval dst;
627-
convert_object_to_type(op, &dst, IS_ARRAY, convert_to_array);
613+
zval dst;
614+
convert_object_to_type(op, &dst, IS_ARRAY, convert_to_array);
628615

629-
if (Z_TYPE(dst) == IS_ARRAY) {
630-
zval_ptr_dtor(op);
631-
ZVAL_COPY_VALUE(op, &dst);
632-
return;
633-
}
616+
if (Z_TYPE(dst) == IS_ARRAY) {
617+
zval_ptr_dtor(op);
618+
ZVAL_COPY_VALUE(op, &dst);
619+
return;
634620
}
635621

636622
zval_ptr_dtor(op);
@@ -2094,6 +2080,10 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2)
20942080
ret = compare_function(result, op_free, op2);
20952081
zend_free_obj_get_result(op_free);
20962082
return ret;
2083+
} else if (Z_TYPE_P(op2) == IS_ARRAY) {
2084+
/* Treat array like null */
2085+
ZVAL_LONG(result, 1);
2086+
return SUCCESS;
20972087
} else if (Z_TYPE_P(op2) != IS_OBJECT && Z_OBJ_HT_P(op1)->cast_object) {
20982088
ZVAL_UNDEF(&tmp_free);
20992089
if (Z_OBJ_HT_P(op1)->cast_object(op1, &tmp_free, ((Z_TYPE_P(op2) == IS_FALSE || Z_TYPE_P(op2) == IS_TRUE) ? _IS_BOOL : Z_TYPE_P(op2))) == FAILURE) {
@@ -2113,6 +2103,10 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2)
21132103
ret = compare_function(result, op1, op_free);
21142104
zend_free_obj_get_result(op_free);
21152105
return ret;
2106+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
2107+
/* Treat array like null */
2108+
ZVAL_LONG(result, -1);
2109+
return SUCCESS;
21162110
} else if (Z_TYPE_P(op1) != IS_OBJECT && Z_OBJ_HT_P(op2)->cast_object) {
21172111
ZVAL_UNDEF(&tmp_free);
21182112
if (Z_OBJ_HT_P(op2)->cast_object(op2, &tmp_free, ((Z_TYPE_P(op1) == IS_FALSE || Z_TYPE_P(op1) == IS_TRUE) ? _IS_BOOL : Z_TYPE_P(op1))) == FAILURE) {

Zend/zend_vm_def.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5307,22 +5307,25 @@ ZEND_VM_COLD_CONST_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE)
53075307
} else {
53085308
ZVAL_EMPTY_ARRAY(result);
53095309
}
5310-
} else if (Z_OBJ_HT_P(expr)->get_properties) {
5311-
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
5312-
if (obj_ht) {
5313-
/* fast copy */
5314-
obj_ht = zend_proptable_to_symtable(obj_ht,
5315-
(Z_OBJCE_P(expr)->default_properties_count ||
5316-
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
5317-
GC_IS_RECURSIVE(obj_ht)));
5318-
ZVAL_ARR(result, obj_ht);
5319-
} else {
5320-
ZVAL_EMPTY_ARRAY(result);
5321-
}
53225310
} else {
5323-
ZVAL_COPY_VALUE(result, expr);
5324-
Z_ADDREF_P(result);
5325-
convert_to_array(result);
5311+
ZVAL_UNDEF(result);
5312+
if (Z_OBJ_HT_P(expr)->cast_object) {
5313+
if (Z_OBJ_HT_P(expr)->cast_object(expr, result, IS_ARRAY) == FAILURE) {
5314+
zend_error(E_RECOVERABLE_ERROR,
5315+
"Object of class %s could not be converted to array",
5316+
ZSTR_VAL(Z_OBJCE_P(expr)->name));
5317+
}
5318+
} else if (Z_OBJ_HT_P(expr)->get) {
5319+
zval *newop = Z_OBJ_HT_P(expr)->get(expr, result);
5320+
if (Z_TYPE_P(newop) != IS_OBJECT) {
5321+
/* for safety - avoid loop */
5322+
ZVAL_COPY_VALUE(result, newop);
5323+
convert_to_array(result);
5324+
}
5325+
}
5326+
if (UNEXPECTED(Z_TYPE_P(result) != IS_ARRAY)) {
5327+
array_init(result);
5328+
}
53265329
}
53275330
} else {
53285331
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));

Zend/zend_vm_execute.h

Lines changed: 72 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,22 +3121,25 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_H
31213121
} else {
31223122
ZVAL_EMPTY_ARRAY(result);
31233123
}
3124-
} else if (Z_OBJ_HT_P(expr)->get_properties) {
3125-
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
3126-
if (obj_ht) {
3127-
/* fast copy */
3128-
obj_ht = zend_proptable_to_symtable(obj_ht,
3129-
(Z_OBJCE_P(expr)->default_properties_count ||
3130-
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
3131-
GC_IS_RECURSIVE(obj_ht)));
3132-
ZVAL_ARR(result, obj_ht);
3133-
} else {
3134-
ZVAL_EMPTY_ARRAY(result);
3135-
}
31363124
} else {
3137-
ZVAL_COPY_VALUE(result, expr);
3138-
Z_ADDREF_P(result);
3139-
convert_to_array(result);
3125+
ZVAL_UNDEF(result);
3126+
if (Z_OBJ_HT_P(expr)->cast_object) {
3127+
if (Z_OBJ_HT_P(expr)->cast_object(expr, result, IS_ARRAY) == FAILURE) {
3128+
zend_error(E_RECOVERABLE_ERROR,
3129+
"Object of class %s could not be converted to array",
3130+
ZSTR_VAL(Z_OBJCE_P(expr)->name));
3131+
}
3132+
} else if (Z_OBJ_HT_P(expr)->get) {
3133+
zval *newop = Z_OBJ_HT_P(expr)->get(expr, result);
3134+
if (Z_TYPE_P(newop) != IS_OBJECT) {
3135+
/* for safety - avoid loop */
3136+
ZVAL_COPY_VALUE(result, newop);
3137+
convert_to_array(result);
3138+
}
3139+
}
3140+
if (UNEXPECTED(Z_TYPE_P(result) != IS_ARRAY)) {
3141+
array_init(result);
3142+
}
31403143
}
31413144
} else {
31423145
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
@@ -18092,22 +18095,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC
1809218095
} else {
1809318096
ZVAL_EMPTY_ARRAY(result);
1809418097
}
18095-
} else if (Z_OBJ_HT_P(expr)->get_properties) {
18096-
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
18097-
if (obj_ht) {
18098-
/* fast copy */
18099-
obj_ht = zend_proptable_to_symtable(obj_ht,
18100-
(Z_OBJCE_P(expr)->default_properties_count ||
18101-
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
18102-
GC_IS_RECURSIVE(obj_ht)));
18103-
ZVAL_ARR(result, obj_ht);
18104-
} else {
18105-
ZVAL_EMPTY_ARRAY(result);
18106-
}
1810718098
} else {
18108-
ZVAL_COPY_VALUE(result, expr);
18109-
Z_ADDREF_P(result);
18110-
convert_to_array(result);
18099+
ZVAL_UNDEF(result);
18100+
if (Z_OBJ_HT_P(expr)->cast_object) {
18101+
if (Z_OBJ_HT_P(expr)->cast_object(expr, result, IS_ARRAY) == FAILURE) {
18102+
zend_error(E_RECOVERABLE_ERROR,
18103+
"Object of class %s could not be converted to array",
18104+
ZSTR_VAL(Z_OBJCE_P(expr)->name));
18105+
}
18106+
} else if (Z_OBJ_HT_P(expr)->get) {
18107+
zval *newop = Z_OBJ_HT_P(expr)->get(expr, result);
18108+
if (Z_TYPE_P(newop) != IS_OBJECT) {
18109+
/* for safety - avoid loop */
18110+
ZVAL_COPY_VALUE(result, newop);
18111+
convert_to_array(result);
18112+
}
18113+
}
18114+
if (UNEXPECTED(Z_TYPE_P(result) != IS_ARRAY)) {
18115+
array_init(result);
18116+
}
1811118117
}
1811218118
} else {
1811318119
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
@@ -21100,22 +21106,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC
2110021106
} else {
2110121107
ZVAL_EMPTY_ARRAY(result);
2110221108
}
21103-
} else if (Z_OBJ_HT_P(expr)->get_properties) {
21104-
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
21105-
if (obj_ht) {
21106-
/* fast copy */
21107-
obj_ht = zend_proptable_to_symtable(obj_ht,
21108-
(Z_OBJCE_P(expr)->default_properties_count ||
21109-
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
21110-
GC_IS_RECURSIVE(obj_ht)));
21111-
ZVAL_ARR(result, obj_ht);
21112-
} else {
21113-
ZVAL_EMPTY_ARRAY(result);
21114-
}
2111521109
} else {
21116-
ZVAL_COPY_VALUE(result, expr);
21117-
Z_ADDREF_P(result);
21118-
convert_to_array(result);
21110+
ZVAL_UNDEF(result);
21111+
if (Z_OBJ_HT_P(expr)->cast_object) {
21112+
if (Z_OBJ_HT_P(expr)->cast_object(expr, result, IS_ARRAY) == FAILURE) {
21113+
zend_error(E_RECOVERABLE_ERROR,
21114+
"Object of class %s could not be converted to array",
21115+
ZSTR_VAL(Z_OBJCE_P(expr)->name));
21116+
}
21117+
} else if (Z_OBJ_HT_P(expr)->get) {
21118+
zval *newop = Z_OBJ_HT_P(expr)->get(expr, result);
21119+
if (Z_TYPE_P(newop) != IS_OBJECT) {
21120+
/* for safety - avoid loop */
21121+
ZVAL_COPY_VALUE(result, newop);
21122+
convert_to_array(result);
21123+
}
21124+
}
21125+
if (UNEXPECTED(Z_TYPE_P(result) != IS_ARRAY)) {
21126+
array_init(result);
21127+
}
2111921128
}
2112021129
} else {
2112121130
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
@@ -37467,22 +37476,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO
3746737476
} else {
3746837477
ZVAL_EMPTY_ARRAY(result);
3746937478
}
37470-
} else if (Z_OBJ_HT_P(expr)->get_properties) {
37471-
HashTable *obj_ht = Z_OBJ_HT_P(expr)->get_properties(expr);
37472-
if (obj_ht) {
37473-
/* fast copy */
37474-
obj_ht = zend_proptable_to_symtable(obj_ht,
37475-
(Z_OBJCE_P(expr)->default_properties_count ||
37476-
Z_OBJ_P(expr)->handlers != &std_object_handlers ||
37477-
GC_IS_RECURSIVE(obj_ht)));
37478-
ZVAL_ARR(result, obj_ht);
37479-
} else {
37480-
ZVAL_EMPTY_ARRAY(result);
37481-
}
3748237479
} else {
37483-
ZVAL_COPY_VALUE(result, expr);
37484-
Z_ADDREF_P(result);
37485-
convert_to_array(result);
37480+
ZVAL_UNDEF(result);
37481+
if (Z_OBJ_HT_P(expr)->cast_object) {
37482+
if (Z_OBJ_HT_P(expr)->cast_object(expr, result, IS_ARRAY) == FAILURE) {
37483+
zend_error(E_RECOVERABLE_ERROR,
37484+
"Object of class %s could not be converted to array",
37485+
ZSTR_VAL(Z_OBJCE_P(expr)->name));
37486+
}
37487+
} else if (Z_OBJ_HT_P(expr)->get) {
37488+
zval *newop = Z_OBJ_HT_P(expr)->get(expr, result);
37489+
if (Z_TYPE_P(newop) != IS_OBJECT) {
37490+
/* for safety - avoid loop */
37491+
ZVAL_COPY_VALUE(result, newop);
37492+
convert_to_array(result);
37493+
}
37494+
}
37495+
if (UNEXPECTED(Z_TYPE_P(result) != IS_ARRAY)) {
37496+
array_init(result);
37497+
}
3748637498
}
3748737499
} else {
3748837500
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));

ext/simplexml/simplexml.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,10 @@ static int sxe_object_cast_ex(zval *readobj, zval *writeobj, int type)
18651865

18661866
sxe = Z_SXEOBJ_P(readobj);
18671867

1868+
if (type == IS_ARRAY) {
1869+
return zend_std_cast_object_tostring(readobj, writeobj, type);
1870+
}
1871+
18681872
if (type == _IS_BOOL) {
18691873
node = php_sxe_get_first_node(sxe, NULL);
18701874
if (node) {

0 commit comments

Comments
 (0)