Repository: Roave/BetterReflection (PHP)

Issue:
`PhpStormStubsSourceStubber` calls into application autoloader
while debugging today, I realized that when BetterReflection is building up its reflection knowledge based on phpstorm stubs, it is invoking application autoloaders.

I found stacktraces like

![grafik](https://github.com/Roave/BetterReflection/assets/120441/76a66529-c6e9-4631-b98f-7c6094630a99)

in which you can see the top-level frame `Rocket::autoload`. thats my application autoloader which is registered via phpstan bootstrap file.

my questions:
- should BetterReflection trigger autoloading while analyzing phpstorm stubs?
- if autoloading is really needed, should better reflection isolate the autoloading involved from side-effects like a application level autoloader?

I came to this question, because our application autoloading needs todo some gymnastics and it is invoked a lot for all kind of classes which `BetterReflection` is locating while parsing stub files.

looking deeper it might even be the case this only happens from within PHPStan.. not sure yet


Discussion:
> should BetterReflection trigger autoloading while analyzing phpstorm stubs?
> if autoloading is really needed, should better reflection isolate the autoloading involved from side-effects like a application level autoloader?

The autoload is not intentional. It looks it's triggered by `defined`. 

We use `updateConstantValue` because values in stubs are not actual or are different on different PHP versions or operating systems.
> It looks it's triggered by `defined`.

I wasn't aware `defined` can trigger autoloading, but you are right:

https://3v4l.org/pMmLA
We can probably use `get_defined_constants`: https://3v4l.org/r3O9o
@kukulich I found a way on how it could work, please have  a look: https://github.com/Roave/BetterReflection/pull/1416
@staabm I think it's very nice solution 👍 




Patch:
diff --git a/src/SourceLocator/SourceStubber/PhpStormStubs/CachingVisitor.php b/src/SourceLocator/SourceStubber/PhpStormStubs/CachingVisitor.php
index 4cbe3b094..ef9a2d8a2 100644
--- a/src/SourceLocator/SourceStubber/PhpStormStubs/CachingVisitor.php
+++ b/src/SourceLocator/SourceStubber/PhpStormStubs/CachingVisitor.php
@@ -13,8 +13,11 @@
 
 use function array_key_exists;
 use function assert;
+use function class_exists;
 use function constant;
+use function count;
 use function defined;
+use function explode;
 use function in_array;
 use function is_resource;
 use function sprintf;
@@ -195,6 +198,16 @@ public function clearNodes(): void
      */
     private function updateConstantValue(Node\Expr\FuncCall|Node\Const_ $node, string $constantName): void
     {
+        // prevent autoloading while discovering class constants
+        $parts = explode('::', $constantName, 2);
+        if (count($parts) === 2) {
+            [$className, $classConstName] = $parts;
+
+            if (! class_exists($className, false)) {
+                return;
+            }
+        }
+
         if (! defined($constantName)) {
             return;
         }


Test patch:
diff --git a/test/unit/SourceLocator/SourceStubber/PhpStormStubsSourceStubberTest.php b/test/unit/SourceLocator/SourceStubber/PhpStormStubsSourceStubberTest.php
index 68498b083..37ae5fb39 100644
--- a/test/unit/SourceLocator/SourceStubber/PhpStormStubsSourceStubberTest.php
+++ b/test/unit/SourceLocator/SourceStubber/PhpStormStubsSourceStubberTest.php
@@ -62,6 +62,7 @@
 use function get_defined_functions;
 use function in_array;
 use function sort;
+use function spl_autoload_register;
 use function sprintf;
 
 use const PHP_VERSION_ID;
@@ -626,6 +627,19 @@ public function testCaseSensitiveConstantSearchOptimization(): void
         self::assertNull($this->sourceStubber->generateConstantStub('date_atom'));
     }
 
+    #[RunInSeparateProcess]
+    public function testUpdateConstantValueDoesNotTriggerAutoload(): void
+    {
+        spl_autoload_register(static function (string $className): void {
+            self::fail('Parsing php-src constant should not trigger userland autoloading');
+        });
+
+        $sourceStubber     = new PhpStormStubsSourceStubber(BetterReflectionSingleton::instance()->phpParser());
+        $constConstantStub = $sourceStubber->generateConstantStub('JSON_PRETTY_PRINT');
+        self::assertNotNull($constConstantStub);
+        self::assertStringContainsString("define('JSON_PRETTY_PRINT',", $constConstantStub->getStub());
+    }
+
     #[RunInSeparateProcess]
     public function testUpdateConstantValue(): void
     {