Skip to content

Always resolve __DIR__ and __FILE__ to constant strings instead of generalizing them#5711

Open
phpstan-bot wants to merge 1 commit into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-tl4ae02
Open

Always resolve __DIR__ and __FILE__ to constant strings instead of generalizing them#5711
phpstan-bot wants to merge 1 commit into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-tl4ae02

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

require __DIR__ . '/path.php' and similar constructs did not report fileNotFound errors because __DIR__ and __FILE__ were generalized from ConstantStringType to non-empty-string by default. This meant RequireFileExistsRule could not resolve the actual file path. The undocumented usePathConstantsAsConstantString: true config option was required to get the correct behavior.

This PR makes __DIR__ and __FILE__ always resolve to their actual constant string values, removing the need for the config option entirely.

Changes

  • src/Reflection/InitializerExprTypeResolver.php: Remove usePathConstantsAsConstantString constructor parameter and its #[AutowiredParameter] attribute. Always return ConstantStringType for __DIR__ and __FILE__ magic constants instead of conditionally generalizing. Remove unused GeneralizePrecision and AutowiredParameter imports.
  • src/DependencyInjection/ValidateIgnoredErrorsExtension.php: Remove the 7th argument (true) passed to InitializerExprTypeResolver constructor.
  • src/Testing/PHPStanTestCase.php: Remove $container->getParameter('usePathConstantsAsConstantString') argument from InitializerExprTypeResolver construction.
  • tests/PHPStan/Rules/Keywords/RequireFileExistsRuleTest.php: Remove getAdditionalConfigFiles() that loaded usePathConstantsAsConstantString.neon — no longer needed.
  • tests/PHPStan/Analyser/PathConstantsTest.php: Remove getAdditionalConfigFiles() for the same reason.
  • tests/PHPStan/Analyser/usePathConstantsAsConstantString.neon: Deleted — no longer referenced.
  • tests/PHPStan/Analyser/nsrt/binary.php: Updated __DIR__/__FILE__ type assertions from literal-string&non-falsy-string to use substr() for machine-independent constant string checks.
  • tests/PHPStan/Analyser/Fiber/data/fnsr.php: Same update for __DIR__/__FILE__ assertions.
  • tests/PHPStan/Reflection/BetterReflection/SourceLocator/OptimizedSingleFileSourceLocatorTest.php: Updated expected type for const_with_dir_const from generalized type to the actual constant string path.

Root cause

In InitializerExprTypeResolver::getType(), when processing __DIR__ (Dir) and __FILE__ (File) AST nodes, the code created a ConstantStringType with the actual path but then called ->generalize(GeneralizePrecision::moreSpecific()) when usePathConstantsAsConstantString was false (the default). This turned the precise constant type into non-empty-string, losing the actual path information.

RequireFileExistsRule::resolveFilePaths() calls $type->getConstantStrings() on the include expression's type. With a generalized non-empty-string, getConstantStrings() returns an empty array, so the rule silently skips file existence checking for any path built using __DIR__ or __FILE__.

The fix simply always returns the ConstantStringType without generalizing.

Analogous cases probed

  • dirname(__DIR__): Still resolves to non-empty-string because dirname() has a stub that maps non-empty-string -> non-empty-string, not a dynamic return type extension that constant-folds. This is a separate enhancement opportunity, not the same bug.
  • Other magic constants (__LINE__, __CLASS__, __FUNCTION__, __METHOD__, __TRAIT__, __NAMESPACE__): Already resolve to constant types and are not affected by this change.
  • __FILE__: Fixed in the same change as __DIR__ — same code path.

Test

The existing testBug12203 regression test in RequireFileExistsRuleTest now works without the usePathConstantsAsConstantString config option, proving the fix. Additional test updates ensure the new constant string types are correctly asserted throughout the test suite.

Fixes phpstan/phpstan#12203

…f generalizing them

- Remove `usePathConstantsAsConstantString` parameter from `InitializerExprTypeResolver` constructor
- `__DIR__` and `__FILE__` now always resolve to `ConstantStringType` with the actual path, instead of being generalized to `non-empty-string` when the (undocumented) `usePathConstantsAsConstantString` option was `false`
- This makes `RequireFileExistsRule` correctly report missing files in `require __DIR__ . '/path.php'` without needing any config
- Remove the extra constructor argument from `ValidateIgnoredErrorsExtension` and `PHPStanTestCase`
- Update tests that asserted the old generalized type (`literal-string&non-falsy-string`) to use `substr()` for machine-independent assertions
- The `usePathConstantsAsConstantString` config parameter is kept in the schema for backward compatibility but is now a no-op
@ondrejmirtes
Copy link
Copy Markdown
Member

How can you resolve these to constant values when the code is usually executed on multiple machines from multiple paths?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants