2

I'm writing a custom PHPStan rule that suggests marking a class as readonly (introduced in PHP 8.2) when it meets all the necessary conditions.

One of the requirements for safely applying readonly is ensuring that the class has no child classes, because in PHP 8.2, if a parent is readonly, all its child classes must be readonly too.

The problem is, PHPStan doesn't provide a simple, reliable way to check if a class is extended within the project during static analysis.

I've considered the following approaches:

  • Using Composer's autoload_classmap.php, but with PSR-4 autoloading, it often doesn't contain project classes.
  • Forcing composer dump-autoload -o --classmap-authoritative, which solves the problem but isn't always acceptable for development setups.
  • Writing a custom file scanner that parses PHP files to extract namespace and class names to build a class map (either via simple regex or using nikic/php-parser).

Question: What is the recommended or common approach to reliably detect child classes in a PHPStan custom rule, considering static analysis only? Are there any best practices for this scenario, or do people generally rely on their own project-specific class scanning logic?

1
  • edit You WILL need to load every file in the project and scan them and store them in a a reference file, so you only have to do the scan once everytime a new class starts using it. [/edit] Sorry, didn't read your question correctly. Commented Jun 23 at 8:59

1 Answer 1

5

This is what Collectors are great for - you can collect data when the whole project is analysed and then evaluate them in a single rule invoked at the end of the analysis.

Learn more: https://phpstan.org/developing-extensions/collectors

Some great community packages are implemented thanks to Collectors, like https://github.com/shipmonk-rnd/dead-code-detector.

The way I’d go about implementing this would be to introduce two Collectors and one CollectedDataNode rule.

  1. Collector for InClassNode collecting all extended classes by going through ClassReflection::getParentClass() recursively.

  2. Collector for InClassNode collecting all not-yet-readonly class declarations.

  3. CollectedDataNode rule comparing these two datasets and deciding what to report.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.