You think PHPStan as a side tool? You missed the real deal

PHPStan is one of the base tools for a PHP developer as it helps a lot to double check types are coherent in the application.

However, by using it this way you are passing aside of its key features, sharing knowledge.

Share your mistakes

Often on a project, different developers tends to make the same mistakes when manipulating the same notions.

The issue being the knowledge is not shared from one developer to another.

So what is possible to prevent this? The first idea that comes to mind would be to mention it inside the documentation or using code review to share that knowledge.

However, both of theses have flaws as the documentation needs to be read and the code review needs to be done by a developer with that knowledge.

That is why we need another solution which would fit better the problem we have.

This is where PHPStan step in offering us custom rules.

What is a custom rule

A rule is some class that contains the logic necessary to decide if PHPStan reports an error.

Due to that a PHPStan rule will have have access to the code analyzed under the form of a tree called the AST, Abstract Syntax Tree, to facilitate the navigation.

To help us even more PHPStan will provide us only with the type of node that interest us.

Create a custom rule

Any custom rule needs to implement the following interface, \PHPStan\Rules\Rule .

This interface has two methods:

  • getNodeType which is here to let you pick the node type the rule will be executed on. To get the list of all node types available a list is provided on PHPStan documentation.
  • processNode which contains the main logic that will return potential errors.

A rule to prevent var_dump

Now that we understood the base setup a custom rule, it is now time for an example.

For that we will create a rule that detect if the function var_dump is used and if it the case display an error message.

For that we will have to attach our rule to the \PhpParser\Node\Expr\FuncCall node type the following way:

 public function getNodeType(): string
{
return \PhpParser\Node\Expr\FuncCall::class;
}

Next it is time to write the main logic to raise the error.

For this we will be using the name from the current node and compare it to var_dump .

If it is the case then the error message will be returned:

public function processNode( Node $node, Scope $scope ): array {
$name = (string) $node->name;
if( 'var_dump' !== $name ) {
return [];
}
return [
RuleErrorBuilder::message(
'var_dump function is not allowed'
)
->identifier('varDumpForbidden')
->build(),
];
}

That way our rule will finally look like that:


use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

class VarDumpRule implements Rule {
public function getNodeType(): string
{
return \PhpParser\Node\Expr\FuncCall::class;
}

public function processNode( Node $node, Scope $scope ): array {
$name = (string) $node->name;
if( 'var_dump' !== $name ) {
return [];
}
return [
RuleErrorBuilder::message(
'var_dump function is not allowed'
)
->identifier('varDumpForbidden')
->build(),
];
}
}

Testing the rule

Now that we added the logic inside the custom rule we now need to test it works correctly.

For that we will have to write some automated tests.

PHPStan rules tests are based on code template that will be analyzed with the rule and a match between error raised from that analysis with what was expected.

To create a testcase for a PHPStan rule it needs to extend PHPStan\Testing\RuleTestCase:

use PHPStan\Testing\RuleTestCase;

class VarDumpRuleTest extends RuleTestCase {

}

This testcase always needs to implement the method getRule to return the instance of the tested rule:

  protected function getRule(): Rule {
return new VarDumpRule();
}

To create a test a new method should be created starting with the word test.

In our example we will have two cases:

  • A function var_dump is present inside the file.
  • No function var_dump is present inside the file.

For the first case, we will have to write the following method:

public function testMethodPresentShouldRaiseError() {

}

Once this is done we will create a template file with the following content:

function test ($var_dump) {
var_dump(1000);
}

Then we gonna link both of theses file using the analysemethod inside the test:

public function testMethodPresentShouldRaiseError() {
$this->analyse([__DIR__ . '/../data/present.php'], [
[
'var_dump function is not allowed',
2,
],
]);
}

The first parameter we pass to the analyse method is the list of paths from files to analyse and the second is the list of error expected to be raised with the following syntax:

  • Message from the error.
  • Line from the error.

Use the custom rule

Creating a rule is only useful if it is used.

This is why inside this part we will see how to use it.

For that there are two options.

Using it inside the same project or creating a PHPStan extension.

Register a rule

To use a rule in the same project then it needs to be added to the PHPStan configuration from the project.

For that it is possible to use the rules key:

rules:
- VarDumpRule

Create an extension

The second way of using a custom rule is to create a PHPStan library.

For that we need to create first a composer project with the following command composer init and make the wiring between the namespace and the src folder.

Then the rule VarDumpRule should be moved to the src folder.

Finally we need to create a configuration file extension.nano for the extension:

rules:
- VarDumpRule

Leave a Reply

Your email address will not be published. Required fields are marked *