From 3310fa37367d8ad4f8553752f4af29a2300bb0b0 Mon Sep 17 00:00:00 2001 From: Damien Seguy Date: Sun, 21 Jun 2026 11:39:57 +0200 Subject: [PATCH] Zend: Make `instanceof object` equivalent to `is_object()` `$x instanceof object` was syntactically valid but always returned false because `object` is a pseudo-type with no corresponding class entry in the class table. This made the construct silently unusable. At compile time, when the right-hand side of `instanceof` is the literal `object` pseudo-type, emit `ZEND_TYPE_CHECK` with `MAY_BE_OBJECT` instead of `ZEND_INSTANCEOF`. This is the same opcode used by `is_object()`, so `$x instanceof object` now behaves identically to `is_object($x)`. The fix is entirely at compile time; no VM handler changes are required. --- NEWS | 2 + UPGRADING | 6 ++ Zend/tests/instanceof_object_pseudo_type.phpt | 58 +++++++++++++++++++ Zend/zend_compile.c | 9 +++ 4 files changed, 75 insertions(+) create mode 100644 Zend/tests/instanceof_object_pseudo_type.phpt diff --git a/NEWS b/NEWS index c7645ca27b8a..8fa4543a409a 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,8 @@ PHP NEWS ?? ??? ????, PHP 8.6.0alpha1 - Core: + . `$x instanceof object` now evaluates like `is_object($x)`, correctly + returning true for any object. (Damien Seguy) . Added first-class callable cache to share instances for the duration of the request. (ilutov) . It is now possible to use reference assign on WeakMap without the key diff --git a/UPGRADING b/UPGRADING index c77bfbfa4e02..4b306d9ef7dd 100644 --- a/UPGRADING +++ b/UPGRADING @@ -19,6 +19,10 @@ PHP 8.6 UPGRADE NOTES 1. Backward Incompatible Changes ======================================== +- Core: + . `$x instanceof object` previously always evaluated to false. It now + evaluates identically to `is_object($x)`, returning true for any object. + - DOM: . Properties previously documented as @readonly (e.g. DOMNode::$nodeType, DOMDocument::$xmlEncoding, DOMEntity::$actualEncoding, ::$encoding, @@ -181,6 +185,8 @@ PHP 8.6 UPGRADE NOTES ======================================== - Core: + . `$x instanceof object` now evaluates like `is_object($x)`, correctly + returning true for any object. . It is now possible to use reference assign on WeakMap without the key needing to be present beforehand. . It is now possible to define the `__debugInfo()` magic method on enums. diff --git a/Zend/tests/instanceof_object_pseudo_type.phpt b/Zend/tests/instanceof_object_pseudo_type.phpt new file mode 100644 index 000000000000..485e27cee7b0 --- /dev/null +++ b/Zend/tests/instanceof_object_pseudo_type.phpt @@ -0,0 +1,58 @@ +--TEST-- +instanceof with pseudo-type "object" is equivalent to is_object() +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(false) +All match is_object() +bool(true) +bool(true) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7e9f7ceac8db..2678154d63ab 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -11077,6 +11077,15 @@ static void zend_compile_instanceof(znode *result, zend_ast *ast) /* {{{ */ return; } + /* "instanceof object" is equivalent to is_object(): emit a type-check instead of a class lookup */ + if (class_ast->kind == ZEND_AST_ZVAL + && Z_TYPE_P(zend_ast_get_zval(class_ast)) == IS_STRING + && zend_string_equals_ci(zend_ast_get_str(class_ast), ZSTR_KNOWN(ZEND_STR_OBJECT))) { + opline = zend_emit_op_tmp(result, ZEND_TYPE_CHECK, &obj_node, NULL); + opline->extended_value = (1 << IS_OBJECT); + return; + } + zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_EXCEPTION | ZEND_FETCH_CLASS_SILENT);