Commit 6030e5637f7 for php

commit 6030e5637f7d9b06234772ecf37bf5ba4b1024bf
Merge: 0826ebea2be 8184e9efec2
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Tue Sep 29 15:47:46 2026 -0400

    Merge branch 'PHP-8.4' into PHP-8.5

    * PHP-8.4:
      ext/bcmath: Clear the sign of BcMath\Number results that truncate to zero

diff --cc NEWS
index 1320a09aa7e,a011c225aa6..40eaef9775a
--- a/NEWS
+++ b/NEWS
@@@ -1,12 -1,20 +1,16 @@@
  PHP                                                                        NEWS
  |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 -?? ??? ????, PHP 8.4.27
 +?? ??? ????, PHP 8.5.12

+ - BCMath:
+   . Fixed BcMath\Number results that truncate to zero keeping a negative sign
+     and comparing less than zero. (Ilia Alshanetsky)
+
 -- CLI
 -  . Fix GH-22567 (Windows ZTS CLI SAPI should refresh its TSRMLS cache during
 -    request activation). (matyhtf)
 -  . Fixed bug GH-23764 (Built-in server leaks a file descriptor on every HEAD
 -    request for a static file). (jakubskopal)
 -  . Fixed crash in the built-in server when a client is reset before being
 -    accepted. (David Carlier)
 -
 -- Core
 +- Core:
 +  . Fixed bug GH-23644 (Optimizer leaves a constant-vs-constant comparison
 +    unfolded, crashing the VM in zval_undefined_cv). (ndossche)
 +  . Fixed OSS-Fuzz 532353396 (assertion failure with static type). (Girgias)
 +  . Fix GH-23662 (Avoid NAN warning in print_r()). (CodedByManish)
    . Fix GH-21999: GC inconsistency with lazy object, var_dump(), and object
      comparison. (Arnaud)
    . Fixed OSS-Fuzz #536440507 (Immutable class incorrect assertion).
diff --cc ext/bcmath/libbcmath/src/div.c
index 24ec9a64d77,a45ffdb7757..56db164e444
--- a/ext/bcmath/libbcmath/src/div.c
+++ b/ext/bcmath/libbcmath/src/div.c
@@@ -349,63 -345,145 +349,71 @@@ bool bc_divide(bc_num numerator, bc_nu

  	/* If divisor is 1 / -1, the quotient's n_value is equal to numerator's n_value. */
  	if (_bc_do_compare(divisor, BCG(_one_), divisor->n_scale, false) == BCMATH_EQUAL) {
 -		size_t quot_scale = MIN(numerator->n_scale, scale);
 -		*quot = bc_new_num_nonzeroed(numerator->n_len, quot_scale);
 -		char *qptr = (*quot)->n_value;
 -		memcpy(qptr, numerator->n_value, numerator->n_len + quot_scale);
 -		_bc_rm_leading_zeros(*quot);
 +		bc_divide_by_one(numerator, quot, quot_scale);
- 		(*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS;
+ 		if (bc_is_zero(*quot)) {
+ 			(*quot)->n_sign = PLUS;
+ 		} else {
+ 			(*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS;
+ 		}
  		return true;
  	}

 -	char *numeratorptr = numerator->n_value;
 -	char *numeratorend = numeratorptr + numerator->n_len + numerator->n_scale - 1;
 -	size_t numerator_len = numerator->n_len;
 -	size_t numerator_scale = numerator->n_scale;
 -
 -	char *divisorptr = divisor->n_value;
 -	char *divisorend = divisorptr + divisor->n_len + divisor->n_scale - 1;
 -	size_t divisor_len = divisor->n_len;
 -	size_t divisor_scale = divisor->n_scale;
 -	size_t divisor_int_right_zeros = 0;
 -
 -	/* remove divisor trailing zeros */
 -	while (*divisorend == 0 && divisor_scale > 0) {
 -		divisorend--;
 -		divisor_scale--;
 -	}
 -	while (*divisorend == 0) {
 -		divisorend--;
 -		divisor_int_right_zeros++;
 -	}
 +	const char *numeratorptr = numerator->n_value;
 +	size_t numerator_size = numerator->n_len + quot_scale + divisor->n_scale;

 -	if (*numeratorptr == 0 && numerator_len == 1) {
 -		numeratorptr++;
 -		numerator_len = 0;
 -	}
 +	const char *divisorptr = divisor->n_value;
 +	size_t divisor_size = divisor->n_len + divisor->n_scale;

 -	size_t numerator_top_extension = 0;
 -	size_t numerator_bottom_extension = 0;
 -	if (divisor_scale > 0) {
 -		/*
 -		 * e.g. divisor_scale = 4
 -		 * divisor = .0002, to be 2 or divisor = 200.001, to be 200001
 -		 * numerator = .03, to be 300 or numerator = .000003, to be .03
 -		 * numerator may become longer than the original data length due to the addition of
 -		 * trailing zeros in the integer part.
 -		 */
 -		numerator_len += divisor_scale;
 -		numerator_bottom_extension = numerator_scale < divisor_scale ? divisor_scale - numerator_scale : 0;
 -		numerator_scale = numerator_scale > divisor_scale ? numerator_scale - divisor_scale : 0;
 -		divisor_len += divisor_scale;
 -		divisor_scale = 0;
 -	} else if (divisor_int_right_zeros > 0) {
 -		/*
 -		 * e.g. divisor_int_right_zeros = 4
 -		 * divisor = 2000, to be 2
 -		 * numerator = 30, to be .03 or numerator = 30000, to be 30
 -		 * Also, numerator may become longer than the original data length due to the addition of
 -		 * leading zeros in the fractional part.
 -		 */
 -		numerator_top_extension = numerator_len < divisor_int_right_zeros ? divisor_int_right_zeros - numerator_len : 0;
 -		numerator_len = numerator_len > divisor_int_right_zeros ? numerator_len - divisor_int_right_zeros : 0;
 -		numerator_scale += divisor_int_right_zeros;
 -		divisor_len -= divisor_int_right_zeros;
 -		divisor_scale = 0;
 -	}
 -
 -	/* remove numerator leading zeros */
 -	while (*numeratorptr == 0 && numerator_len > 0) {
 +	/* check and remove numerator leading zeros */
 +	size_t numerator_leading_zeros = 0;
 +	while (*numeratorptr == 0) {
  		numeratorptr++;
 -		numerator_len--;
 +		numerator_leading_zeros++;
 +		if (numerator_leading_zeros == numerator_size) {
 +			goto quot_zero;
 +		}
  	}
 -	/* remove divisor leading zeros */
 +	numerator_size -= numerator_leading_zeros;
 +
 +	/* check and remove divisor leading zeros */
  	while (*divisorptr == 0) {
  		divisorptr++;
 -		divisor_len--;
 +		divisor_size--;
  	}

 -	/* Considering the scale specification, the quotient is always 0 if this condition is met */
 -	if (divisor_len > numerator_len + scale) {
 -		*quot = bc_copy_num(BCG(_zero_));
 -		return true;
 +	if (divisor_size > numerator_size) {
 +		goto quot_zero;
  	}

 -	/* Length of numerator data that can be read */
 -	size_t numerator_readable_len = numeratorend - numeratorptr + 1;
 -
 -	/* set scale to numerator */
 -	if (numerator_scale > scale) {
 -		size_t scale_diff = numerator_scale - scale;
 -		if (numerator_bottom_extension > scale_diff) {
 -			numerator_bottom_extension -= scale_diff;
 -		} else {
 -			numerator_bottom_extension = 0;
 -			if (EXPECTED(numerator_readable_len > scale_diff)) {
 -				numerator_readable_len -= scale_diff;
 -				numeratorend -= scale_diff;
 -			} else {
 -				numerator_readable_len = 0;
 -				numeratorend = numeratorptr;
 -			}
 +	/* check and remove divisor trailing zeros. The divisor is not 0, so leave only one digit */
 +	size_t divisor_trailing_zeros = 0;
 +	for (size_t i = divisor_size - 1; i > 0; i--) {
 +		if (divisorptr[i] != 0) {
 +			break;
  		}
 -		numerator_top_extension = MIN(numerator_top_extension, scale);
 -	} else {
 -		numerator_bottom_extension += scale - numerator_scale;
 +		divisor_trailing_zeros++;
  	}
 -	numerator_scale = scale;
 +	divisor_size -= divisor_trailing_zeros;
 +	numerator_size -= divisor_trailing_zeros;

 -	if (divisor_len > numerator_readable_len + numerator_bottom_extension) {
 -		*quot = bc_copy_num(BCG(_zero_));
 -		return true;
 +	size_t quot_size = numerator_size - divisor_size + 1; /* numerator_size >= divisor_size */
 +	if (quot_size > quot_scale) {
 +		*quot = bc_new_num_nonzeroed(quot_size - quot_scale, quot_scale);
 +	} else {
 +		*quot = bc_new_num_nonzeroed(1, quot_scale); /* 1 is for 0 */
  	}

 -	/* If divisor is 1 here, return the result of adjusting the decimal point position of numerator. */
 -	if (divisor_len == 1 && *divisorptr == 1) {
 -		if (numerator_len == 0) {
 -			numerator_len = 1;
 -			numerator_top_extension++;
 -		}
 -		size_t quot_scale = numerator_scale > numerator_bottom_extension ? numerator_scale - numerator_bottom_extension : 0;
 -		numerator_bottom_extension = numerator_scale < numerator_bottom_extension ? numerator_bottom_extension - numerator_scale : 0;
 +	/* Size that can be read from numeratorptr */
 +	size_t numerator_readable_size = numerator->n_len + numerator->n_scale - numerator_leading_zeros;

 -		*quot = bc_new_num_nonzeroed(numerator_len, quot_scale);
 -		char *qptr = (*quot)->n_value;
 -		for (size_t i = 0; i < numerator_top_extension; i++) {
 -			*qptr++ = 0;
 -		}
 -		memcpy(qptr, numeratorptr, numerator_readable_len);
 -		qptr += numerator_readable_len;
 -		for (size_t i = 0; i < numerator_bottom_extension; i++) {
 -			*qptr++ = 0;
 -		}
 +	/* If divisor is 1 here, return the result of adjusting the decimal point position of numerator. */
 +	if (divisor_size == 1 && *divisorptr == 1) {
 +		bc_divide_by_pow_10(numeratorptr, numerator_readable_size, quot, quot_size, quot_scale);
- 		(*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS;
+ 		if (bc_is_zero(*quot)) {
+ 			(*quot)->n_sign = PLUS;
+ 		} else {
+ 			(*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS;
+ 		}
  		return true;
  	}

diff --cc ext/bcmath/libbcmath/src/raise.c
index 959ba924e57,efb30f24ffc..ef39148327f
--- a/ext/bcmath/libbcmath/src/raise.c
+++ b/ext/bcmath/libbcmath/src/raise.c
@@@ -245,17 -92,22 +245,20 @@@ bc_raise_status bc_raise(bc_num base, l

  	/* Assign the value. */
  	if (is_neg) {
 -		if (bc_divide(BCG(_one_), temp, result, rscale) == false) {
 -			bc_free_num (&temp);
 +		if (bc_divide(BCG(_one_), power, result, rscale) == false) {
  			bc_free_num (&power);
 -			return false;
 +			return BC_RAISE_STATUS_DIVIDE_BY_ZERO;
  		}
 -		bc_free_num (&temp);
 +		bc_free_num (&power);
  	} else {
  		bc_free_num (result);
 -		*result = temp;
 +		*result = power;
  		(*result)->n_scale = MIN(scale, (*result)->n_scale);
+ 		if (bc_is_zero(*result)) {
+ 			(*result)->n_sign = PLUS;
+ 		}
  	}
 -	bc_free_num (&power);
 -	return true;
 +	return BC_RAISE_STATUS_OK;
  }

  /* This is used internally by BCMath */