mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Fix enum to bool comparison
The compiler compiles $value == true to ZEND_BOOL, which always returns true for objects (with the default cast_object handler). However, when compared to a statically unknown rhs $value == $true, the resulting opcode ZEND_IS_EQUAL would call the objects compare handler. The zend_objects_not_comparable() handler, which is installed for enums and other internal classes, blanketly returns false. This does not match the ZEND_BOOL semantics. Object to boolean comparison is now handled directly in zend_compare(), analogous to object to null comparison. It continuous to call the cast_object handler, but guarantees consistent behavior across ZEND_BOOL and ZEND_IS_EQUAL. Fixes GH-16954 Closes GH-17031
This commit is contained in:
@@ -28,6 +28,12 @@ PHP 8.5 UPGRADE NOTES
|
||||
- Core:
|
||||
. It is no longer possible to use "array" and "callable" as class alias names
|
||||
in class_alias().
|
||||
. Loosely comparing uncomparable objects (e.g. enums, \CurlHandle and other
|
||||
internal classes) to booleans was previously inconsistent. If compared to a
|
||||
boolean literal $object == true, it would behave the same way as (bool)
|
||||
$object. If compared to a statically unknown value $object == $true, it
|
||||
would always return false. This behavior was consolidated to always follow
|
||||
the behavior of (bool) $object.
|
||||
|
||||
- Intl:
|
||||
. The extension now requires at least ICU 57.1.
|
||||
|
||||
@@ -53,5 +53,5 @@ bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(true)
|
||||
|
||||
50
Zend/tests/gh16954.phpt
Normal file
50
Zend/tests/gh16954.phpt
Normal file
@@ -0,0 +1,50 @@
|
||||
--TEST--
|
||||
GH-16954: Enum to bool comparison is inconsistent
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
enum E {
|
||||
case C;
|
||||
}
|
||||
|
||||
$true = true;
|
||||
$false = false;
|
||||
|
||||
var_dump(E::C == true);
|
||||
var_dump(E::C == $true);
|
||||
var_dump(true == E::C);
|
||||
var_dump($true == E::C);
|
||||
|
||||
var_dump(E::C != true);
|
||||
var_dump(E::C != $true);
|
||||
var_dump(true != E::C);
|
||||
var_dump($true != E::C);
|
||||
|
||||
var_dump(E::C == false);
|
||||
var_dump(E::C == $false);
|
||||
var_dump(false == E::C);
|
||||
var_dump($false == E::C);
|
||||
|
||||
var_dump(E::C != false);
|
||||
var_dump(E::C != $false);
|
||||
var_dump(false != E::C);
|
||||
var_dump($false != E::C);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -2063,8 +2063,9 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
|
||||
object_lhs = false;
|
||||
}
|
||||
ZEND_ASSERT(Z_TYPE_P(value) != IS_OBJECT);
|
||||
uint8_t target_type = (Z_TYPE_P(value) == IS_FALSE || Z_TYPE_P(value) == IS_TRUE)
|
||||
? _IS_BOOL : Z_TYPE_P(value);
|
||||
uint8_t target_type = Z_TYPE_P(value);
|
||||
/* Should be handled in zend_compare(). */
|
||||
ZEND_ASSERT(target_type != IS_FALSE && target_type != IS_TRUE);
|
||||
if (Z_OBJ_HT_P(object)->cast_object(Z_OBJ_P(object), &casted, target_type) == FAILURE) {
|
||||
// TODO: Less crazy.
|
||||
if (target_type == IS_LONG || target_type == IS_DOUBLE) {
|
||||
|
||||
@@ -2333,13 +2333,29 @@ ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2) /* {{{ */
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(op1) == IS_OBJECT
|
||||
&& Z_TYPE_P(op2) == IS_OBJECT
|
||||
&& Z_OBJ_P(op1) == Z_OBJ_P(op2)) {
|
||||
return 0;
|
||||
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
|
||||
return Z_OBJ_HANDLER_P(op1, compare)(op1, op2);
|
||||
} else if (Z_TYPE_P(op2) == IS_OBJECT) {
|
||||
return Z_OBJ_HANDLER_P(op2, compare)(op1, op2);
|
||||
|| Z_TYPE_P(op2) == IS_OBJECT) {
|
||||
zval *object, *other;
|
||||
if (Z_TYPE_P(op1) == IS_OBJECT) {
|
||||
object = op1;
|
||||
other = op2;
|
||||
} else {
|
||||
object = op2;
|
||||
other = op1;
|
||||
}
|
||||
if (EXPECTED(Z_TYPE_P(other) == IS_OBJECT)) {
|
||||
if (Z_OBJ_P(object) == Z_OBJ_P(other)) {
|
||||
return 0;
|
||||
}
|
||||
} else if (Z_TYPE_P(other) == IS_TRUE || Z_TYPE_P(other) == IS_FALSE) {
|
||||
zval casted;
|
||||
if (Z_OBJ_HANDLER_P(object, cast_object)(Z_OBJ_P(object), &casted, _IS_BOOL) == FAILURE) {
|
||||
return object == op1 ? 1 : -1;
|
||||
}
|
||||
int ret = object == op1 ? zend_compare(&casted, other) : zend_compare(other, &casted);
|
||||
ZEND_ASSERT(!Z_REFCOUNTED_P(&casted));
|
||||
return ret;
|
||||
}
|
||||
return Z_OBJ_HANDLER_P(object, compare)(op1, op2);
|
||||
}
|
||||
|
||||
if (!converted) {
|
||||
|
||||
Reference in New Issue
Block a user