🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

__DIR__ in include inside a trait is resolved against the using class directory #15015

Description

@zonuexe

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions