From f9a579ba79b60d4c30ff8dafa303457c0da43407 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Wed, 12 Aug 2026 12:14:26 -0700 Subject: [PATCH] [RFC] Deprecate `is_a()` and `is_subclass_of()` with string when not allowed For `is_a()` and `is_subclass_of()`, emit deprecation warnings when calling with a string as the first argument but with `$allow_string` being false. The two functions share implementation code and so are updated together. https://wiki.php.net/rfc/deprecations_php_8_6 --- UPGRADING | 5 + Zend/tests/is_a_string.phpt | 16 ++ Zend/tests/is_subclass_of_string.phpt | 18 ++ Zend/zend_builtin_functions.c | 12 ++ ext/standard/tests/class_object/is_a.phpt | 162 +++++++++++++++++- .../class_object/is_a_variation_001.phpt | 8 + 6 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/is_a_string.phpt create mode 100644 Zend/tests/is_subclass_of_string.phpt diff --git a/UPGRADING b/UPGRADING index a4cc44f9a004..4dcc497fe29a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -454,6 +454,11 @@ PHP 8.6 UPGRADE NOTES RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_the_possibility_to_name_a_function_readonly . Passing a 3rd argument to define() is now deprecated. RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_define_with_case_insensitive_being_specified + . Calling is_subclass_of() with a string when $allow_string is false is now + deprecated. + RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_is_subclass_of_with_string_when_allow_string_is_false + . Calling is_a() with a string when $allow_string is false is now deprecated. + RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_is_a_with_string_when_allow_string_is_false - BZ2 . Passing an object for the Bzip2 {de}compression stream filter is now diff --git a/Zend/tests/is_a_string.phpt b/Zend/tests/is_a_string.phpt new file mode 100644 index 000000000000..3334cfb1596d --- /dev/null +++ b/Zend/tests/is_a_string.phpt @@ -0,0 +1,16 @@ +--TEST-- +is_a() with $allow_string false and a string +--FILE-- + +--EXPECTF-- +bool(true) + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d +bool(false) diff --git a/Zend/tests/is_subclass_of_string.phpt b/Zend/tests/is_subclass_of_string.phpt new file mode 100644 index 000000000000..5ef3407b1dca --- /dev/null +++ b/Zend/tests/is_subclass_of_string.phpt @@ -0,0 +1,18 @@ +--TEST-- +is_subclass_of() with $allow_string false and a string +--FILE-- + +--EXPECTF-- +bool(true) + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d +bool(false) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 6aa47abd2c6b..e383c8f83540 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -714,6 +714,18 @@ static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ * if (!instance_ce) { RETURN_FALSE; } + } else if (Z_TYPE_P(obj) == IS_STRING) { + zend_string *function_name = get_active_function_or_method_name(); + zend_error( + E_DEPRECATED, + "Calling %pS() with a string when $allow_string is false", + function_name + ); + zend_string_release(function_name); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + RETURN_FALSE; } else if (Z_TYPE_P(obj) == IS_OBJECT) { instance_ce = Z_OBJCE_P(obj); } else { diff --git a/ext/standard/tests/class_object/is_a.phpt b/ext/standard/tests/class_object/is_a.phpt index efb03b343492..ee6f30455a60 100644 --- a/ext/standard/tests/class_object/is_a.phpt +++ b/ext/standard/tests/class_object/is_a.phpt @@ -81,109 +81,173 @@ $t->test(); ?> ---EXPECT-- +--EXPECTF-- >>> With Defined class is_a( OBJECT:base, base) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, base) = no is_a( STRING:base, base, true) = yes is_subclass_of( OBJECT:base, base) = no is_subclass_of( STRING:base, base) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, base,false) = no >>> With Undefined is_a( STRING:undefB, base,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, base) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, base,false) = no is_subclass_of( STRING:undefB, base) = no >>> With Defined class is_a( OBJECT:base, derived_a) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, derived_a) = no is_a( STRING:base, derived_a, true) = no is_subclass_of( OBJECT:base, derived_a) = no is_subclass_of( STRING:base, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, derived_a,false) = no >>> With Undefined is_a( STRING:undefB, derived_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, derived_a,false) = no is_subclass_of( STRING:undefB, derived_a) = no >>> With Defined class is_a( OBJECT:base, if_a) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, if_a) = no is_a( STRING:base, if_a, true) = no is_subclass_of( OBJECT:base, if_a) = no is_subclass_of( STRING:base, if_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, if_a,false) = no >>> With Undefined is_a( STRING:undefB, if_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, if_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, if_a,false) = no is_subclass_of( STRING:undefB, if_a) = no >>> With Defined class is_a( OBJECT:base, undefA) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, undefA) = no is_a( STRING:base, undefA, true) = no is_subclass_of( OBJECT:base, undefA) = no is_subclass_of( STRING:base, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, undefA,false) = no >>> With Undefined is_a( STRING:undefB, undefA,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, undefA,false) = no is_subclass_of( STRING:undefB, undefA) = no >>> With Defined class is_a( OBJECT:derived_a, base) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, base) = no is_a( STRING:derived_a, base, true) = yes is_subclass_of( OBJECT:derived_a, base) = yes is_subclass_of( STRING:derived_a, base) = yes + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, base,false) = no >>> With Undefined is_a( STRING:undefB, base,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, base) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, base,false) = no is_subclass_of( STRING:undefB, base) = no >>> With Defined class is_a( OBJECT:derived_a, derived_a) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, derived_a) = no is_a( STRING:derived_a, derived_a, true) = yes is_subclass_of( OBJECT:derived_a, derived_a) = no is_subclass_of( STRING:derived_a, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, derived_a,false) = no >>> With Undefined is_a( STRING:undefB, derived_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, derived_a,false) = no is_subclass_of( STRING:undefB, derived_a) = no >>> With Defined class is_a( OBJECT:derived_a, if_a) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, if_a) = no is_a( STRING:derived_a, if_a, true) = yes is_subclass_of( OBJECT:derived_a, if_a) = yes is_subclass_of( STRING:derived_a, if_a) = yes + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, if_a,false) = no >>> With Undefined is_a( STRING:undefB, if_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, if_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, if_a,false) = no is_subclass_of( STRING:undefB, if_a) = no >>> With Defined class is_a( OBJECT:derived_a, undefA) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, undefA) = no is_a( STRING:derived_a, undefA, true) = no is_subclass_of( OBJECT:derived_a, undefA) = no is_subclass_of( STRING:derived_a, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, undefA,false) = no >>> With Undefined is_a( STRING:undefB, undefA,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, undefA,false) = no is_subclass_of( STRING:undefB, undefA) = no @@ -192,60 +256,92 @@ NOW WITH AUTOLOAD >>> With Defined class is_a( OBJECT:base, base) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, base) = no is_a( STRING:base, base, true) = yes is_subclass_of( OBJECT:base, base) = no is_subclass_of( STRING:base, base) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, base,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, base,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, base) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, base,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, base) = no >>> With Defined class is_a( OBJECT:base, derived_a) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, derived_a) = no is_a( STRING:base, derived_a, true) = no is_subclass_of( OBJECT:base, derived_a) = no is_subclass_of( STRING:base, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, derived_a,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, derived_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, derived_a,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, derived_a) = no >>> With Defined class is_a( OBJECT:base, if_a) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, if_a) = no is_a( STRING:base, if_a, true) = no is_subclass_of( OBJECT:base, if_a) = no is_subclass_of( STRING:base, if_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, if_a,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, if_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, if_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, if_a,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, if_a) = no >>> With Defined class is_a( OBJECT:base, undefA) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:base, undefA) = no is_a( STRING:base, undefA, true) = no is_subclass_of( OBJECT:base, undefA) = no is_subclass_of( STRING:base, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:base, undefA,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, undefA,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, undefA,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, undefA) = no @@ -253,60 +349,92 @@ is_subclass_of( STRING:undefB, undefA) = no >>> With Defined class is_a( OBJECT:derived_a, base) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, base) = no is_a( STRING:derived_a, base, true) = yes is_subclass_of( OBJECT:derived_a, base) = yes is_subclass_of( STRING:derived_a, base) = yes + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, base,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, base,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, base) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, base,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, base) = no >>> With Defined class is_a( OBJECT:derived_a, derived_a) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, derived_a) = no is_a( STRING:derived_a, derived_a, true) = yes is_subclass_of( OBJECT:derived_a, derived_a) = no is_subclass_of( STRING:derived_a, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, derived_a,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, derived_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, derived_a,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, derived_a) = no >>> With Defined class is_a( OBJECT:derived_a, if_a) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, if_a) = no is_a( STRING:derived_a, if_a, true) = yes is_subclass_of( OBJECT:derived_a, if_a) = yes is_subclass_of( STRING:derived_a, if_a) = yes + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, if_a,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, if_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, if_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, if_a,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, if_a) = no >>> With Defined class is_a( OBJECT:derived_a, undefA) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_a, undefA) = no is_a( STRING:derived_a, undefA, true) = no is_subclass_of( OBJECT:derived_a, undefA) = no is_subclass_of( STRING:derived_a, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_a, undefA,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, undefA,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, undefA,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, undefA) = no @@ -314,60 +442,92 @@ is_subclass_of( STRING:undefB, undefA) = no >>> With Defined class is_a( OBJECT:derived_b, base) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_b, base) = no is_a( STRING:derived_b, base, true) = yes is_subclass_of( OBJECT:derived_b, base) = yes is_subclass_of( STRING:derived_b, base) = yes + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_b, base,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, base,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, base) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, base,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, base) = no >>> With Defined class is_a( OBJECT:derived_b, derived_a) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_b, derived_a) = no is_a( STRING:derived_b, derived_a, true) = no is_subclass_of( OBJECT:derived_b, derived_a) = no is_subclass_of( STRING:derived_b, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_b, derived_a,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, derived_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, derived_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, derived_a,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, derived_a) = no >>> With Defined class is_a( OBJECT:derived_b, if_a) = yes + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_b, if_a) = no is_a( STRING:derived_b, if_a, true) = yes is_subclass_of( OBJECT:derived_b, if_a) = yes is_subclass_of( STRING:derived_b, if_a) = yes + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_b, if_a,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, if_a,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, if_a) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, if_a,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, if_a) = no >>> With Defined class is_a( OBJECT:derived_b, undefA) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:derived_b, undefA) = no is_a( STRING:derived_b, undefA, true) = no is_subclass_of( OBJECT:derived_b, undefA) = no is_subclass_of( STRING:derived_b, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:derived_b, undefA,false) = no >>> With Undefined >>>> In autoload: string(6) "undefB" is_a( STRING:undefB, undefA,true) = no + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d is_a( STRING:undefB, undefA) = no + +Deprecated: Calling is_subclass_of() with a string when $allow_string is false in %s on line %d is_subclass_of( STRING:undefB, undefA,false) = no >>>> In autoload: string(6) "undefB" is_subclass_of( STRING:undefB, undefA) = no diff --git a/ext/standard/tests/class_object/is_a_variation_001.phpt b/ext/standard/tests/class_object/is_a_variation_001.phpt index 4f77b9735a7e..374007da7b1d 100644 --- a/ext/standard/tests/class_object/is_a_variation_001.phpt +++ b/ext/standard/tests/class_object/is_a_variation_001.phpt @@ -137,15 +137,23 @@ Arg value bool(false) Arg value + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d bool(false) Arg value + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d bool(false) Arg value string + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d bool(false) Arg value String + +Deprecated: Calling is_a() with a string when $allow_string is false in %s on line %d bool(false) Arg value