From 20e93ac03327adfc10d6870f24abbb30427e0cdf Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Tue, 28 Jul 2026 23:57:54 +0900 Subject: [PATCH 1/2] Resolve __DIR__ in include/require against the trait file in a trait context --- src/Rules/Keywords/RequireFileExistsRule.php | 10 +++++----- .../Keywords/RequireFileExistsRuleTest.php | 13 +++++++++++++ .../data/bug-15015/class-dir/Bug15015Class.php | 10 ++++++++++ .../bug-15015/class-dir/only-in-class-dir.php | 3 +++ .../data/bug-15015/trait-dir/Bug15015Trait.php | 18 ++++++++++++++++++ .../bug-15015/trait-dir/only-in-trait-dir.php | 3 +++ 6 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 tests/PHPStan/Rules/Keywords/data/bug-15015/class-dir/Bug15015Class.php create mode 100644 tests/PHPStan/Rules/Keywords/data/bug-15015/class-dir/only-in-class-dir.php create mode 100644 tests/PHPStan/Rules/Keywords/data/bug-15015/trait-dir/Bug15015Trait.php create mode 100644 tests/PHPStan/Rules/Keywords/data/bug-15015/trait-dir/only-in-trait-dir.php diff --git a/src/Rules/Keywords/RequireFileExistsRule.php b/src/Rules/Keywords/RequireFileExistsRule.php index 77278a6d8e..6fcadd08d5 100644 --- a/src/Rules/Keywords/RequireFileExistsRule.php +++ b/src/Rules/Keywords/RequireFileExistsRule.php @@ -117,10 +117,10 @@ private function doesFileExist(string $path, Scope $scope): bool } /** - * The "calling script's own directory" fallback of a relative include is resolved - * at compile time, so inside a trait it is the directory of the file the trait is - * declared in - not the file of the class that uses it, which is what - * Scope::getFile() returns in a trait context. + * Both `__DIR__` and the "calling script's own directory" fallback of a relative + * include are resolved at compile time, so inside a trait they point at the file + * the trait is declared in - not at the file of the class that uses it, which is + * what Scope::getFile() returns in a trait context. */ private function getScopeFile(Scope $scope): string { @@ -198,7 +198,7 @@ private function resolveFilePaths(Expr $expr, Scope $scope, bool &$magicDirFallb $paths = []; foreach ($scope->getType($expr->right)->getConstantStrings() as $constantString) { - $paths[] = new ConstantStringType(dirname($scope->getFile()) . $constantString->getValue()); + $paths[] = new ConstantStringType(dirname($this->getScopeFile($scope)) . $constantString->getValue()); } return $paths; } diff --git a/tests/PHPStan/Rules/Keywords/RequireFileExistsRuleTest.php b/tests/PHPStan/Rules/Keywords/RequireFileExistsRuleTest.php index 9760d640fc..d8a882ccf2 100644 --- a/tests/PHPStan/Rules/Keywords/RequireFileExistsRuleTest.php +++ b/tests/PHPStan/Rules/Keywords/RequireFileExistsRuleTest.php @@ -171,6 +171,19 @@ public function testRelativePathInTrait(): void ]); } + public function testBug15015(): void + { + $this->analyse([ + __DIR__ . '/data/bug-15015/trait-dir/Bug15015Trait.php', + __DIR__ . '/data/bug-15015/class-dir/Bug15015Class.php', + ], [ + [ + "Path in include() __DIR__ . '/only-in-class-dir.php' is not a file or it does not exist.", + 15, + ], + ]); + } + public function testInFileExists(): void { $this->analyse([__DIR__ . '/data/include-in-file-exists.php'], []); diff --git a/tests/PHPStan/Rules/Keywords/data/bug-15015/class-dir/Bug15015Class.php b/tests/PHPStan/Rules/Keywords/data/bug-15015/class-dir/Bug15015Class.php new file mode 100644 index 0000000000..b68d353bc2 --- /dev/null +++ b/tests/PHPStan/Rules/Keywords/data/bug-15015/class-dir/Bug15015Class.php @@ -0,0 +1,10 @@ + true]; diff --git a/tests/PHPStan/Rules/Keywords/data/bug-15015/trait-dir/Bug15015Trait.php b/tests/PHPStan/Rules/Keywords/data/bug-15015/trait-dir/Bug15015Trait.php new file mode 100644 index 0000000000..461c8b292d --- /dev/null +++ b/tests/PHPStan/Rules/Keywords/data/bug-15015/trait-dir/Bug15015Trait.php @@ -0,0 +1,18 @@ + true]; From dcde2be6ca3caae0e0d4bee6bfbc9fd39ea560e7 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Tue, 28 Jul 2026 21:34:19 +0900 Subject: [PATCH 2/2] Resolve __FILE__ and __DIR__ against the trait file in a trait context --- src/Reflection/InitializerExprContext.php | 13 ++++++++++++- tests/PHPStan/Analyser/PathConstantsTest.php | 7 +++++++ .../path-constants-trait/PathConstantsTrait.php | 16 ++++++++++++++++ .../PHPStan/Analyser/data/pathConstants-win.php | 7 +++++++ tests/PHPStan/Analyser/data/pathConstants.php | 7 +++++++ 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Analyser/data/path-constants-trait/PathConstantsTrait.php diff --git a/src/Reflection/InitializerExprContext.php b/src/Reflection/InitializerExprContext.php index a148db9f51..59ae90d27b 100644 --- a/src/Reflection/InitializerExprContext.php +++ b/src/Reflection/InitializerExprContext.php @@ -42,8 +42,19 @@ public static function fromScope(Scope $scope): self { $function = $scope->getFunction(); + // __FILE__ and __DIR__ are resolved at compile time, so in a trait context they + // point at the file the trait is declared in, while Scope::getFile() returns the + // file of the class that uses the trait. + $file = $scope->getFile(); + if ($scope->isInTrait()) { + $traitFileName = $scope->getTraitReflection()->getFileName(); + if ($traitFileName !== null) { + $file = $traitFileName; + } + } + return new self( - $scope->getFile(), + $file, $scope->getNamespace(), $scope->isInClass() ? $scope->getClassReflection()->getName() : null, $scope->isInTrait() ? $scope->getTraitReflection()->getName() : null, diff --git a/tests/PHPStan/Analyser/PathConstantsTest.php b/tests/PHPStan/Analyser/PathConstantsTest.php index 3a3e124e58..6ce2116d88 100644 --- a/tests/PHPStan/Analyser/PathConstantsTest.php +++ b/tests/PHPStan/Analyser/PathConstantsTest.php @@ -38,4 +38,11 @@ public static function getAdditionalConfigFiles(): array ]; } + protected static function getAdditionalAnalysedFiles(): array + { + return [ + __DIR__ . '/data/path-constants-trait/PathConstantsTrait.php', + ]; + } + } diff --git a/tests/PHPStan/Analyser/data/path-constants-trait/PathConstantsTrait.php b/tests/PHPStan/Analyser/data/path-constants-trait/PathConstantsTrait.php new file mode 100644 index 0000000000..eabaa36bc2 --- /dev/null +++ b/tests/PHPStan/Analyser/data/path-constants-trait/PathConstantsTrait.php @@ -0,0 +1,16 @@ +