Bug report
With the magicDirInInclude bleeding edge feature, RequireFileExistsRule resolves __DIR__ . '/file.php' in an include or require against dirname($scope->getFile()). In a trait context Scope::getFile() returns the file of the class that uses the trait, not the file the trait is declared in. At runtime __DIR__ is resolved at compile time and points at the trait file, so the result is the opposite of what PHP does.
The same applies to a relative path such as include 'file.php', where PHP falls back to the calling script's own directory. That directory is the trait file directory too.
File layout:
TraitDir/MyTrait.php trait MyTrait { public function load(): mixed { return include __DIR__ . '/data.php'; } }
TraitDir/data.php <?php return ['ok' => true];
ClassDir/MyClass.php class MyClass { use MyTrait; }
Analysing ClassDir and TraitDir with magicDirInInclude: true reports:
TraitDir/MyTrait.php (in context of class Repro\MyClass)
11 Path in include() __DIR__ . '/data.php' is not a file or it does not exist.
include.fileNotFound
Behaviour depending on where data.php is placed:
location of data.php |
PHP runtime |
PHPStan 2.2.6 |
| next to the trait |
works |
include.fileNotFound |
| next to the using class |
warning, include fails |
no error |
Version: PHPStan 2.2.6 with vendor/phpstan/phpstan/conf/bleedingEdge.neon.
There is a related problem in the type system. InitializerExprContext::fromScope() also passes Scope::getFile(), so with usePathConstantsAsConstantString: true the types of __DIR__ and __FILE__ inside a trait are the using class file. With the default setting the value is generalized to literal-string&non-falsy-string, so it is not visible in output, but the underlying value is wrong.
Related to #3019 and phpstan/phpstan-src#2043, but a different code path. That PR only changed __CLASS__ and __NAMESPACE__, which vary per using class and were widened to non constant types. __DIR__ and __FILE__ are always the trait file, so they should be resolved to the correct value instead of being widened.
Code snippet that reproduces the problem
No response
Expected output
In a trait context __DIR__ should resolve to the directory of the file the trait is declared in, and the relative include fallback should use the same directory. With the layout above no error should be reported when data.php sits next to the trait, and include.fileNotFound should be reported when it sits next to the using class.
A fix is prepared in phpstan-src, adding a trait aware file lookup in RequireFileExistsRule and in InitializerExprContext::fromScope().
Did PHPStan help you today? Did it make you happy in any way?
It’s fun to see so many people in the Japanese community mentioning PHPStan turbo.
Bug report
With the
magicDirInIncludebleeding edge feature,RequireFileExistsRuleresolves__DIR__ . '/file.php'in anincludeorrequireagainstdirname($scope->getFile()). In a trait contextScope::getFile()returns the file of the class that uses the trait, not the file the trait is declared in. At runtime__DIR__is resolved at compile time and points at the trait file, so the result is the opposite of what PHP does.The same applies to a relative path such as
include 'file.php', where PHP falls back to the calling script's own directory. That directory is the trait file directory too.File layout:
Analysing
ClassDirandTraitDirwithmagicDirInInclude: truereports:Behaviour depending on where
data.phpis placed:data.phpinclude.fileNotFoundVersion: PHPStan 2.2.6 with
vendor/phpstan/phpstan/conf/bleedingEdge.neon.There is a related problem in the type system.
InitializerExprContext::fromScope()also passesScope::getFile(), so withusePathConstantsAsConstantString: truethe types of__DIR__and__FILE__inside a trait are the using class file. With the default setting the value is generalized toliteral-string&non-falsy-string, so it is not visible in output, but the underlying value is wrong.Related to #3019 and phpstan/phpstan-src#2043, but a different code path. That PR only changed
__CLASS__and__NAMESPACE__, which vary per using class and were widened to non constant types.__DIR__and__FILE__are always the trait file, so they should be resolved to the correct value instead of being widened.Code snippet that reproduces the problem
No response
Expected output
In a trait context
__DIR__should resolve to the directory of the file the trait is declared in, and the relative include fallback should use the same directory. With the layout above no error should be reported whendata.phpsits next to the trait, andinclude.fileNotFoundshould be reported when it sits next to the using class.A fix is prepared in phpstan-src, adding a trait aware file lookup in
RequireFileExistsRuleand inInitializerExprContext::fromScope().Did PHPStan help you today? Did it make you happy in any way?
It’s fun to see so many people in the Japanese community mentioning PHPStan turbo.