php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #78351 Possible to check for invalid class
Submitted: 2019-07-29 18:14 UTC Modified: 2019-08-22 13:27 UTC
Votes:13
Avg. Score:4.9 ± 0.3
Reproduced:12 of 12 (100.0%)
Same Version:11 (91.7%)
Same OS:10 (83.3%)
From: php at braten dot be Assigned:
Status: Open Package: Scripting Engine problem
PHP Version: Next Minor Version OS: Irrelevant
Private report: No CVE-ID: None
 [2019-07-29 18:14 UTC] php at braten dot be
Description:
------------
It should be possible in PHP to check if a class exists or has a certain method without throwing a fatal error if the class is invalid.

This has become an issue since https://bugs.php.net/bug.php?id=76980 was fixed, and is described in more detail in https://github.com/symfony/symfony/issues/32395

This is a feature request for the next version of PHP to allow functions like get_class_methods, get_class_vars, get_parent_class, is_a, is_subclass_of, class_exists, class_implements, class_parents, trait_exists, defined, interface_exists, method_exists, property_exists, is_callable to be called without throwing a *fatal* error if a class or interface does not exist.

It can be ok if you later do "new" on the same class, that PHP must throw a fatal error. But it should be possible to get a negative result from the above mentioned functions without a fatal error.

Test script:
---------------
https://github.com/derrabus/symfony-32395-reproducer

Expected result:
----------------
Possible to catch the error and resume.


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-07-29 18:36 UTC] nicolas dot grekas+php at gmail dot com
In https://github.com/symfony/symfony/issues/32395#issuecomment-509593220, Nikita  gives us the following problematic example on 7.4:

<quote>
// A.php
class A {
    public function foo($x): B {}
}
// B.php
class B extends A {
    public function foo($x): C {}
}
// C.php
class C extends B implements DoesNotExist {
}
// main.php
new C;

What happens now is the following:

1. Autoload C. The class C is registered provisionally.
2. Autoload the parent B. The class B is registered provisionally.
3.Autoload the parent A. The class A is registered provisionally.
4. The class A is verified (trivially) and registered fully.
5. The class B is verified and registered fully (and may already be used). During the verification it makes use of the fact that C is a subclass of B, otherwise the return types would not be covariant.
6. Autoload the interface DoesNotExist, which throws an exception.

After these steps have happened ... what can we do now? We can't fully register the class C, because the interface it implements does not exist. We can't remove the provisionally registered class C either, because then a new class C that is not a subclass of B could be registered, and thus violate the variance assumption we made above.
</quote>

Then a commenter suggests:
<quote>
Would it be possible to mark a provisionally registered class as “in use” as soon as it is referenced somewhere?

This way we could determine in an exception case if it is OK to remove the provisionally registered class and gracefully recover or if we have to throw a fatal error.

In your example this would mean that once class B is verified it would mark class C as “in use”. When loading DoesNotExist throws the exception then, we would throw a fatal error because class C is “in use”.

This way we could gracefully recover for most cases while resulting in a fatal error for the impossible cases, which sounds like a perfect trade-off to me.
</quote>

Implementing that proposal, if technically possible, would be enough to have code that provided the describe feature before 7.4 continue to work in most real-life situations.

This is going to be a critical issue for Symfony at least, where this resiliency is pretty much needed.

Thanks for considering.
 [2019-08-22 13:27 UTC] php at braten dot be
More discussion about this can be found at https://github.com/symfony/symfony/issues/32995
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Dec 30 14:01:28 2024 UTC