diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 63838ad2a6e23..1111286d97bab 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -98,6 +98,9 @@ typedef struct _gmp_temp { #define IS_GMP(zval) \ (Z_TYPE_P(zval) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zval), gmp_ce)) +#define IS_GMP_BASE(zval) \ + (IS_GMP(zval) && Z_OBJCE_P(zval) == gmp_ce) + #define GET_GMP_OBJECT_FROM_OBJ(obj) \ php_gmp_object_from_zend_object(obj) #define GET_GMP_OBJECT_FROM_ZVAL(zv) \ @@ -370,21 +373,93 @@ static void shift_operator_helper(gmp_binary_ui_op_t op, zval *return_value, zva } \ return SUCCESS; +#define CALL_PARENT_OP(method, on, result) \ + zval method_name; \ + ZVAL_STRING(&method_name, method); \ + zval params[2]; \ + ZVAL_COPY(¶ms[0], op1); \ + ZVAL_COPY(¶ms[1], op2); \ + int success = call_user_function(NULL, on, &method_name, result, 2, params); \ + zval_ptr_dtor(&method_name); \ + zval_ptr_dtor(¶ms[0]); \ + zval_ptr_dtor(¶ms[1]); + static zend_result gmp_do_operation_ex(uint8_t opcode, zval *result, zval *op1, zval *op2) /* {{{ */ { switch (opcode) { case ZEND_ADD: + // is op1 a base gmp object? + if(IS_GMP_BASE(op1) && IS_GMP(op2) && !IS_GMP_BASE(op2)) { + CALL_PARENT_OP("add", op2, result) + return success; + } + + if(IS_GMP(op1) && !IS_GMP_BASE(op1)) { + CALL_PARENT_OP("add", op1, result); + return success; + } + DO_BINARY_UI_OP(mpz_add); case ZEND_SUB: + if(IS_GMP_BASE(op1) && IS_GMP(op2) && !IS_GMP_BASE(op2)) { + CALL_PARENT_OP("subtract", op2, result); + return success; + } + + if(IS_GMP(op1) && !IS_GMP_BASE(op1)) { + CALL_PARENT_OP("subtract", op1, result); + return success; + } + DO_BINARY_UI_OP(mpz_sub); case ZEND_MUL: + if(IS_GMP_BASE(op1) && IS_GMP(op2) && !IS_GMP_BASE(op2)) { + CALL_PARENT_OP("multiply", op2, result); + return success; + } + + if(IS_GMP(op1) && !IS_GMP_BASE(op1)) { + CALL_PARENT_OP("multiply", op1, result); + return success; + } + DO_BINARY_UI_OP(mpz_mul); case ZEND_POW: + if(IS_GMP_BASE(op1) && IS_GMP(op2) && !IS_GMP_BASE(op2)) { + CALL_PARENT_OP("pow", op2, result); + return success; + } + + if(IS_GMP(op1) && !IS_GMP_BASE(op1)) { + CALL_PARENT_OP("pow", op1, result); + return success; + } + shift_operator_helper(mpz_pow_ui, result, op1, op2, opcode); return SUCCESS; case ZEND_DIV: + if(IS_GMP_BASE(op1) && IS_GMP(op2) && !IS_GMP_BASE(op2)) { + CALL_PARENT_OP("divide", op2, result); + return success; + } + + if(IS_GMP(op1) && !IS_GMP_BASE(op1)) { + CALL_PARENT_OP("divide", op1, result); + return success; + } + DO_BINARY_UI_OP_EX(mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1); case ZEND_MOD: + if(IS_GMP_BASE(op1) && IS_GMP(op2) && !IS_GMP_BASE(op2)) { + CALL_PARENT_OP("mod", op2, result); + return success; + } + + if(IS_GMP(op1) && !IS_GMP_BASE(op1)) { + CALL_PARENT_OP("mod", op1, result); + return success; + } + DO_BINARY_UI_OP_EX(mpz_mod, gmp_mpz_mod_ui, 1); case ZEND_SL: shift_operator_helper(mpz_mul_2exp, result, op1, op2, opcode); @@ -407,6 +482,101 @@ static zend_result gmp_do_operation_ex(uint8_t opcode, zval *result, zval *op1, } /* }}} */ +#define CHECK_OVERRIDE_ARGS(op1, op2) \ + if (!(Z_TYPE_P(op1) == IS_OBJECT && instanceof_function(Z_OBJCE_P(op1), gmp_ce)) && \ + Z_TYPE_P(op1) != IS_LONG && Z_TYPE_P(op1) != IS_STRING) { \ + zend_throw_error(NULL, "Parameter 1 must be of type GMP|int|string"); \ + return; \ + } \ + if (!(Z_TYPE_P(op2) == IS_OBJECT && instanceof_function(Z_OBJCE_P(op2), gmp_ce)) && \ + Z_TYPE_P(op2) != IS_LONG && Z_TYPE_P(op2) != IS_STRING) { \ + zend_throw_error(NULL, "Parameter 2 must be of type GMP|int|string"); \ + return; \ + } + +PHP_METHOD(GMP, add) +{ + zval *op1, *op2; + zval result; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &op1, &op2) == FAILURE) { + return; + } + CHECK_OVERRIDE_ARGS(op1, op2) + + gmp_zval_binary_ui_op(&result, op1, op2, mpz_add, mpz_add_ui, 0, /* is_operator */ true); + RETURN_ZVAL(&result, 0, 1); +} + +PHP_METHOD(GMP, multiply) +{ + zval *op1, *op2; + zval result; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &op1, &op2) == FAILURE) { + return; + } + CHECK_OVERRIDE_ARGS(op1, op2); + gmp_zval_binary_ui_op(&result, op1, op2, mpz_mul, mpz_mul_ui, 0, /* is_operator */ true); + RETURN_ZVAL(&result, 0, 1); +} + +PHP_METHOD(GMP, subtract) +{ + zval *op1, *op2; + zval result; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &op1, &op2) == FAILURE) { + return; + } + CHECK_OVERRIDE_ARGS(op1, op2); + gmp_zval_binary_ui_op(&result, op1, op2, mpz_sub, mpz_sub_ui, 0, /* is_operator */ true); + RETURN_ZVAL(&result, 0, 1); +} + +PHP_METHOD(GMP, divide) { + zval *op1, *op2; + zval result; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &op1, &op2) == FAILURE) { + return; + } + CHECK_OVERRIDE_ARGS(op1, op2); + gmp_zval_binary_ui_op(&result, op1, op2, mpz_tdiv_q, gmp_mpz_tdiv_q_ui, 1, /* is_operator */ true); + RETURN_ZVAL(&result, 0, 1); +} + +PHP_METHOD(GMP, mod) +{ + zval *op1, *op2; + zval result; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &op1, &op2) == FAILURE) { + return; + } + CHECK_OVERRIDE_ARGS(op1, op2); + gmp_zval_binary_ui_op(&result, op1, op2, mpz_mod, gmp_mpz_mod_ui, 1, /* is_operator */ true); + RETURN_ZVAL(&result, 0, 1); +} + +PHP_METHOD(GMP, pow) +{ + zval *op1, *op2; + zval result; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &op1, &op2) == FAILURE) { + return; + } + CHECK_OVERRIDE_ARGS(op1, op2); + shift_operator_helper(mpz_pow_ui, &result, op1, op2, ZEND_POW); + RETURN_ZVAL(&result, 0, 1); +} + +PHP_METHOD(GMP, comparable) +{ + RETURN_TRUE; +} + static zend_result gmp_do_operation(uint8_t opcode, zval *result, zval *op1, zval *op2) /* {{{ */ { zval op1_copy; @@ -431,6 +601,26 @@ static int gmp_compare(zval *op1, zval *op2) /* {{{ */ { zval result; + if(IS_GMP(op1) && !IS_GMP_BASE(op1)) { + CALL_PARENT_OP("comparable", op1, &result); + + if(Z_TYPE(result) == IS_FALSE) { + printf("throwing error"); + zend_throw_exception(zend_ce_arithmetic_error, "Can't compare incompatible types", 0); + return 1; + } + } + + if(IS_GMP(op2) && !IS_GMP_BASE(op2)) { + CALL_PARENT_OP("comparable", op2, &result); + + if(Z_TYPE(result) == IS_FALSE) { + printf("throwing error"); + zend_throw_exception(zend_ce_arithmetic_error, "Can't compare incompatible types", 0); + return 1; + } + } + gmp_cmp(&result, op1, op2, /* is_operator */ true); /* An error/exception occurs if one of the operands is not a numeric string @@ -2056,11 +2246,12 @@ ZEND_METHOD(GMP, __construct) zend_string *arg_str = NULL; zend_long arg_l = 0; zend_long base = 0; + zval *arg_1; ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_STR_OR_LONG(arg_str, arg_l) - Z_PARAM_LONG(base) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(arg_1) + Z_PARAM_LONG(base) ZEND_PARSE_PARAMETERS_END(); if (!gmp_verify_base(base, 2)) { @@ -2070,6 +2261,19 @@ ZEND_METHOD(GMP, __construct) return_value = ZEND_THIS; mpz_ptr gmp_number = GET_GMP_FROM_ZVAL(ZEND_THIS); + if (IS_GMP(arg_1)) { + mpz_set(gmp_number, GET_GMP_FROM_ZVAL(arg_1)); + return; + } + + if(Z_TYPE_P(arg_1) == IS_STRING) { + arg_str = Z_STR_P(arg_1); + } + + if(Z_TYPE_P(arg_1) == IS_LONG) { + arg_l = Z_LVAL_P(arg_1); + } + if (gmp_initialize_number(gmp_number, arg_str, arg_l, base) == FAILURE) { RETURN_THROWS(); } diff --git a/ext/gmp/gmp.stub.php b/ext/gmp/gmp.stub.php index ff5b5afb4055b..521a26a9c8b8b 100644 --- a/ext/gmp/gmp.stub.php +++ b/ext/gmp/gmp.stub.php @@ -57,13 +57,27 @@ */ const GMP_NATIVE_ENDIAN = UNKNOWN; -class GMP +readonly class GMP { - public function __construct(int|string $num = 0, int $base = 0) {} + public function __construct(int|string|GMP $num = 0, int $base = 0) {} public function __serialize(): array {} public function __unserialize(array $data): void {} + + protected function add(GMP|int|string $left, GMP|int|string $right): GMP {} + + protected function multiply(GMP|int|string $left, GMP|int|string $right): GMP {} + + protected function subtract(GMP|int|string $left, GMP|int|string $right): GMP {} + + protected function divide(GMP|int|string $left, GMP|int|string $right): GMP {} + + protected function mod(GMP|int|string $left, GMP|int|string $right): GMP {} + + protected function pow(GMP|int|string $base, GMP|int|string $exp): GMP {} + + protected function comparable(GMP|int|string $op1, GMP|int|string $op2): bool {} } function gmp_init(int|string $num, int $base = 0): GMP {} diff --git a/ext/gmp/gmp_arginfo.h b/ext/gmp/gmp_arginfo.h index 8126fa6686731..c3dd7b1cb521e 100644 --- a/ext/gmp/gmp_arginfo.h +++ b/ext/gmp/gmp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d52f82c7084a8122fe07c91eb6d4ab6030daa27d */ + * Stub hash: 40d41bc0aeae5ca465329151c134d546e178d4e4 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_init, 0, 1, GMP, 0) ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_STRING, NULL) @@ -185,7 +185,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_binomial, 0, 2, GMP, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_GMP___construct, 0, 0, 0) - ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_STRING, "0") + ZEND_ARG_OBJ_TYPE_MASK(0, num, GMP, MAY_BE_LONG|MAY_BE_STRING, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, base, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -196,6 +196,29 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_GMP___unserialize, 0, 1, I ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_GMP_add, 0, 2, GMP, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, left, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_OBJ_TYPE_MASK(0, right, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() + +#define arginfo_class_GMP_multiply arginfo_class_GMP_add + +#define arginfo_class_GMP_subtract arginfo_class_GMP_add + +#define arginfo_class_GMP_divide arginfo_class_GMP_add + +#define arginfo_class_GMP_mod arginfo_class_GMP_add + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_GMP_pow, 0, 2, GMP, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, base, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_OBJ_TYPE_MASK(0, exp, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_GMP_comparable, 0, 2, _IS_BOOL, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, op1, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_OBJ_TYPE_MASK(0, op2, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(gmp_init); ZEND_FUNCTION(gmp_import); ZEND_FUNCTION(gmp_export); @@ -249,6 +272,13 @@ ZEND_FUNCTION(gmp_binomial); ZEND_METHOD(GMP, __construct); ZEND_METHOD(GMP, __serialize); ZEND_METHOD(GMP, __unserialize); +ZEND_METHOD(GMP, add); +ZEND_METHOD(GMP, multiply); +ZEND_METHOD(GMP, subtract); +ZEND_METHOD(GMP, divide); +ZEND_METHOD(GMP, mod); +ZEND_METHOD(GMP, pow); +ZEND_METHOD(GMP, comparable); static const zend_function_entry ext_functions[] = { ZEND_FE(gmp_init, arginfo_gmp_init) @@ -309,6 +339,13 @@ static const zend_function_entry class_GMP_methods[] = { ZEND_ME(GMP, __construct, arginfo_class_GMP___construct, ZEND_ACC_PUBLIC) ZEND_ME(GMP, __serialize, arginfo_class_GMP___serialize, ZEND_ACC_PUBLIC) ZEND_ME(GMP, __unserialize, arginfo_class_GMP___unserialize, ZEND_ACC_PUBLIC) + ZEND_ME(GMP, add, arginfo_class_GMP_add, ZEND_ACC_PROTECTED) + ZEND_ME(GMP, multiply, arginfo_class_GMP_multiply, ZEND_ACC_PROTECTED) + ZEND_ME(GMP, subtract, arginfo_class_GMP_subtract, ZEND_ACC_PROTECTED) + ZEND_ME(GMP, divide, arginfo_class_GMP_divide, ZEND_ACC_PROTECTED) + ZEND_ME(GMP, mod, arginfo_class_GMP_mod, ZEND_ACC_PROTECTED) + ZEND_ME(GMP, pow, arginfo_class_GMP_pow, ZEND_ACC_PROTECTED) + ZEND_ME(GMP, comparable, arginfo_class_GMP_comparable, ZEND_ACC_PROTECTED) ZEND_FE_END }; @@ -334,6 +371,7 @@ static zend_class_entry *register_class_GMP(void) INIT_CLASS_ENTRY(ce, "GMP", class_GMP_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_READONLY_CLASS; return class_entry; }