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?