Skip to content

[PoC] Broker: Handle fatal errors when autoloading unknown classes/interfaces/traits #255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

Majkl578
Copy link
Contributor

@Majkl578 Majkl578 commented May 5, 2017

Possible fix for #168 & #159. This could be completely wrong or broken.


Note that this doesn't handle other fatal errors, like:

class Foo extends Foo {}

or

use Foo\Bar;
class Bar {}

Those would have to be checked on different level (parser?). This targets solely autoload failures.

@ondrejmirtes
Copy link
Member

I really don't get it - how is it supposed to work?

@Majkl578
Copy link
Contributor Author

Majkl578 commented May 6, 2017

Imagine you have a class Foo that extends class Bar. Class Foo exists and is known to at least one autoloader (i.e. Composer), but Bar does not.

<?php
class Foo extends Bar {}

Now, what happens when we attempt to instantiate Foo in our code:

  1. PHP tries to autoload Foo
  2. Composer's autoloader knows the location of Foo, so it includes it
  3. now that Composer properly included Foo, no further autoloaders are executed, but PHP needs to compile the class
  4. PHP checks whether class extends/implements/uses anything else and if so, it'll have to autoload these first
  5. PHP noticed Foo extends Bar, but Bar doesn't exist yet, so it first needs to register Bar
  6. PHP tries to autoload Bar
  7. Composer's autoloader doesn't know the location of Bar, so it does nothing
  8. no autoloader was able to load Bar, so PHP still doesn't know Bar
  9. PHP triggers fatal error because Bar could not be found and no autoloader was able to provide it

What this PR does is:

  • it injects a fallback autoloader to the stack
  • this fallback autoloader will only be triggered when no previous autoloader was able to autoload the class (in the above, between 8. and 9.)
  • it does what other proper autoloaders should never do - throws an exception for unknown class
  • by throwing an exception, it prevents PHP to escalate unknown class to fatal error

@fprochazka
Copy link
Contributor

fprochazka commented May 6, 2017

Looks like a good idea. I'll try it on symfony.

FYI, last time it died on 6 or 7 files, and I had to "fix" them manually for PHPStan to be able to complete the analysis

Fatal error: Class 'Symfony\Bug\NotExistClass' not found in src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php on line 16

@Majkl578
Copy link
Contributor Author

Majkl578 commented May 6, 2017

Symfony will fail because of other fatal errors, like class extending itself. You should probably exclude such files?

@fprochazka
Copy link
Contributor

fprochazka commented May 6, 2017

There is still this problem with Symfony, but everything else passed!

 2718/3608 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░]  75%
Fatal error: Class 'Symfony\Component\VarDumper\Tests\Fixtures\NotLoadableClass' not found in src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php on line 5

because of (already mentioned)

namespace Symfony\Component\VarDumper\Tests\Fixtures;

class NotLoadableClass extends NotLoadableClass
{
}

with master, it dies here already

 2034/3608 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░]  56%
Fatal error: Class 'Symfony\Bug\NotExistClass' not found in src/Symfony/Component/DependencyInjection/Tests/Compiler/OptionalServiceClass.php on line 16

@fprochazka
Copy link
Contributor

fprochazka commented May 6, 2017

Btw, I haven't found a way to exclude a file from beeing loaded by PHPStan, only how to exclude it from the results. Guessing this is not implemented yet?

@Majkl578 Majkl578 force-pushed the broker-handle-unknown-classes branch from ecafda3 to 0d0cb2b Compare May 6, 2017 16:54
@ondrejmirtes
Copy link
Member

Btw, I haven't found a way to exclude a file from beeing loaded by PHPStan, only how to exclude it from the results. Guessing this is not implemented yet?

@fprochazka There's a way how to exclude a file from analysis using excludes_analyse. If your analysis still fails, it means that some of your not excluded files depend on a file that is broken. Also, specified directories determine whether a referenced trait is considered as part of the codebase and should be analysed as well.

@ondrejmirtes
Copy link
Member

I now understand @Majkl578's solution, but as @fprochazka pointed out, it only helps with a narrow issue of a referenced class not being found. It doesn't help with a lot of other issues, as non-abstract class having abstract methods, extending an interface in a class etc... Maybe a better approach (and all fatal errors encompassing) would be to implement a custom error handler that, although still exiting PHPStan prematurely, would at least clean the memory limit file to prevent the confusing message on the next run?

@kaloyan-raev
Copy link

I successfully tested this PR to prevent phpstan from dying when PHP Fatal Error for class not found occurs.

It would be great if phpstan is tolerant to any PHP Fatal Errors and turn them to validation error in the report instead of dying. Note that most (if not all) real-life project that had not used a static code analyzer yet are far from perfect. Thus analyzing their code would produce lots of fatal errors. It is really discouraging to spend hours, if not days, in excluding files from analysis in order to get some initial report.

@ondrejmirtes
Copy link
Member

OK, good to hear that. I'm considering to merge this but I'd really like a test case demonstrating this behaviour.

@Majkl578 Can you try to add it in AnalyserIntegrationTest? Thanks.

@ondrejmirtes
Copy link
Member

Merged with added testcase: 95af8d9

Thanks!

@Majkl578
Copy link
Contributor Author

Cool! And sorry, didn't get to it earlier. :/

@Majkl578 Majkl578 deleted the broker-handle-unknown-classes branch July 23, 2017 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants