Search code examples
phpstan

How to determine if a node is inside certain class constructor in PHPStan


I want to determine whether a MethodCall node is inside a certain class constructor. More specifically, I want to know if a method is being called within an exception constructor.

This is the background: A third-party translation service uses static analysis to find translatable strings in my code. It does so by analyzing call to a ::translate() method. It only supports string literals, so I can't pass variables or method calls, for example--except when re-throwing exceptions, where there's no way to know the strings ahead of time. Here's an example:

<?php

class ExampleClass
{
    // I know this string ahead of time.
    const BAD = 'BAD';

    function exampleMethod()
    {
        // Should PASS: I control the string, so I pass it directly.
        $this->translate('Good');

        try {
            $this->throwsAnException();
        } catch (Throwable $e) {
            // Should PASS: I don't "own" the string--but it still has to
            // be passed through the translation system for "reasons"--so
            // the only option is to pass the method call.
            throw new Exception($this->translate($e->getMessage()));
        }

        // Should FAIL: I control the string, so I should pass the literal.
        $bad = 'BAD';
        $this->translate($bad);

        // Should FAIL: Same thing. It's my string, so I should pass the literal.
        $this->translate(self::BAD);
    }
}

I have a (basically) working solution to prohibiting non-string values here: How to determine whether a method argument is a literal/scalar string in PHPStan, even if concatenated. What I still need is to ignore the rule when the node is in a new Exception() call. Does anyone have any ideas?


Solution

  • So your rule is registered to process the MethodCall node which is the translate method call.

    You essentially want to "look around" the method call to see if it's inside new Exception.

    Currently you need a custom node visitor to set the attribute on the node. Here are the details: https://phpstan.org/blog/preprocessing-ast-for-custom-rules

    And please ask in GitHub Discussions next time, you'll get a good answer too and quickly :) https://github.com/phpstan/phpstan/discussions