@@ -333,25 +333,89 @@ static zend_object *gmp_clone_obj(zend_object *obj) /* {{{ */
333333}
334334/* }}} */ 
335335
336- static  void  shift_operator_helper (gmp_binary_ui_op_t  op , zval  * return_value , zval  * op1 , zval  * op2 , uint8_t  opcode ) {
337- 	zend_long  shift  =  zval_get_long (op2 );
336+ static  zend_result  shift_operator_helper (gmp_binary_ui_op_t  op , zval  * return_value , zval  * op1 , zval  * op2 , uint8_t  opcode ) {
337+ 	zend_long  shift  =  0 ;
338+ 
339+ 	if  (UNEXPECTED (Z_TYPE_P (op2 ) !=  IS_LONG )) {
340+ 		if  (UNEXPECTED (!IS_GMP (op2 ))) {
341+ 			// For PHP 8.3 and up use zend_try_get_long() 
342+ 			switch  (Z_TYPE_P (op2 )) {
343+ 				case  IS_DOUBLE :
344+ 					shift  =  zval_get_long (op2 );
345+ 					if  (UNEXPECTED (EG (exception ))) {
346+ 						return  FAILURE ;
347+ 					}
348+ 					break ;
349+ 				case  IS_STRING :
350+ 					if  (is_numeric_str_function (Z_STR_P (op2 ), & shift , NULL ) !=  IS_LONG ) {
351+ 						goto valueof_op_failure ;
352+ 					}
353+ 					break ;
354+ 				default :
355+ 					goto typeof_op_failure ;
356+ 			}
357+ 		} else  {
358+ 			// TODO We shouldn't cast the GMP object to int here 
359+ 			shift  =  zval_get_long (op2 );
360+ 		}
361+ 	} else  {
362+ 		shift  =  Z_LVAL_P (op2 );
363+ 	}
338364
339365	if  (shift  <  0 ) {
340366		zend_throw_error (
341367			zend_ce_value_error , "%s must be greater than or equal to 0" ,
342368			opcode  ==  ZEND_POW  ? "Exponent"  : "Shift" 
343369		);
344370		ZVAL_UNDEF (return_value );
345- 		return ;
371+ 		return   FAILURE ;
346372	} else  {
347373		mpz_ptr  gmpnum_op , gmpnum_result ;
348374		gmp_temp_t  temp ;
349375
350- 		FETCH_GMP_ZVAL (gmpnum_op , op1 , temp , 1 );
376+ 		/* We do not use FETCH_GMP_ZVAL(...); here as we don't use convert_to_gmp() 
377+ 		 * as we want to handle the emitted exception ourself.  */ 
378+ 		if  (UNEXPECTED (!IS_GMP (op1 ))) {
379+ 			if  (UNEXPECTED (Z_TYPE_P (op1 ) !=  IS_LONG )) {
380+ 				goto typeof_op_failure ;
381+ 			}
382+ 			mpz_init (temp .num );
383+ 			mpz_set_si (temp .num , Z_LVAL_P (op1 ));
384+ 			temp .is_used  =  1 ;
385+ 			gmpnum_op  =  temp .num ;
386+ 		} else  {
387+ 			gmpnum_op  =  GET_GMP_FROM_ZVAL (op1 );
388+ 			temp .is_used  =  0 ;
389+ 		}
351390		INIT_GMP_RETVAL (gmpnum_result );
352391		op (gmpnum_result , gmpnum_op , (gmp_ulong ) shift );
353392		FREE_GMP_TEMP (temp );
393+ 		return  SUCCESS ;
394+ 	}
395+ 
396+ typeof_op_failure : ;
397+ 	/* Returning FAILURE without throwing an exception would emit the 
398+ 	 * Unsupported operand types: GMP OP TypeOfOp2 
399+ 	 * However, this leads to the engine trying to interpret the GMP object as an integer 
400+ 	 * and doing the operation that way, which is not something we want. */ 
401+ 	const  char  * op_sigil ;
402+ 	switch  (opcode ) {
403+ 		case  ZEND_POW :
404+ 			op_sigil  =  "**" ;
405+ 			break ;
406+ 		case  ZEND_SL :
407+ 			op_sigil  =  "<<" ;
408+ 			break ;
409+ 		case  ZEND_SR :
410+ 			op_sigil  =  ">>" ;
411+ 			break ;
412+ 		EMPTY_SWITCH_DEFAULT_CASE ();
354413	}
414+ 	zend_type_error ("Unsupported operand types: %s %s %s" , zend_zval_type_name (op1 ), op_sigil , zend_zval_type_name (op2 ));
415+ 	return  FAILURE ;
416+ valueof_op_failure :
417+ 	zend_value_error ("Number is not an integer string" );
418+ 	return  FAILURE ;
355419}
356420
357421#define  DO_BINARY_UI_OP_EX (op , uop , check_b_zero ) \
@@ -380,18 +444,15 @@ static zend_result gmp_do_operation_ex(uint8_t opcode, zval *result, zval *op1,
380444	case  ZEND_MUL :
381445		DO_BINARY_UI_OP (mpz_mul );
382446	case  ZEND_POW :
383- 		shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
384- 		return  SUCCESS ;
447+ 		return  shift_operator_helper (mpz_pow_ui , result , op1 , op2 , opcode );
385448	case  ZEND_DIV :
386449		DO_BINARY_UI_OP_EX (mpz_tdiv_q , gmp_mpz_tdiv_q_ui , 1 );
387450	case  ZEND_MOD :
388451		DO_BINARY_UI_OP_EX (mpz_mod , gmp_mpz_mod_ui , 1 );
389452	case  ZEND_SL :
390- 		shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
391- 		return  SUCCESS ;
453+ 		return  shift_operator_helper (mpz_mul_2exp , result , op1 , op2 , opcode );
392454	case  ZEND_SR :
393- 		shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
394- 		return  SUCCESS ;
455+ 		return  shift_operator_helper (mpz_fdiv_q_2exp , result , op1 , op2 , opcode );
395456	case  ZEND_BW_OR :
396457		DO_BINARY_OP (mpz_ior );
397458	case  ZEND_BW_AND :
@@ -619,6 +680,13 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui
619680	case  IS_STRING : {
620681		return  convert_zstr_to_gmp (gmpnumber , Z_STR_P (val ), base , arg_pos );
621682	}
683+ 	case  IS_NULL :
684+ 		/* Just reject null for operator overloading */ 
685+ 		if  (arg_pos  ==  0 ) {
686+ 			zend_type_error ("Number must be of type GMP|string|int, %s given" , zend_zval_type_name (val ));
687+ 			return  FAILURE ;
688+ 		}
689+ 		ZEND_FALLTHROUGH ;
622690	default : {
623691		zend_long  lval ;
624692		if  (!zend_parse_arg_long_slow (val , & lval , arg_pos )) {
0 commit comments