From e793c9af69541c68dfad09a10a813b27d50cfdf1 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Thu, 22 Dec 2022 16:28:59 +0000 Subject: [PATCH 01/15] Add object and resource tests to array_(sum|product)() --- .../tests/array/array_product_variation1.phpt | 12 +++++------- .../tests/array/array_product_variation5.phpt | 9 +++++++++ .../tests/array/array_product_variation6.phpt | 11 +++++++++++ ext/standard/tests/array/array_sum_variation8.phpt | 9 +++++++++ ext/standard/tests/array/array_sum_variation9.phpt | 11 +++++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 ext/standard/tests/array/array_product_variation5.phpt create mode 100644 ext/standard/tests/array/array_product_variation6.phpt create mode 100644 ext/standard/tests/array/array_sum_variation8.phpt create mode 100644 ext/standard/tests/array/array_sum_variation9.phpt diff --git a/ext/standard/tests/array/array_product_variation1.phpt b/ext/standard/tests/array/array_product_variation1.phpt index c27d872a85f36..4d24b6b210827 100644 --- a/ext/standard/tests/array/array_product_variation1.phpt +++ b/ext/standard/tests/array/array_product_variation1.phpt @@ -7,22 +7,20 @@ echo "*** Testing array_product() : variation - using non numeric values ***\n"; class A { static function help() { echo "hello\n"; } } -$fp = fopen(__FILE__, "r"); $types = array("boolean (true)" => true, "boolean (false)" => false, "string" => "hello", "numeric string" => "12", - "resource" => $fp, "object" => new A(), "null" => null, + "resource" => STDERR, "object" => new A(), "null" => null, "array" => array(3,2)); foreach ($types as $desc => $type) { - echo $desc . "\n"; - var_dump(array_product(array($type))); + echo $desc, "\n"; + var_dump(array_product([1, $type])); echo "\n"; } -fclose($fp); ?> ---EXPECTF-- +--EXPECT-- *** Testing array_product() : variation - using non numeric values *** boolean (true) int(1) @@ -37,7 +35,7 @@ numeric string int(12) resource -int(%d) +int(3) object int(1) diff --git a/ext/standard/tests/array/array_product_variation5.phpt b/ext/standard/tests/array/array_product_variation5.phpt new file mode 100644 index 0000000000000..cb83139095e4b --- /dev/null +++ b/ext/standard/tests/array/array_product_variation5.phpt @@ -0,0 +1,9 @@ +--TEST-- +Test array_product() function: ressources in array +--FILE-- + +--EXPECT-- +int(30) diff --git a/ext/standard/tests/array/array_product_variation6.phpt b/ext/standard/tests/array/array_product_variation6.phpt new file mode 100644 index 0000000000000..5b90a4918fbe3 --- /dev/null +++ b/ext/standard/tests/array/array_product_variation6.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test array_product() function with objects castable to numeric type +--EXTENSIONS-- +gmp +--FILE-- + +--EXPECT-- +int(1) diff --git a/ext/standard/tests/array/array_sum_variation8.phpt b/ext/standard/tests/array/array_sum_variation8.phpt new file mode 100644 index 0000000000000..d3ddfe3c1cdef --- /dev/null +++ b/ext/standard/tests/array/array_sum_variation8.phpt @@ -0,0 +1,9 @@ +--TEST-- +Test array_sum() function: ressources in array +--FILE-- + +--EXPECT-- +int(13) diff --git a/ext/standard/tests/array/array_sum_variation9.phpt b/ext/standard/tests/array/array_sum_variation9.phpt new file mode 100644 index 0000000000000..1dfa0c0938c82 --- /dev/null +++ b/ext/standard/tests/array/array_sum_variation9.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test array_sum() function with objects castable to numeric type +--EXTENSIONS-- +gmp +--FILE-- + +--EXPECT-- +int(0) From 4a17d7498d0573c5a90dc637cf2bef95016a86c6 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Thu, 22 Dec 2022 17:19:55 +0000 Subject: [PATCH 02/15] Start aligning array_(sum|product)() behaviour with binary ops --- ext/standard/array.c | 99 ++++++++++++------- .../tests/array/array_product_variation6.phpt | 2 +- .../tests/array/array_sum_variation9.phpt | 2 +- 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 88d2335c0afb9..91830e70eb114 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5918,62 +5918,95 @@ PHP_FUNCTION(array_rand) /* {{{ Returns the sum of the array entries */ PHP_FUNCTION(array_sum) { - zval *input, - *entry, - entry_n; + HashTable *input; + zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(input) + Z_PARAM_ARRAY_HT(input) ZEND_PARSE_PARAMETERS_END(); - ZVAL_LONG(return_value, 0); + if (zend_hash_num_elements(input) == 0) { + // TODO Deprecate empty array + RETURN_LONG(0); + } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) { - if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) { - continue; + ZVAL_LONG(return_value, 0); + ZEND_HASH_FOREACH_VAL(input, entry) { + zend_result status = add_function(return_value, return_value, entry); + if (status == FAILURE) { + ZEND_ASSERT(EG(exception)); + zend_clear_exception(); + /* BC resources: previously resources were cast to int */ + if (Z_TYPE_P(entry) == IS_RESOURCE) { + zval tmp; + ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry)); + add_function(return_value, return_value, &tmp); + } + /* BC non numeric strings: previously were cast to 0 */ + if (Z_TYPE_P(entry) == IS_STRING) { + zval tmp; + ZVAL_LONG(&tmp, 0); + add_function(return_value, return_value, &tmp); + } + // TODO Warning/Deprecation? } - ZVAL_COPY(&entry_n, entry); - convert_scalar_to_number(&entry_n); - fast_add_function(return_value, return_value, &entry_n); } ZEND_HASH_FOREACH_END(); + + /* Traversal of array encountered a numerically catable object */ + if (Z_TYPE_P(return_value) == IS_OBJECT) { + /* First try to convert to int */ + zval dst; + if (Z_OBJ_HT_P(return_value)->cast_object(Z_OBJ_P(return_value), &dst, IS_LONG) == SUCCESS) { + zval_ptr_dtor(return_value); + RETURN_COPY(&dst); + } + convert_scalar_to_number(return_value); + } } /* }}} */ /* {{{ Returns the product of the array entries */ PHP_FUNCTION(array_product) { - zval *input, - *entry, - entry_n; - double dval; + HashTable *input; + zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(input) + Z_PARAM_ARRAY_HT(input) ZEND_PARSE_PARAMETERS_END(); - ZVAL_LONG(return_value, 1); - if (!zend_hash_num_elements(Z_ARRVAL_P(input))) { - return; + if (zend_hash_num_elements(input) == 0) { + // TODO Deprecate empty array + RETURN_LONG(1); } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) { - if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) { - continue; - } - ZVAL_COPY(&entry_n, entry); - convert_scalar_to_number(&entry_n); + ZVAL_LONG(return_value, 1); - if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) { - dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n); - if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) { - Z_LVAL_P(return_value) *= Z_LVAL(entry_n); - continue; + ZEND_HASH_FOREACH_VAL(input, entry) { + zend_result status = mul_function(return_value, return_value, entry); + if (status == FAILURE) { + ZEND_ASSERT(EG(exception)); + zend_clear_exception(); + /* BC resources: previously resources were cast to int */ + if (Z_TYPE_P(entry) == IS_RESOURCE) { + zval tmp; + ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry)); + mul_function(return_value, return_value, &tmp); } + /* BC non numeric strings: previously were cast to 0 */ + if (Z_TYPE_P(entry) == IS_STRING) { + zval tmp; + ZVAL_LONG(&tmp, 0); + mul_function(return_value, return_value, &tmp); + } + // TODO Warning/Deprecation? } - convert_to_double(return_value); - convert_to_double(&entry_n); - Z_DVAL_P(return_value) *= Z_DVAL(entry_n); } ZEND_HASH_FOREACH_END(); + + /* Traversal of array encountered a numerically castable object */ + if (Z_TYPE_P(return_value) == IS_OBJECT) { + convert_scalar_to_number(return_value); + } } /* }}} */ diff --git a/ext/standard/tests/array/array_product_variation6.phpt b/ext/standard/tests/array/array_product_variation6.phpt index 5b90a4918fbe3..65a9c835bf2cd 100644 --- a/ext/standard/tests/array/array_product_variation6.phpt +++ b/ext/standard/tests/array/array_product_variation6.phpt @@ -8,4 +8,4 @@ $input = [gmp_init(25), gmp_init(6)]; var_dump(array_product($input)); ?> --EXPECT-- -int(1) +int(150) diff --git a/ext/standard/tests/array/array_sum_variation9.phpt b/ext/standard/tests/array/array_sum_variation9.phpt index 1dfa0c0938c82..1697757f662f8 100644 --- a/ext/standard/tests/array/array_sum_variation9.phpt +++ b/ext/standard/tests/array/array_sum_variation9.phpt @@ -8,4 +8,4 @@ $input = [gmp_init(25), gmp_init(6)]; var_dump(array_sum($input)); ?> --EXPECT-- -int(0) +int(31) From 31d963d6b4f27e6efa02ea362ad321ec8b738081 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 23 Dec 2022 14:15:44 +0000 Subject: [PATCH 03/15] Deprecate passing empty array to array_(sum|product)() --- ext/standard/array.c | 4 ++-- .../tests/array/array_product_empty_array.phpt | 10 ++++++++++ ext/standard/tests/array/array_sum_empty_array.phpt | 10 ++++++++++ ext/standard/tests/array/array_sum_variation7.phpt | 7 ------- ext/standard/tests/array/bug35014.phpt | 2 -- ext/standard/tests/array/bug35014_64bit.phpt | 2 -- ext/standard/tests/array/bug48484.phpt | 8 -------- 7 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 ext/standard/tests/array/array_product_empty_array.phpt create mode 100644 ext/standard/tests/array/array_sum_empty_array.phpt delete mode 100644 ext/standard/tests/array/bug48484.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index 91830e70eb114..a152572f677af 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5926,7 +5926,7 @@ PHP_FUNCTION(array_sum) ZEND_PARSE_PARAMETERS_END(); if (zend_hash_num_elements(input) == 0) { - // TODO Deprecate empty array + php_error_docref(NULL, E_DEPRECATED, "Passing an empty array is deprecated"); RETURN_LONG(0); } @@ -5976,7 +5976,7 @@ PHP_FUNCTION(array_product) ZEND_PARSE_PARAMETERS_END(); if (zend_hash_num_elements(input) == 0) { - // TODO Deprecate empty array + php_error_docref(NULL, E_DEPRECATED, "Passing an empty array is deprecated"); RETURN_LONG(1); } diff --git a/ext/standard/tests/array/array_product_empty_array.phpt b/ext/standard/tests/array/array_product_empty_array.phpt new file mode 100644 index 0000000000000..92aaaf64b6bc9 --- /dev/null +++ b/ext/standard/tests/array/array_product_empty_array.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test array_product() function with empty array +--FILE-- + +--EXPECTF-- +Deprecated: array_product(): Passing an empty array is deprecated in %s on line %d +int(1) diff --git a/ext/standard/tests/array/array_sum_empty_array.phpt b/ext/standard/tests/array/array_sum_empty_array.phpt new file mode 100644 index 0000000000000..802487b249b3d --- /dev/null +++ b/ext/standard/tests/array/array_sum_empty_array.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test array_sum() function with empty array +--FILE-- + +--EXPECTF-- +Deprecated: array_sum(): Passing an empty array is deprecated in %s on line %d +int(0) diff --git a/ext/standard/tests/array/array_sum_variation7.phpt b/ext/standard/tests/array/array_sum_variation7.phpt index 9e716485f4e91..ce39905fd3dae 100644 --- a/ext/standard/tests/array/array_sum_variation7.phpt +++ b/ext/standard/tests/array/array_sum_variation7.phpt @@ -9,11 +9,6 @@ Test array_sum() function : usage variations - 'input' array with unexpected val echo "*** Testing array_sum() : array with unexpected entries ***\n"; -// empty array -$input = array(); -echo "-- empty array --\n"; -var_dump( array_sum($input) ); - // string array $input = array('Apple', 'Banana', 'Carrot', 'Mango', 'Orange'); echo "-- array with string values --\n"; @@ -64,8 +59,6 @@ echo "Done" ?> --EXPECT-- *** Testing array_sum() : array with unexpected entries *** --- empty array -- -int(0) -- array with string values -- int(0) -- array with bool values -- diff --git a/ext/standard/tests/array/bug35014.phpt b/ext/standard/tests/array/bug35014.phpt index f1f407081f52d..3935657f7a796 100644 --- a/ext/standard/tests/array/bug35014.phpt +++ b/ext/standard/tests/array/bug35014.phpt @@ -5,7 +5,6 @@ Bug #35014 (array_product() always returns 0) (32bit) --FILE-- --EXPECT-- -int(1) int(0) int(3) int(9) diff --git a/ext/standard/tests/array/bug35014_64bit.phpt b/ext/standard/tests/array/bug35014_64bit.phpt index 40d656d83509e..29fa9e187ac73 100644 --- a/ext/standard/tests/array/bug35014_64bit.phpt +++ b/ext/standard/tests/array/bug35014_64bit.phpt @@ -7,7 +7,6 @@ precision=14 --FILE-- --EXPECT-- -int(1) int(0) int(3) int(9) diff --git a/ext/standard/tests/array/bug48484.phpt b/ext/standard/tests/array/bug48484.phpt deleted file mode 100644 index 5688c0cad8beb..0000000000000 --- a/ext/standard/tests/array/bug48484.phpt +++ /dev/null @@ -1,8 +0,0 @@ ---TEST-- -Bug 48484 (array_product() always returns 0 for an empty array) ---FILE-- - ---EXPECT-- -int(1) From 621d24fc9392a65f83a36aaafcc765dff9f506b7 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 23 Dec 2022 15:24:41 +0000 Subject: [PATCH 04/15] Handle object that define a do_operation handler but no cast handler Introduce such a dummy class in the zend_test extension --- ext/standard/array.c | 32 +++-- ...ray_product_objects_operation_no_cast.phpt | 12 ++ .../array_sum_objects_operation_no_cast.phpt | 12 ++ ext/zend_test/test.c | 110 ++++++++++++++++++ ext/zend_test/test.stub.php | 5 + ext/zend_test/test_arginfo.h | 30 ++++- .../tests/do_operation_not_cast.phpt | 23 ++++ 7 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 ext/standard/tests/array/array_product_objects_operation_no_cast.phpt create mode 100644 ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt create mode 100644 ext/zend_test/tests/do_operation_not_cast.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index a152572f677af..f72d298f3e35b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5952,15 +5952,21 @@ PHP_FUNCTION(array_sum) } } ZEND_HASH_FOREACH_END(); - /* Traversal of array encountered a numerically catable object */ + /* Traversal of array encountered objects that support multiplication */ if (Z_TYPE_P(return_value) == IS_OBJECT) { - /* First try to convert to int */ + /* Cannot use convert_scalar_to_number() as we don't know if the cast succeeded */ zval dst; - if (Z_OBJ_HT_P(return_value)->cast_object(Z_OBJ_P(return_value), &dst, IS_LONG) == SUCCESS) { + zend_result status = Z_OBJ_HT_P(return_value)->cast_object(Z_OBJ_P(return_value), &dst, _IS_NUMBER); + + /* Do not type error for BC */ + if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) { + zend_error(E_WARNING, "Object of class %s could not be converted to int|float", + ZSTR_VAL(Z_OBJCE_P(return_value)->name)); zval_ptr_dtor(return_value); - RETURN_COPY(&dst); + RETURN_LONG(0); } - convert_scalar_to_number(return_value); + zval_ptr_dtor(return_value); + RETURN_COPY_VALUE(&dst); } } /* }}} */ @@ -6003,9 +6009,21 @@ PHP_FUNCTION(array_product) } } ZEND_HASH_FOREACH_END(); - /* Traversal of array encountered a numerically castable object */ + /* Traversal of array encountered objects that support multiplication */ if (Z_TYPE_P(return_value) == IS_OBJECT) { - convert_scalar_to_number(return_value); + /* Cannot use convert_scalar_to_number() as we don't know if the cast succeeded */ + zval dst; + zend_result status = Z_OBJ_HT_P(return_value)->cast_object(Z_OBJ_P(return_value), &dst, _IS_NUMBER); + + /* Do not type error for BC */ + if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) { + zend_error(E_WARNING, "Object of class %s could not be converted to int|float", + ZSTR_VAL(Z_OBJCE_P(return_value)->name)); + zval_ptr_dtor(return_value); + RETURN_LONG(1); + } + zval_ptr_dtor(return_value); + RETURN_COPY_VALUE(&dst); } } /* }}} */ diff --git a/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt b/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt new file mode 100644 index 0000000000000..5d783ea558dc1 --- /dev/null +++ b/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test array_product() function with objects that implement addition but not castable to numeric type +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Warning: Object of class DoOperationNoCast could not be converted to int|float in %s on line %d +int(1) diff --git a/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt b/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt new file mode 100644 index 0000000000000..a55441c5a77dd --- /dev/null +++ b/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test array_sum() function with objects that implement addition but not castable to numeric type +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Warning: Object of class DoOperationNoCast could not be converted to int|float in %s on line %d +int(0) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 7c7b1d3c6500f..0875bf57e60cf 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -717,6 +717,110 @@ static ZEND_METHOD(ZendTestForbidDynamicCall, callStatic) zend_forbid_dynamic_call(); } +/* donc refers to DoOperationNoCast */ +static zend_class_entry *donc_ce; +static zend_object_handlers donc_object_handlers; + +static zend_object* donc_object_create_ex(zend_class_entry* ce, zend_long l) { + zend_object *obj = zend_objects_new(ce); + object_properties_init(obj, ce); + obj->handlers = &donc_object_handlers; + ZVAL_LONG(OBJ_PROP_NUM(obj, 0), l); + return obj; +} +static zend_object *donc_object_create(zend_class_entry *ce) /* {{{ */ +{ + return donc_object_create_ex(ce, 0); +} +/* }}} */ + +static inline void donc_create(zval *target, zend_long l) /* {{{ */ +{ + ZVAL_OBJ(target, donc_object_create_ex(donc_ce, l)); +} + +#define IS_DONC(zval) \ + (Z_TYPE_P(zval) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zval), donc_ce)) + +static void donc_add(zval *result, zval *op1, zval *op2) +{ + zend_long val_1; + zend_long val_2; + if (IS_DONC(op1)) { + val_1 = Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op1), 0)); + } else { + val_1 = zval_get_long(op1); + } + if (IS_DONC(op2)) { + val_2 = Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op2), 0)); + } else { + val_2 = zval_get_long(op2); + } + + donc_create(result, val_1 + val_2); +} +static void donc_mul(zval *result, zval *op1, zval *op2) +{ + zend_long val_1; + zend_long val_2; + if (IS_DONC(op1)) { + val_1 = Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op1), 0)); + } else { + val_1 = zval_get_long(op1); + } + if (IS_DONC(op2)) { + val_2 = Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op2), 0)); + } else { + val_2 = zval_get_long(op2); + } + + donc_create(result, val_1 * val_2); +} + +static zend_result donc_do_operation(zend_uchar opcode, zval *result, zval *op1, zval *op2) +{ + zval op1_copy; + zend_result status; + + if (result == op1) { + ZVAL_COPY_VALUE(&op1_copy, op1); + op1 = &op1_copy; + } + + switch (opcode) { + case ZEND_ADD: + donc_add(result, op1, op2); + if (UNEXPECTED(EG(exception))) { status = FAILURE; } + status = SUCCESS; + break; + case ZEND_MUL: + donc_mul(result, op1, op2); + if (UNEXPECTED(EG(exception))) { status = FAILURE; } + status = SUCCESS; + break; + default: + status = FAILURE; + break; + } + + if (status == SUCCESS && op1 == &op1_copy) { + zval_ptr_dtor(op1); + } + + return status; +} + +PHP_METHOD(DoOperationNoCast, __construct) +{ + zend_long l; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(l) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), l); +} + PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals) @@ -778,6 +882,12 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_string_enum = register_class_ZendTestStringEnum(); zend_test_int_enum = register_class_ZendTestIntEnum(); + /* DoOperationNoCast class */ + donc_ce = register_class_DoOperationNoCast(); + donc_ce->create_object = donc_object_create; + memcpy(&donc_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + donc_object_handlers.do_operation = donc_do_operation; + zend_register_functions(NULL, ext_function_legacy, NULL, EG(current_module)->type); // Loading via dl() not supported with the observer API diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 9058db36d75fd..83b1e4de334e3 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -117,6 +117,11 @@ enum ZendTestIntEnum: int { case Baz = -1; } + final class DoOperationNoCast { + private int $val; + public function __construct(int $val) {} + } + function zend_test_array_return(): array {} function zend_test_nullable_array_return(): null|array {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 4d0ff2a4e810c..800db7ee9783a 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3e2308593cea5289709dcfb1c9ee598de945d2e2 */ + * Stub hash: 947056bfe52d586b8a4969e3022f84e83b677e0e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -160,6 +160,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_ZendTestForbidDynamicCall_callStatic arginfo_zend_test_void_return +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DoOperationNoCast___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, val, IS_LONG, 0) +ZEND_END_ARG_INFO() + #if (PHP_VERSION_ID >= 80100) ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZendTestNS_Foo_method, 0, 0, IS_LONG, 0) #else @@ -223,6 +227,7 @@ static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, override); static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override); static ZEND_METHOD(ZendTestForbidDynamicCall, call); static ZEND_METHOD(ZendTestForbidDynamicCall, callStatic); +static ZEND_METHOD(DoOperationNoCast, __construct); static ZEND_METHOD(ZendTestNS_Foo, method); static ZEND_METHOD(ZendTestNS_UnlikelyCompileError, method); static ZEND_METHOD(ZendTestNS2_Foo, method); @@ -353,6 +358,12 @@ static const zend_function_entry class_ZendTestIntEnum_methods[] = { }; +static const zend_function_entry class_DoOperationNoCast_methods[] = { + ZEND_ME(DoOperationNoCast, __construct, arginfo_class_DoOperationNoCast___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + static const zend_function_entry class_ZendTestNS_Foo_methods[] = { ZEND_ME(ZendTestNS_Foo, method, arginfo_class_ZendTestNS_Foo_method, ZEND_ACC_PUBLIC) ZEND_FE_END @@ -695,6 +706,23 @@ static zend_class_entry *register_class_ZendTestIntEnum(void) } #endif +static zend_class_entry *register_class_DoOperationNoCast(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DoOperationNoCast", class_DoOperationNoCast_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + zval property_val_default_value; + ZVAL_UNDEF(&property_val_default_value); + zend_string *property_val_name = zend_string_init("val", sizeof("val") - 1, 1); + zend_declare_typed_property(class_entry, property_val_name, &property_val_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_val_name); + + return class_entry; +} + static zend_class_entry *register_class_ZendTestNS_Foo(void) { zend_class_entry ce, *class_entry; diff --git a/ext/zend_test/tests/do_operation_not_cast.phpt b/ext/zend_test/tests/do_operation_not_cast.phpt new file mode 100644 index 0000000000000..fbab03c01a841 --- /dev/null +++ b/ext/zend_test/tests/do_operation_not_cast.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test DoOperationNotCast dummy class +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +object(DoOperationNoCast)#3 (1) { + ["val":"DoOperationNoCast":private]=> + int(31) +} +object(DoOperationNoCast)#3 (1) { + ["val":"DoOperationNoCast":private]=> + int(150) +} From 39975f541b5976266daf030a1a21cc2a751b7da9 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 23 Dec 2022 15:43:32 +0000 Subject: [PATCH 05/15] Add warning when trying to perform binop on unsupported types --- ext/standard/array.c | 6 ++-- ext/standard/tests/array/003.phpt | 16 +++++----- .../tests/array/array_product_variation1.phpt | 10 +++++- .../tests/array/array_product_variation5.phpt | 3 +- .../tests/array/array_sum_variation7.phpt | 32 ++++++++++++++++++- .../tests/array/array_sum_variation8.phpt | 3 +- 6 files changed, 56 insertions(+), 14 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index f72d298f3e35b..5b5b6f388d475 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5948,7 +5948,8 @@ PHP_FUNCTION(array_sum) ZVAL_LONG(&tmp, 0); add_function(return_value, return_value, &tmp); } - // TODO Warning/Deprecation? + php_error_docref(NULL, E_WARNING, "Addition is not supported on type %s", + zend_zval_type_name(entry)); } } ZEND_HASH_FOREACH_END(); @@ -6005,7 +6006,8 @@ PHP_FUNCTION(array_product) ZVAL_LONG(&tmp, 0); mul_function(return_value, return_value, &tmp); } - // TODO Warning/Deprecation? + php_error_docref(NULL, E_WARNING, "Multiplication is not supported on type %s", + zend_zval_type_name(entry)); } } ZEND_HASH_FOREACH_END(); diff --git a/ext/standard/tests/array/003.phpt b/ext/standard/tests/array/003.phpt index 2cea8034bb10c..27d9cad4eb3b7 100644 --- a/ext/standard/tests/array/003.phpt +++ b/ext/standard/tests/array/003.phpt @@ -8,27 +8,27 @@ require(__DIR__ . '/data.inc'); function cmp ($a, $b) { is_array ($a) - and $a = array_sum ($a); + and $a = count($a); is_array ($b) - and $b = array_sum ($b); + and $b = count($b); return strcmp ($a, $b); } -echo " -- Testing uasort() -- \n"; +echo "-- Testing uasort() --\n"; uasort ($data, 'cmp'); var_dump ($data); -echo "\n -- Testing uksort() -- \n"; +echo "\n-- Testing uksort() --\n"; uksort ($data, 'cmp'); var_dump ($data); -echo "\n -- Testing usort() -- \n"; +echo "\n-- Testing usort() --\n"; usort ($data, 'cmp'); var_dump ($data); ?> --EXPECT-- --- Testing uasort() -- +-- Testing uasort() -- array(8) { [16777216]=> float(-0.3333333333333333) @@ -53,7 +53,7 @@ array(8) { string(4) "test" } - -- Testing uksort() -- +-- Testing uksort() -- array(8) { [-1000]=> array(2) { @@ -78,7 +78,7 @@ array(8) { int(27) } - -- Testing usort() -- +-- Testing usort() -- array(8) { [0]=> float(-0.3333333333333333) diff --git a/ext/standard/tests/array/array_product_variation1.phpt b/ext/standard/tests/array/array_product_variation1.phpt index 4d24b6b210827..330479f4ef8bf 100644 --- a/ext/standard/tests/array/array_product_variation1.phpt +++ b/ext/standard/tests/array/array_product_variation1.phpt @@ -20,7 +20,7 @@ foreach ($types as $desc => $type) { } ?> ---EXPECT-- +--EXPECTF-- *** Testing array_product() : variation - using non numeric values *** boolean (true) int(1) @@ -29,20 +29,28 @@ boolean (false) int(0) string + +Warning: array_product(): Multiplication is not supported on type string in %s on line %d int(0) numeric string int(12) resource + +Warning: array_product(): Multiplication is not supported on type resource in %s on line %d int(3) object + +Warning: array_product(): Multiplication is not supported on type A in %s on line %d int(1) null int(0) array + +Warning: array_product(): Multiplication is not supported on type array in %s on line %d int(1) diff --git a/ext/standard/tests/array/array_product_variation5.phpt b/ext/standard/tests/array/array_product_variation5.phpt index cb83139095e4b..e05b3301c1989 100644 --- a/ext/standard/tests/array/array_product_variation5.phpt +++ b/ext/standard/tests/array/array_product_variation5.phpt @@ -5,5 +5,6 @@ Test array_product() function: ressources in array $input = [10, STDERR /* Should get casted to 3 as an integer */]; var_dump(array_product($input)); ?> ---EXPECT-- +--EXPECTF-- +Warning: array_product(): Multiplication is not supported on type resource in %s on line %d int(30) diff --git a/ext/standard/tests/array/array_sum_variation7.phpt b/ext/standard/tests/array/array_sum_variation7.phpt index ce39905fd3dae..181a172e3087a 100644 --- a/ext/standard/tests/array/array_sum_variation7.phpt +++ b/ext/standard/tests/array/array_sum_variation7.phpt @@ -57,18 +57,48 @@ echo "-- array with mixed values --\n"; var_dump( array_sum($input) ); echo "Done" ?> ---EXPECT-- +--EXPECTF-- *** Testing array_sum() : array with unexpected entries *** -- array with string values -- + +Warning: array_sum(): Addition is not supported on type string in %s on line %d + +Warning: array_sum(): Addition is not supported on type string in %s on line %d + +Warning: array_sum(): Addition is not supported on type string in %s on line %d + +Warning: array_sum(): Addition is not supported on type string in %s on line %d + +Warning: array_sum(): Addition is not supported on type string in %s on line %d int(0) -- array with bool values -- int(3) -- array with null values -- int(0) -- array with subarrays -- + +Warning: array_sum(): Addition is not supported on type array in %s on line %d + +Warning: array_sum(): Addition is not supported on type array in %s on line %d + +Warning: array_sum(): Addition is not supported on type array in %s on line %d int(0) -- array with object values -- + +Warning: array_sum(): Addition is not supported on type MyClass in %s on line %d + +Warning: array_sum(): Addition is not supported on type MyClass in %s on line %d + +Warning: array_sum(): Addition is not supported on type MyClass in %s on line %d + +Warning: array_sum(): Addition is not supported on type MyClass in %s on line %d int(0) -- array with mixed values -- + +Warning: array_sum(): Addition is not supported on type string in %s on line %d + +Warning: array_sum(): Addition is not supported on type string in %s on line %d + +Warning: array_sum(): Addition is not supported on type array in %s on line %d float(14) Done diff --git a/ext/standard/tests/array/array_sum_variation8.phpt b/ext/standard/tests/array/array_sum_variation8.phpt index d3ddfe3c1cdef..454d4c2314908 100644 --- a/ext/standard/tests/array/array_sum_variation8.phpt +++ b/ext/standard/tests/array/array_sum_variation8.phpt @@ -5,5 +5,6 @@ Test array_sum() function: ressources in array $input = [10, STDERR /* Should get casted to 3 as an integer */]; var_dump(array_sum($input)); ?> ---EXPECT-- +--EXPECTF-- +Warning: array_sum(): Addition is not supported on type resource in %s on line %d int(13) From fafd690e795cb431af313026226487461e4fdd99 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 23 Dec 2022 15:53:02 +0000 Subject: [PATCH 06/15] Merge common array_(sum|product) implementation --- ext/standard/array.c | 79 ++++++++++---------------------------------- 1 file changed, 17 insertions(+), 62 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 5b5b6f388d475..65327adf48a03 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5915,8 +5915,8 @@ PHP_FUNCTION(array_rand) } /* }}} */ -/* {{{ Returns the sum of the array entries */ -PHP_FUNCTION(array_sum) +/* Wrapper for array_sum and array_product */ +static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial) { HashTable *input; zval *entry; @@ -5927,12 +5927,12 @@ PHP_FUNCTION(array_sum) if (zend_hash_num_elements(input) == 0) { php_error_docref(NULL, E_DEPRECATED, "Passing an empty array is deprecated"); - RETURN_LONG(0); + RETURN_LONG(initial); } - ZVAL_LONG(return_value, 0); + ZVAL_LONG(return_value, initial); ZEND_HASH_FOREACH_VAL(input, entry) { - zend_result status = add_function(return_value, return_value, entry); + zend_result status = op(return_value, return_value, entry); if (status == FAILURE) { ZEND_ASSERT(EG(exception)); zend_clear_exception(); @@ -5940,16 +5940,16 @@ PHP_FUNCTION(array_sum) if (Z_TYPE_P(entry) == IS_RESOURCE) { zval tmp; ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry)); - add_function(return_value, return_value, &tmp); + op(return_value, return_value, &tmp); } /* BC non numeric strings: previously were cast to 0 */ if (Z_TYPE_P(entry) == IS_STRING) { zval tmp; ZVAL_LONG(&tmp, 0); - add_function(return_value, return_value, &tmp); + op(return_value, return_value, &tmp); } - php_error_docref(NULL, E_WARNING, "Addition is not supported on type %s", - zend_zval_type_name(entry)); + php_error_docref(NULL, E_WARNING, "%s is not supported on type %s", + op_name, zend_zval_type_name(entry)); } } ZEND_HASH_FOREACH_END(); @@ -5964,69 +5964,24 @@ PHP_FUNCTION(array_sum) zend_error(E_WARNING, "Object of class %s could not be converted to int|float", ZSTR_VAL(Z_OBJCE_P(return_value)->name)); zval_ptr_dtor(return_value); - RETURN_LONG(0); + RETURN_LONG(initial); } zval_ptr_dtor(return_value); RETURN_COPY_VALUE(&dst); } } + +/* {{{ Returns the sum of the array entries */ +PHP_FUNCTION(array_sum) +{ + php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Addition", add_function, 0); +} /* }}} */ /* {{{ Returns the product of the array entries */ PHP_FUNCTION(array_product) { - HashTable *input; - zval *entry; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_HT(input) - ZEND_PARSE_PARAMETERS_END(); - - if (zend_hash_num_elements(input) == 0) { - php_error_docref(NULL, E_DEPRECATED, "Passing an empty array is deprecated"); - RETURN_LONG(1); - } - - ZVAL_LONG(return_value, 1); - - ZEND_HASH_FOREACH_VAL(input, entry) { - zend_result status = mul_function(return_value, return_value, entry); - if (status == FAILURE) { - ZEND_ASSERT(EG(exception)); - zend_clear_exception(); - /* BC resources: previously resources were cast to int */ - if (Z_TYPE_P(entry) == IS_RESOURCE) { - zval tmp; - ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry)); - mul_function(return_value, return_value, &tmp); - } - /* BC non numeric strings: previously were cast to 0 */ - if (Z_TYPE_P(entry) == IS_STRING) { - zval tmp; - ZVAL_LONG(&tmp, 0); - mul_function(return_value, return_value, &tmp); - } - php_error_docref(NULL, E_WARNING, "Multiplication is not supported on type %s", - zend_zval_type_name(entry)); - } - } ZEND_HASH_FOREACH_END(); - - /* Traversal of array encountered objects that support multiplication */ - if (Z_TYPE_P(return_value) == IS_OBJECT) { - /* Cannot use convert_scalar_to_number() as we don't know if the cast succeeded */ - zval dst; - zend_result status = Z_OBJ_HT_P(return_value)->cast_object(Z_OBJ_P(return_value), &dst, _IS_NUMBER); - - /* Do not type error for BC */ - if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) { - zend_error(E_WARNING, "Object of class %s could not be converted to int|float", - ZSTR_VAL(Z_OBJCE_P(return_value)->name)); - zval_ptr_dtor(return_value); - RETURN_LONG(1); - } - zval_ptr_dtor(return_value); - RETURN_COPY_VALUE(&dst); - } + php_array_binop(INTERNAL_FUNCTION_PARAM_PASSTHRU, "Multiplication", mul_function, 1); } /* }}} */ From f03c4283e8ca160bd907e9b77a71b912fc54b7de Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Wed, 28 Dec 2022 17:41:13 +0000 Subject: [PATCH 07/15] Add array_reduce comparison to array_(sum|product) tests --- .../tests/array/array_product_empty_array.phpt | 9 +++++++++ .../array_product_objects_operation_no_cast.phpt | 12 ++++++++++++ .../tests/array/array_product_variation5.phpt | 13 +++++++++++++ .../tests/array/array_product_variation6.phpt | 11 +++++++++++ ext/standard/tests/array/array_sum_empty_array.phpt | 9 +++++++++ .../array/array_sum_objects_operation_no_cast.phpt | 12 ++++++++++++ ext/standard/tests/array/array_sum_variation8.phpt | 13 +++++++++++++ ext/standard/tests/array/array_sum_variation9.phpt | 11 +++++++++++ 8 files changed, 90 insertions(+) diff --git a/ext/standard/tests/array/array_product_empty_array.phpt b/ext/standard/tests/array/array_product_empty_array.phpt index 92aaaf64b6bc9..d84ec11321493 100644 --- a/ext/standard/tests/array/array_product_empty_array.phpt +++ b/ext/standard/tests/array/array_product_empty_array.phpt @@ -3,8 +3,17 @@ Test array_product() function with empty array --FILE-- $carry * $value, 1)); ?> --EXPECTF-- +array_product() version: + Deprecated: array_product(): Passing an empty array is deprecated in %s on line %d int(1) +array_reduce() version: +int(1) diff --git a/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt b/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt index 5d783ea558dc1..ff0c6bec9aa83 100644 --- a/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt +++ b/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt @@ -5,8 +5,20 @@ zend_test --FILE-- $carry * $value, 1)); ?> --EXPECTF-- +array_product() version: + Warning: Object of class DoOperationNoCast could not be converted to int|float in %s on line %d int(1) +array_reduce() version: +object(DoOperationNoCast)#5 (1) { + ["val":"DoOperationNoCast":private]=> + int(1500) +} diff --git a/ext/standard/tests/array/array_product_variation5.phpt b/ext/standard/tests/array/array_product_variation5.phpt index e05b3301c1989..1f444d96138b3 100644 --- a/ext/standard/tests/array/array_product_variation5.phpt +++ b/ext/standard/tests/array/array_product_variation5.phpt @@ -3,8 +3,21 @@ Test array_product() function: ressources in array --FILE-- $carry * $value, 1)); +} catch (TypeError $e) { + echo $e->getMessage(); +} ?> --EXPECTF-- +array_product() version: + Warning: array_product(): Multiplication is not supported on type resource in %s on line %d int(30) +array_reduce() version: +Unsupported operand types: int * resource diff --git a/ext/standard/tests/array/array_product_variation6.phpt b/ext/standard/tests/array/array_product_variation6.phpt index 65a9c835bf2cd..6526197ce52ec 100644 --- a/ext/standard/tests/array/array_product_variation6.phpt +++ b/ext/standard/tests/array/array_product_variation6.phpt @@ -5,7 +5,18 @@ gmp --FILE-- $carry * $value, 1)); ?> --EXPECT-- +array_product() version: int(150) +array_reduce() version: +object(GMP)#5 (1) { + ["num"]=> + string(3) "150" +} diff --git a/ext/standard/tests/array/array_sum_empty_array.phpt b/ext/standard/tests/array/array_sum_empty_array.phpt index 802487b249b3d..72294b5f7aa0c 100644 --- a/ext/standard/tests/array/array_sum_empty_array.phpt +++ b/ext/standard/tests/array/array_sum_empty_array.phpt @@ -3,8 +3,17 @@ Test array_sum() function with empty array --FILE-- $carry + $value, 0)); ?> --EXPECTF-- +array_sum() version: + Deprecated: array_sum(): Passing an empty array is deprecated in %s on line %d int(0) +array_reduce() version: +int(0) diff --git a/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt b/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt index a55441c5a77dd..8b414ba8f013e 100644 --- a/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt +++ b/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt @@ -5,8 +5,20 @@ zend_test --FILE-- $carry + $value, 0)); ?> --EXPECTF-- +array_sum() version: + Warning: Object of class DoOperationNoCast could not be converted to int|float in %s on line %d int(0) +array_reduce() version: +object(DoOperationNoCast)#5 (1) { + ["val":"DoOperationNoCast":private]=> + int(31) +} diff --git a/ext/standard/tests/array/array_sum_variation8.phpt b/ext/standard/tests/array/array_sum_variation8.phpt index 454d4c2314908..b3f283942ce1e 100644 --- a/ext/standard/tests/array/array_sum_variation8.phpt +++ b/ext/standard/tests/array/array_sum_variation8.phpt @@ -3,8 +3,21 @@ Test array_sum() function: ressources in array --FILE-- $carry + $value, 0)); +} catch (TypeError $e) { + echo $e->getMessage(); +} ?> --EXPECTF-- +array_sum() version: + Warning: array_sum(): Addition is not supported on type resource in %s on line %d int(13) +array_reduce() version: +Unsupported operand types: int + resource diff --git a/ext/standard/tests/array/array_sum_variation9.phpt b/ext/standard/tests/array/array_sum_variation9.phpt index 1697757f662f8..61af56b2a5f81 100644 --- a/ext/standard/tests/array/array_sum_variation9.phpt +++ b/ext/standard/tests/array/array_sum_variation9.phpt @@ -5,7 +5,18 @@ gmp --FILE-- $carry + $value, 0)); ?> --EXPECT-- +array_sum() version: int(31) +array_reduce() version: +object(GMP)#5 (1) { + ["num"]=> + string(2) "31" +} From 1474cb28535df055bc1de0680ef71ce2248712c2 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Wed, 28 Dec 2022 17:43:19 +0000 Subject: [PATCH 08/15] Revert deprecation for empty arrays --- ext/standard/array.c | 1 - ext/standard/tests/array/array_product_empty_array.phpt | 4 +--- ext/standard/tests/array/array_sum_empty_array.phpt | 4 +--- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 65327adf48a03..d030056fa0b7a 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5926,7 +5926,6 @@ static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, b ZEND_PARSE_PARAMETERS_END(); if (zend_hash_num_elements(input) == 0) { - php_error_docref(NULL, E_DEPRECATED, "Passing an empty array is deprecated"); RETURN_LONG(initial); } diff --git a/ext/standard/tests/array/array_product_empty_array.phpt b/ext/standard/tests/array/array_product_empty_array.phpt index d84ec11321493..714030d15103a 100644 --- a/ext/standard/tests/array/array_product_empty_array.phpt +++ b/ext/standard/tests/array/array_product_empty_array.phpt @@ -10,10 +10,8 @@ var_dump(array_product($input)); echo "array_reduce() version:\n"; var_dump(array_reduce($input, fn($carry, $value) => $carry * $value, 1)); ?> ---EXPECTF-- +--EXPECT-- array_product() version: - -Deprecated: array_product(): Passing an empty array is deprecated in %s on line %d int(1) array_reduce() version: int(1) diff --git a/ext/standard/tests/array/array_sum_empty_array.phpt b/ext/standard/tests/array/array_sum_empty_array.phpt index 72294b5f7aa0c..17c80ce43472d 100644 --- a/ext/standard/tests/array/array_sum_empty_array.phpt +++ b/ext/standard/tests/array/array_sum_empty_array.phpt @@ -10,10 +10,8 @@ var_dump(array_sum($input)); echo "array_reduce() version:\n"; var_dump(array_reduce($input, fn($carry, $value) => $carry + $value, 0)); ?> ---EXPECTF-- +--EXPECT-- array_sum() version: - -Deprecated: array_sum(): Passing an empty array is deprecated in %s on line %d int(0) array_reduce() version: int(0) From 2f9065415d60d01413b1454b243c166f9ffcf6ea Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Wed, 28 Dec 2022 17:45:02 +0000 Subject: [PATCH 09/15] Nit: Use English spelling for resource --- ext/standard/tests/array/array_product_variation5.phpt | 2 +- ext/standard/tests/array/array_sum_variation8.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/array/array_product_variation5.phpt b/ext/standard/tests/array/array_product_variation5.phpt index 1f444d96138b3..525b6a52bd14a 100644 --- a/ext/standard/tests/array/array_product_variation5.phpt +++ b/ext/standard/tests/array/array_product_variation5.phpt @@ -1,5 +1,5 @@ --TEST-- -Test array_product() function: ressources in array +Test array_product() function: resources in array --FILE-- Date: Wed, 28 Dec 2022 17:45:45 +0000 Subject: [PATCH 10/15] Nit: use else if instead of second if --- ext/standard/array.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index d030056fa0b7a..8660341426c05 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5942,7 +5942,7 @@ static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, b op(return_value, return_value, &tmp); } /* BC non numeric strings: previously were cast to 0 */ - if (Z_TYPE_P(entry) == IS_STRING) { + else if (Z_TYPE_P(entry) == IS_STRING) { zval tmp; ZVAL_LONG(&tmp, 0); op(return_value, return_value, &tmp); From 8a186724af8e2e9135b7e75c99ad892414b0709f Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Sat, 31 Dec 2022 14:16:48 +0000 Subject: [PATCH 11/15] Nit: indentation --- ext/zend_test/test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 0875bf57e60cf..66d9bffe57ccb 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -883,10 +883,10 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_int_enum = register_class_ZendTestIntEnum(); /* DoOperationNoCast class */ - donc_ce = register_class_DoOperationNoCast(); - donc_ce->create_object = donc_object_create; - memcpy(&donc_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - donc_object_handlers.do_operation = donc_do_operation; + donc_ce = register_class_DoOperationNoCast(); + donc_ce->create_object = donc_object_create; + memcpy(&donc_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + donc_object_handlers.do_operation = donc_do_operation; zend_register_functions(NULL, ext_function_legacy, NULL, EG(current_module)->type); From 648bb688b82f729302d53586721462fb3138cd9c Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Thu, 26 Jan 2023 13:50:31 +0000 Subject: [PATCH 12/15] Add FFI example to tests --- ...ray_sum_objects_operation_no_cast_FFI.phpt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt diff --git a/ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt b/ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt new file mode 100644 index 0000000000000..15602122d42a4 --- /dev/null +++ b/ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test array_sum() function with objects that implement addition but not castable to numeric type +--EXTENSIONS-- +ffi +--FILE-- + $carry + $value, 0)); +?> +--EXPECTF-- +array_sum() version: + +Warning: Object of class FFI\CData could not be converted to int|float in %s on line %d +int(0) +array_reduce() version: +object(FFI\CData:int32_t*)#4 (1) { + [0]=> + int(25) +} From e4883e9c33504217a1ee54fbf0f3e5c5ae3aefa1 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Thu, 26 Jan 2023 16:08:16 +0000 Subject: [PATCH 13/15] Cast objects to numeric --- ext/standard/array.c | 32 +++++++++---------- ...ray_product_objects_operation_no_cast.phpt | 6 +++- .../array_sum_objects_operation_no_cast.phpt | 4 ++- ...ray_sum_objects_operation_no_cast_FFI.phpt | 4 +-- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 8660341426c05..dc0d434d1883e 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5931,6 +5931,21 @@ static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, b ZVAL_LONG(return_value, initial); ZEND_HASH_FOREACH_VAL(input, entry) { + /* For objects we try to cast them to a numeric type */ + if (Z_TYPE_P(entry) == IS_OBJECT) { + zval dst; + zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER); + + /* Do not type error for BC */ + if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) { + php_error_docref(NULL, E_WARNING, "%s is not supported on type %s", + op_name, zend_zval_type_name(entry)); + continue; + } + op(return_value, return_value, &dst); + continue; + } + zend_result status = op(return_value, return_value, entry); if (status == FAILURE) { ZEND_ASSERT(EG(exception)); @@ -5951,23 +5966,6 @@ static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, b op_name, zend_zval_type_name(entry)); } } ZEND_HASH_FOREACH_END(); - - /* Traversal of array encountered objects that support multiplication */ - if (Z_TYPE_P(return_value) == IS_OBJECT) { - /* Cannot use convert_scalar_to_number() as we don't know if the cast succeeded */ - zval dst; - zend_result status = Z_OBJ_HT_P(return_value)->cast_object(Z_OBJ_P(return_value), &dst, _IS_NUMBER); - - /* Do not type error for BC */ - if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) { - zend_error(E_WARNING, "Object of class %s could not be converted to int|float", - ZSTR_VAL(Z_OBJCE_P(return_value)->name)); - zval_ptr_dtor(return_value); - RETURN_LONG(initial); - } - zval_ptr_dtor(return_value); - RETURN_COPY_VALUE(&dst); - } } /* {{{ Returns the sum of the array entries */ diff --git a/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt b/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt index ff0c6bec9aa83..7f4af3759f03e 100644 --- a/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt +++ b/ext/standard/tests/array/array_product_objects_operation_no_cast.phpt @@ -15,7 +15,11 @@ var_dump(array_reduce($input, fn($carry, $value) => $carry * $value, 1)); --EXPECTF-- array_product() version: -Warning: Object of class DoOperationNoCast could not be converted to int|float in %s on line %d +Warning: array_product(): Multiplication is not supported on type DoOperationNoCast in %s on line %d + +Warning: array_product(): Multiplication is not supported on type DoOperationNoCast in %s on line %d + +Warning: array_product(): Multiplication is not supported on type DoOperationNoCast in %s on line %d int(1) array_reduce() version: object(DoOperationNoCast)#5 (1) { diff --git a/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt b/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt index 8b414ba8f013e..d89b44ae0b6b7 100644 --- a/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt +++ b/ext/standard/tests/array/array_sum_objects_operation_no_cast.phpt @@ -15,7 +15,9 @@ var_dump(array_reduce($input, fn($carry, $value) => $carry + $value, 0)); --EXPECTF-- array_sum() version: -Warning: Object of class DoOperationNoCast could not be converted to int|float in %s on line %d +Warning: array_sum(): Addition is not supported on type DoOperationNoCast in %s on line %d + +Warning: array_sum(): Addition is not supported on type DoOperationNoCast in %s on line %d int(0) array_reduce() version: object(DoOperationNoCast)#5 (1) { diff --git a/ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt b/ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt index 15602122d42a4..a19e34f0b170a 100644 --- a/ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt +++ b/ext/standard/tests/array/array_sum_objects_operation_no_cast_FFI.phpt @@ -20,8 +20,8 @@ var_dump(array_reduce($input, fn($carry, $value) => $carry + $value, 0)); --EXPECTF-- array_sum() version: -Warning: Object of class FFI\CData could not be converted to int|float in %s on line %d -int(0) +Warning: array_sum(): Addition is not supported on type FFI\CData in %s on line %d +int(1) array_reduce() version: object(FFI\CData:int32_t*)#4 (1) { [0]=> From 53b3e839fc8ddbf55462003f49466c85de04dc9d Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Fri, 27 Jan 2023 16:10:51 +0000 Subject: [PATCH 14/15] Remove fast_add_function() function --- Zend/zend_operators.h | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index acb7fb0c0a038..c90cca8a648fe 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -707,28 +707,6 @@ overflow: ZEND_ATTRIBUTE_COLD_LABEL #endif } -static zend_always_inline zend_result fast_add_function(zval *result, zval *op1, zval *op2) -{ - if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { - fast_long_add_function(result, op1, op2); - return SUCCESS; - } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { - ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2)); - return SUCCESS; - } - } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { - ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2)); - return SUCCESS; - } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { - ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2))); - return SUCCESS; - } - } - return add_function(result, op1, op2); -} - static zend_always_inline void fast_long_sub_function(zval *result, zval *op1, zval *op2) { #if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__) From 6f2c1f6f4f8b09f1865aed8b0410fa5ff57c0d3f Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Mon, 6 Mar 2023 16:57:27 +0000 Subject: [PATCH 15/15] Add UPGRADING.INTERNALS entry back that got yanked by the rebase --- UPGRADING.INTERNALS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 2fd9a8c86397e..487a2ac147e00 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -37,6 +37,8 @@ PHP 8.3 INTERNALS UPGRADE NOTES * The return types of the following functions have been changed from `bool` to `zend_result`: - zend_fiber_init_context() +* The fast_add_function() has been removed, use add_function() that will + call the static inline add_function_fast() instead. ======================== 2. Build system changes