BetterReflection
BetterReflection copied to clipboard
EvalLoader doesn't handle autoloading additional classes well
So I'm working on monkey patching some calls inside react/stream so it keeps working after prefixing fwrite and such with \ to \fwrite. @asgrim suggested I'd use better reflection for this. And loading and patching the classes works pretty well, up to the point where I want to run my unit tests. Not the classes I'm patching load another class and an interface and the EvalLoader just blows up at that point. My work can be found in this PR https://github.com/WyriHaximus/reactphp-inspector-stream/pull/1 and the exact error is:
HP Fatal error: Class 'Evenement\EventEmitter' not found in /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/Util/Autoload/ClassLoaderMethod/EvalLoader.php(25) : eval()'d code on line 3
PHP Stack trace:
PHP 1. {main}() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/phpunit:0
PHP 2. PHPUnit\TextUI\Command::main() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/phpunit:53
PHP 3. PHPUnit\TextUI\Command->run() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/TextUI/Command.php:135
PHP 4. PHPUnit\TextUI\TestRunner->doRun() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/TextUI/Command.php:206
PHP 5. PHPUnit\Framework\TestSuite->run() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:484
PHP 6. PHPUnit\Framework\TestSuite->run() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestSuite.php:739
PHP 7. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->run() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestSuite.php:739
PHP 8. PHPUnit\Framework\TestResult->run() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:867
PHP 9. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->runBare() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestResult.php:688
PHP 10. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->runTest() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:912
PHP 11. ReflectionMethod->invokeArgs() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:1053
PHP 12. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->testRerouting() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:1053
PHP 13. WyriHaximus\React\Inspector\Stream\Monky::patch() /home/travis/build/WyriHaximus/reactphp-inspector-stream/tests/MonkeyTest.php:14
PHP 14. Roave\BetterReflection\Reflector\ClassReflector->reflect() /home/travis/build/WyriHaximus/reactphp-inspector-stream/src/Monky.php:14
PHP 15. Roave\BetterReflection\SourceLocator\Type\MemoizingSourceLocator->locateIdentifier() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/Reflector/ClassReflector.php:35
PHP 16. Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator->locateIdentifier() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/MemoizingSourceLocator.php:40
PHP 17. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->locateIdentifier() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AggregateSourceLocator.php:38
PHP 18. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->createLocatedSource() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AbstractSourceLocator.php:42
PHP 19. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->attemptAutoloadForIdentifier() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:71
PHP 20. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->locateClassByName() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:91
PHP 21. class_exists() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:134
PHP 22. spl_autoload_call() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:134
PHP 23. Roave\BetterReflection\Util\Autoload\ClassLoader->__invoke() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:134
PHP 24. Roave\BetterReflection\Util\Autoload\ClassLoaderMethod\EvalLoader->__invoke() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/Util/Autoload/ClassLoader.php:58
PHP 25. eval() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/Util/Autoload/ClassLoaderMethod/EvalLoader.php:25
(Taken from: https://travis-ci.org/WyriHaximus/reactphp-inspector-stream/jobs/440604815#L636)
@WyriHaximus can you look a few stack frames deeper?
PHP chops off everything from the autoloader downwards, so it is possible that a few stack frames deeper there is an unexpected autoloader kicking in.
Also, which class is being requested in this frame?
PHP 21. class_exists() /home/travis/build/WyriHaximus/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:134
@Ocramius that's all that is outputted, but I'll dig into that later tonight and let it output a print backtrace plus get you that class it is trying to load
Yes, what I mean is that there's surely a deeper stack frame that is below PHP's internal class_exists() call
On Mon, 15 Oct 2018, 18:13 Cees-Jan Kiewiet, [email protected] wrote:
@Ocramius https://github.com/Ocramius that's all that is outputted, but I'll dig into that later tonight and let it output a print backtrace plus get you that class it is trying to load
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Roave/BetterReflection/issues/449#issuecomment-429917333, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJakEa0rutiXDKp8MpxzvWNkDlJYL5Bks5ulLQpgaJpZM4XZf6A .
Well this gets more interesting, so I prepended class_exists() with echo $className, PHP_EOL; to find out which classes get autoloaded.
This is the full out put, note the first and the last line. When reflecting a second attempt to autoload the same class, which is already monkey patched because we always load bootstrap.php at https://github.com/WyriHaximus/reactphp-inspector-stream/pull/1/files#diff-b5d0ee8c97c7abd7e3fa29b9a27d1780R29 which then monkey patches a bunch of classes: https://github.com/WyriHaximus/reactphp-inspector-stream/pull/1/files#diff-f7370275d016e437a036425d9f03ae84 Now for some reason when when attempting to reflect at https://github.com/WyriHaximus/reactphp-inspector-stream/pull/1/files#diff-48a8ed9b2290094245248b22e2de4f4cR14 it triggers the autoload which tries to autoload it again giving us the initial/current stack trace.
React\Stream\WritableResourceStream
Evenement\EventEmitter
Evenement\EventEmitterInterface
React\Stream\WritableStreamInterface
Evenement\EventEmitterTrait
React\Stream\ReadableResourceStream
Evenement\EventEmitter
Evenement\EventEmitterInterface
React\Stream\ReadableStreamInterface
Evenement\EventEmitterTrait
React\Stream\DuplexResourceStream
Evenement\EventEmitter
Evenement\EventEmitterInterface
React\Stream\DuplexStreamInterface
React\Stream\ReadableStreamInterface
React\Stream\WritableStreamInterface
Evenement\EventEmitterTrait
PHPUnit 6.5.13 by Sebastian Bergmann and contributors.
...PHP Fatal error: Class 'Evenement\EventEmitter' not found in /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/Util/Autoload/ClassLoaderMethod/EvalLoader.php(25) : eval()'d code on line 3
PHP Stack trace:
PHP 1. {main}() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/phpunit:0
PHP 2. PHPUnit\TextUI\Command::main() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/phpunit:53
PHP 3. PHPUnit\TextUI\Command->run() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/TextUI/Command.php:148
PHP 4. PHPUnit\TextUI\TestRunner->doRun() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/TextUI/Command.php:195
PHP 5. PHPUnit\Framework\TestSuite->run() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:545
PHP 6. PHPUnit\Framework\TestSuite->run() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestSuite.php:755
PHP 7. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->run() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestSuite.php:755
PHP 8. PHPUnit\Framework\TestResult->run() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:894
PHP 9. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->runBare() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestResult.php:698
PHP 10. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->runTest() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:939
PHP 11. ReflectionMethod->invokeArgs() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:1071
PHP 12. WyriHaximus\React\Tests\Inspector\Stream\MonkeyTest->testRerouting() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/phpunit/phpunit/src/Framework/TestCase.php:1071
PHP 13. WyriHaximus\React\Inspector\Stream\Monky::patch() /home/wyrihaximus/Projects/reactphp-inspector-stream/tests/MonkeyTest.php:14
PHP 14. Roave\BetterReflection\Reflector\ClassReflector->reflect() /home/wyrihaximus/Projects/reactphp-inspector-stream/src/Monky.php:14
PHP 15. Roave\BetterReflection\SourceLocator\Type\MemoizingSourceLocator->locateIdentifier() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/Reflector/ClassReflector.php:35
PHP 16. Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator->locateIdentifier() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/MemoizingSourceLocator.php:40
PHP 17. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->locateIdentifier() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AggregateSourceLocator.php:38
PHP 18. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->createLocatedSource() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AbstractSourceLocator.php:42
PHP 19. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->attemptAutoloadForIdentifier() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:71
PHP 20. Roave\BetterReflection\SourceLocator\Type\AutoloadSourceLocator->locateClassByName() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:91
PHP 21. class_exists() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:135
PHP 22. spl_autoload_call() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:135
PHP 23. Roave\BetterReflection\Util\Autoload\ClassLoader->__invoke() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/SourceLocator/Type/AutoloadSourceLocator.php:135
PHP 24. Roave\BetterReflection\Util\Autoload\ClassLoaderMethod\EvalLoader->__invoke() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/Util/Autoload/ClassLoader.php:58
PHP 25. eval() /home/wyrihaximus/Projects/reactphp-inspector-stream/vendor/roave/better-reflection/src/Util/Autoload/ClassLoaderMethod/EvalLoader.php:25
React\Stream\WritableResourceStream
What's your source locator setup?
The default
Interesting, the monkey patched classes don't show up in get_declared_classes()
Ok, so what's happening (in my opinion, not 100% sure) is following.
- class A depends on class B
- while attempting to find
A, it falls back to theAutoloadSourceLocator - the
AutoloadSourceLocatorreplaces the filesystem stream wrapper, findsA, plus a bunch of other things related to it (this is where I don't know WHAT other things). - the filesystem stream wrapper is not replaced, and hence any further autoloading fails (this is where you currently are). The
EvalLoaderkicks in, although no autoloading should've happened in first place.
In order to understand what's happening I'd need you to intercept that last autoload call, and then see how deep you are (well below stack frame 25)
Right so I put var_export(get_declared_classes()); at the end of my bootstrap.php and none of the monkey patched classes where listed. They have been patched at this point tho, simple not used.
But when putting the following just above that line they are listed, loaded, and all tests pass without any issue.
new ReadableResourceStream(STDIN, Factory::create());
new WritableResourceStream(STDOUT, Factory::create());
new DuplexResourceStream(fopen('php://memory', 'w+'), Factory::create());
In the mean time I've added the following fix: https://github.com/WyriHaximus/reactphp-inspector-stream/pull/1/commits/9964f2bc4e28d6bf63d79bd80ce8162aaf9e0d92, and while that resolves my issues, I can spent more time on this and try to figure out what is making it consider those classes not loaded. Especially looking at: https://3v4l.org/g3Wam
Note; may help to understand that the classes aren't loaded until used; the monkey patching system is an autoloader that sits on top, and indeed when you use the classes, the patched versions are loaded (hence why the classes must not be loaded beforehand, and why our autoloader must be the "last"). As soon as the class is loaded into PHP, it can't be changed (unless you use something else like runkit)...
Monkey-patching has been removed in 6.0.0: closing here.
Monkey-patching has been removed in
6.0.0: closing here.
Thanks for letting me know, completely missed that in 6.0 😅