]> granicus.if.org Git - php/commitdiff
Unify static/non-static check for magic methods
authorNikita Popov <nikita.ppv@gmail.com>
Mon, 20 Jul 2020 08:39:43 +0000 (10:39 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 20 Jul 2020 08:39:43 +0000 (10:39 +0200)
And promote it to be fatal.

15 files changed:
Zend/tests/bug61025.phpt
Zend/tests/bug70215.phpt
Zend/tests/errmsg_032.phpt
Zend/tests/errmsg_033.phpt
Zend/tests/errmsg_034.phpt
Zend/tests/magic_methods_003.phpt
Zend/tests/magic_methods_005.phpt
Zend/tests/magic_methods_006.phpt
Zend/tests/magic_methods_010.phpt
Zend/tests/magic_methods_serialize.phpt
Zend/tests/magic_methods_set_state.phpt
Zend/tests/magic_methods_unserialize.phpt
Zend/zend_API.c
Zend/zend_compile.c
tests/classes/__call_007.phpt [deleted file]

index 49adea85249de7ef10effcbcbc9e05831b242cd9..d453d73d26c1de6a17ab2bdb67d3bc46a482e699 100644 (file)
@@ -3,10 +3,6 @@ Bug #61025 (__invoke() visibility not honored)
 --FILE--
 <?php
 
-Interface InvokeAble {
-    static function __invoke();
-}
-
 class Bar {
     private function __invoke() {
         return __CLASS__;
@@ -20,8 +16,6 @@ echo $b->__invoke();
 
 ?>
 --EXPECTF--
-Warning: The magic method InvokeAble::__invoke() cannot be static in %sbug61025.php on line %d
-
 Warning: The magic method Bar::__invoke() must have public visibility in %sbug61025.php on line %d
 Bar
 Fatal error: Uncaught Error: Call to private method Bar::__invoke() from global scope in %s:%d
index 60fa802a7f972b2260a97ec97e2c76938b7ea938..50c1d057ca50c13bed1c416858e866b43a84b186 100644 (file)
@@ -17,5 +17,4 @@ $b();
 
 ?>
 --EXPECTF--
-Warning: The magic method A::__invoke() cannot be static in %s on line %d
-A
+Fatal error: Method A::__invoke() cannot be static in %s on line %d
index 20c2762a3220f35afe4b542b42dde49fce6ff75c..401f7073ea6dafbf728d6009efce6b8a04c058b1 100644 (file)
@@ -12,4 +12,4 @@ class test {
 echo "Done\n";
 ?>
 --EXPECTF--
-Fatal error: Constructor test::__construct() cannot be static in %s on line %d
+Fatal error: Method test::__construct() cannot be static in %s on line %d
index 72b1490ea5d0b2aeca27904a50567b4a6307500a..f71b17ce162939bad2f21a57f63d638fd19e765a 100644 (file)
@@ -12,4 +12,4 @@ class test {
 echo "Done\n";
 ?>
 --EXPECTF--
-Fatal error: Destructor test::__destruct() cannot be static in %s on line %d
+Fatal error: Method test::__destruct() cannot be static in %s on line %d
index 940754ad7b4572811c8be325d6e5438e0861435a..97a7f1c2a6efd4c22570a110a0b3c4a0b9619edf 100644 (file)
@@ -12,4 +12,4 @@ class test {
 echo "Done\n";
 ?>
 --EXPECTF--
-Fatal error: Clone method test::__clone() cannot be static in %s on line %d
+Fatal error: Method test::__clone() cannot be static in %s on line %d
index 9f7ff5e9a51c478c9b43bbfb844ea99603a3d863..d85983a4198711f419270aa7a3cec69618070f0b 100644 (file)
@@ -11,4 +11,4 @@ class foo {
 
 ?>
 --EXPECTF--
-Warning: The magic method foo::__unset() cannot be static in %s on line %d
+Fatal error: Method foo::__unset() cannot be static in %s on line %d
index fe1cfa34e484c7a4c2dbe50b2458c48ffedf46b0..cac833f0ce62a7f6d92c46b19d54add2597c9175 100644 (file)
@@ -9,4 +9,4 @@ interface a {
 
 ?>
 --EXPECTF--
-Warning: The magic method a::__call() cannot be static in %s on line %d
+Fatal error: Method a::__call() cannot be static in %s on line %d
index 00fe599fc7182df4e5213d3c1952375cb0c12c7f..b07764ec6f76a03c025221f39c9160d2821b037f 100644 (file)
@@ -9,4 +9,4 @@ interface a {
 
 ?>
 --EXPECTF--
-Warning: The magic method a::__callStatic() must be static in %s on line %d
+Fatal error: Method a::__callStatic() must be static in %s on line %d
index b93d6e19f170e5459ab6fd1e457248cf629336b5..fda1cc1e10a2226470c88c24839e216fb13d57e0 100644 (file)
@@ -12,6 +12,4 @@ class a {
 --EXPECTF--
 Warning: The magic method a::__toString() must have public visibility in %s on line %d
 
-Warning: The magic method a::__toString() cannot be static in %s on line %d
-
 Fatal error: Method a::__toString() cannot take arguments in %s on line %d
index 3cf3b5b159205f304025c2e1ae7ccc4c6166b963..2970c4f36cbf020096bd87f0226146b2c83233a6 100644 (file)
@@ -7,6 +7,4 @@ class Foo {
 }
 ?>
 --EXPECTF--
-Warning: The magic method Foo::__serialize() cannot be static in %s on line %d
-
 Fatal error: Method Foo::__serialize() cannot take arguments in %s on line %d
index 9ff728dd0556a6228b4f3cce3f430d903830ee7d..301dbb84dae77cff2bda28e74bf0b9d27b08068f 100644 (file)
@@ -11,4 +11,4 @@ class Foo {
 
 ?>
 --EXPECTF--
-Warning: The magic method Foo::__set_state() must be static in %s on line %d
+Fatal error: Method Foo::__set_state() must be static in %s on line %d
index 5feedd554df1cb4e46744abed83cc03f07aec794..b0a867c74dd4385c06fa8a9e42b8b880f75a7122 100644 (file)
@@ -7,6 +7,4 @@ class Foo {
 }
 ?>
 --EXPECTF--
-Warning: The magic method Foo::__unserialize() cannot be static in %s on line %d
-
 Fatal error: Method Foo::__unserialize() must take exactly 1 argument in %s on line %d
index f6e96df42fb1657c493329fb26ff243128c78b5c..a697c453ce264c195dbc6f6a91fb4d5da37fb7c4 100644 (file)
@@ -2033,6 +2033,22 @@ static void zend_check_magic_method_args(
        }
 }
 
+static void zend_check_magic_method_non_static(
+               const char *name, const zend_class_entry *ce, const zend_function *fptr, int error_type)
+{
+       if (fptr->common.fn_flags & ZEND_ACC_STATIC) {
+               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(ce->name), name);
+       }
+}
+
+static void zend_check_magic_method_static(
+               const char *name, const zend_class_entry *ce, const zend_function *fptr, int error_type)
+{
+       if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) {
+               zend_error(error_type, "Method %s::%s() must be static", ZSTR_VAL(ce->name), name);
+       }
+}
+
 ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, const zend_function *fptr, zend_string *lcname, int error_type) /* {{{ */
 {
        if (ZSTR_VAL(fptr->common.function_name)[0] != '_'
@@ -2040,32 +2056,49 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce,
                return;
        }
 
-       if (zend_string_equals_literal(lcname, ZEND_DESTRUCTOR_FUNC_NAME)) {
+       if (zend_string_equals_literal(lcname, ZEND_CONSTRUCTOR_FUNC_NAME)) {
+               zend_check_magic_method_non_static("__construct", ce, fptr, error_type);
+       } else if (zend_string_equals_literal(lcname, ZEND_DESTRUCTOR_FUNC_NAME)) {
                zend_check_magic_method_args(0, "__destruct", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__destruct", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_CLONE_FUNC_NAME)) {
                zend_check_magic_method_args(0, "__clone", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__clone", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_GET_FUNC_NAME)) {
                zend_check_magic_method_args(1, "__get", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__get", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_SET_FUNC_NAME)) {
                zend_check_magic_method_args(2, "__set", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__set", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_UNSET_FUNC_NAME)) {
                zend_check_magic_method_args(1, "__unset", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__unset", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_ISSET_FUNC_NAME)) {
                zend_check_magic_method_args(1, "__isset", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__isset", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_CALL_FUNC_NAME)) {
                zend_check_magic_method_args(2, "__call", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__call", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) {
                zend_check_magic_method_args(2, "__callStatic", ce, fptr, error_type);
+               zend_check_magic_method_static("__callStatic", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
                zend_check_magic_method_args(0, "__toString", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__toString", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
                zend_check_magic_method_args(0, "__debugInfo", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__debugInfo", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, "__serialize")) {
                zend_check_magic_method_args(0, "__serialize", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__serialize", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, "__unserialize")) {
                zend_check_magic_method_args(1, "__unserialize", ce, fptr, error_type);
+               zend_check_magic_method_non_static("__unserialize", ce, fptr, error_type);
        } else if (zend_string_equals_literal(lcname, "__set_state")) {
                zend_check_magic_method_args(1, "__set_state", ce, fptr, error_type);
+               zend_check_magic_method_static("__set_state", ce, fptr, error_type);
+       } else if (zend_string_equals_literal(lcname, "__invoke")) {
+               zend_check_magic_method_non_static("__invoke", ce, fptr, error_type);
        }
 }
 /* }}} */
@@ -2285,7 +2318,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
                        }
                        if (reg_function) {
                                zend_check_magic_method_implementation(
-                                       scope, reg_function, lowercase_name, error_type);
+                                       scope, reg_function, lowercase_name, E_CORE_ERROR);
                        }
                }
                ptr++;
@@ -2325,62 +2358,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
                scope->__unserialize = __unserialize;
                if (ctor) {
                        ctor->common.fn_flags |= ZEND_ACC_CTOR;
-                       if (ctor->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Constructor %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(ctor->common.function_name));
-                       }
-               }
-               if (dtor) {
-                       if (dtor->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Destructor %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(dtor->common.function_name));
-                       }
-               }
-               if (clone) {
-                       if (clone->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "%s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(clone->common.function_name));
-                       }
-               }
-               if (__call) {
-                       if (__call->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__call->common.function_name));
-                       }
                }
-               if (__callstatic) {
-                       if (!(__callstatic->common.fn_flags & ZEND_ACC_STATIC)) {
-                               zend_error(error_type, "Method %s::%s() must be static", ZSTR_VAL(scope->name), ZSTR_VAL(__callstatic->common.function_name));
-                       }
-                       __callstatic->common.fn_flags |= ZEND_ACC_STATIC;
-               }
-               if (__tostring) {
-                       if (__tostring->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__tostring->common.function_name));
-                       }
-               }
-               if (__get) {
-                       if (__get->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__get->common.function_name));
-                       }
-               }
-               if (__set) {
-                       if (__set->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__set->common.function_name));
-                       }
-               }
-               if (__unset) {
-                       if (__unset->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__unset->common.function_name));
-                       }
-               }
-               if (__isset) {
-                       if (__isset->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__isset->common.function_name));
-                       }
-               }
-               if (__debugInfo) {
-                       if (__debugInfo->common.fn_flags & ZEND_ACC_STATIC) {
-                               zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__debugInfo->common.function_name));
-                       }
-               }
-
                if (ctor && (ctor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
                        zend_error_noreturn(E_CORE_ERROR, "Constructor %s::%s() cannot declare a return type", ZSTR_VAL(scope->name), ZSTR_VAL(ctor->common.function_name));
                }
index 4529aea5ba008f014b663cb17b57083c05cca903..dbb37bbadbe6e8c4dda7a420ea69e63e0a876156 100644 (file)
@@ -6443,25 +6443,13 @@ static void zend_compile_implicit_closure_uses(closure_info *info)
        ZEND_HASH_FOREACH_END();
 }
 
-static void zend_check_magic_method_attr(uint32_t attr, zend_class_entry *ce, const char* method, zend_bool is_static) /* {{{ */
+static void zend_check_magic_method_attr(uint32_t attr, zend_class_entry *ce, const char* method) /* {{{ */
 {
        if (!(attr & ZEND_ACC_PUBLIC)) {
                zend_error(E_WARNING,
                                "The magic method %s::%s() must have public visibility",
                                ZSTR_VAL(ce->name), method);
        }
-
-       if (is_static) {
-               if (!(attr & ZEND_ACC_STATIC)) {
-                       zend_error(E_WARNING,
-                               "The magic method %s::%s() must be static",
-                               ZSTR_VAL(ce->name), method);
-               }
-       } else if (attr & ZEND_ACC_STATIC) {
-               zend_error(E_WARNING,
-                               "The magic method %s::%s() cannot be static",
-                               ZSTR_VAL(ce->name), method);
-       }
 }
 /* }}} */
 
@@ -6540,44 +6528,44 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name,
        } else if (zend_string_equals_literal(lcname, ZEND_CLONE_FUNC_NAME)) {
                ce->clone = (zend_function *) op_array;
        } else if (zend_string_equals_literal(lcname, ZEND_CALL_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__call", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__call");
                ce->__call = (zend_function *) op_array;
        } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__callStatic", 1);
+               zend_check_magic_method_attr(fn_flags, ce, "__callStatic");
                ce->__callstatic = (zend_function *) op_array;
        } else if (zend_string_equals_literal(lcname, ZEND_GET_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__get", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__get");
                ce->__get = (zend_function *) op_array;
                ce->ce_flags |= ZEND_ACC_USE_GUARDS;
        } else if (zend_string_equals_literal(lcname, ZEND_SET_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__set", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__set");
                ce->__set = (zend_function *) op_array;
                ce->ce_flags |= ZEND_ACC_USE_GUARDS;
        } else if (zend_string_equals_literal(lcname, ZEND_UNSET_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__unset", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__unset");
                ce->__unset = (zend_function *) op_array;
                ce->ce_flags |= ZEND_ACC_USE_GUARDS;
        } else if (zend_string_equals_literal(lcname, ZEND_ISSET_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__isset", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__isset");
                ce->__isset = (zend_function *) op_array;
                ce->ce_flags |= ZEND_ACC_USE_GUARDS;
        } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__toString", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__toString");
                ce->__tostring = (zend_function *) op_array;
                add_stringable_interface(ce);
        } else if (zend_string_equals_literal(lcname, ZEND_INVOKE_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__invoke", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__invoke");
        } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
-               zend_check_magic_method_attr(fn_flags, ce, "__debugInfo", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__debugInfo");
                ce->__debugInfo = (zend_function *) op_array;
        } else if (zend_string_equals_literal(lcname, "__serialize")) {
-               zend_check_magic_method_attr(fn_flags, ce, "__serialize", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__serialize");
                ce->__serialize = (zend_function *) op_array;
        } else if (zend_string_equals_literal(lcname, "__unserialize")) {
-               zend_check_magic_method_attr(fn_flags, ce, "__unserialize", 0);
+               zend_check_magic_method_attr(fn_flags, ce, "__unserialize");
                ce->__unserialize = (zend_function *) op_array;
        } else if (zend_string_equals_literal(lcname, "__set_state")) {
-        zend_check_magic_method_attr(fn_flags, ce, "__set_state", 1);
+        zend_check_magic_method_attr(fn_flags, ce, "__set_state");
        }
 
        return lcname;
@@ -7173,10 +7161,6 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) /
 
        if (ce->constructor) {
                ce->constructor->common.fn_flags |= ZEND_ACC_CTOR;
-               if (ce->constructor->common.fn_flags & ZEND_ACC_STATIC) {
-                       zend_error_noreturn(E_COMPILE_ERROR, "Constructor %s::%s() cannot be static",
-                               ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
-               }
                if (ce->constructor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
                        zend_error_noreturn(E_COMPILE_ERROR,
                                "Constructor %s::%s() cannot declare a return type",
@@ -7184,20 +7168,14 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) /
                }
        }
        if (ce->destructor) {
-               if (ce->destructor->common.fn_flags & ZEND_ACC_STATIC) {
-                       zend_error_noreturn(E_COMPILE_ERROR, "Destructor %s::%s() cannot be static",
-                               ZSTR_VAL(ce->name), ZSTR_VAL(ce->destructor->common.function_name));
-               } else if (ce->destructor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+               if (ce->destructor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
                        zend_error_noreturn(E_COMPILE_ERROR,
                                "Destructor %s::%s() cannot declare a return type",
                                ZSTR_VAL(ce->name), ZSTR_VAL(ce->destructor->common.function_name));
                }
        }
        if (ce->clone) {
-               if (ce->clone->common.fn_flags & ZEND_ACC_STATIC) {
-                       zend_error_noreturn(E_COMPILE_ERROR, "Clone method %s::%s() cannot be static",
-                               ZSTR_VAL(ce->name), ZSTR_VAL(ce->clone->common.function_name));
-               } else if (ce->clone->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+               if (ce->clone->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
                        zend_error_noreturn(E_COMPILE_ERROR,
                                "Clone method %s::%s() cannot declare a return type",
                                ZSTR_VAL(ce->name), ZSTR_VAL(ce->clone->common.function_name));
diff --git a/tests/classes/__call_007.phpt b/tests/classes/__call_007.phpt
deleted file mode 100644 (file)
index e2edb8a..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
---TEST--
-Ensure exceptions are handled properly when thrown in a statically declared __call.
---FILE--
-<?php
-class A {
-    static function __call($strMethod, $arrArgs) {
-        @var_dump($this);
-        throw new Exception;
-        echo "You should not see this";
-    }
-    function test() {
-        A::unknownCalledWithSRO(1,2,3);
-    }
-}
-
-class B extends A {
-    function test() {
-        B::unknownCalledWithSROFromChild(1,2,3);
-    }
-}
-
-$a = new A();
-
-echo "---> Invoke __call via simple method call.\n";
-try {
-    $a->unknown();
-} catch (Exception $e) {
-    echo "Exception caught OK; continuing.\n";
-}
-
-echo "\n\n---> Invoke __call via scope resolution operator within instance.\n";
-try {
-    $a->test();
-} catch (Exception $e) {
-    echo "Exception caught OK; continuing.\n";
-}
-
-echo "\n\n---> Invoke __call via scope resolution operator within child instance.\n";
-$b = new B();
-try {
-    $b->test();
-} catch (Exception $e) {
-    echo "Exception caught OK; continuing.\n";
-}
-
-echo "\n\n---> Invoke __call via callback.\n";
-try {
-    call_user_func(array($b, 'unknownCallback'), 1,2,3);
-} catch (Exception $e) {
-    echo "Exception caught OK; continuing.\n";
-}
-?>
---EXPECTF--
-Warning: The magic method A::__call() cannot be static in %s on line 3
----> Invoke __call via simple method call.
-object(A)#1 (0) {
-}
-Exception caught OK; continuing.
-
-
----> Invoke __call via scope resolution operator within instance.
-object(A)#1 (0) {
-}
-Exception caught OK; continuing.
-
-
----> Invoke __call via scope resolution operator within child instance.
-object(B)#2 (0) {
-}
-Exception caught OK; continuing.
-
-
----> Invoke __call via callback.
-object(B)#2 (0) {
-}
-Exception caught OK; continuing.