Skip to content

Commit

Permalink
Merge pull request #1 from sasezaki/initial-setup
Browse files Browse the repository at this point in the history
initial setups
  • Loading branch information
sasezaki authored Nov 11, 2023
2 parents 3a49301 + 16b55ef commit 143cebb
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 61 deletions.
4 changes: 3 additions & 1 deletion composer-require-checker.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"PHPStan\\Type\\ArrayType",
"PHPStan\\Type\\Constant\\ConstantStringType",
"PHPStan\\Type\\ObjectType",
"PHPStan\\Type\\Type"
"PHPStan\\Type\\Type",
"PhpParser\\Node\\Expr\\FuncCall",
"PhpParser\\Node\\Name"
]
}
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "struggle-for-php/sfp-phpstan-dont-operation-inside-constructor",
"description": "Extra strict and opinionated resource operation rules.neon for PHPStan",
"description": "Extra strict and opinionated resource operation rules for PHPStan",
"type": "phpstan-extension",
"keywords": ["phpstan", "static analysis", "constructor", "operation", "resource"],
"license": [
Expand All @@ -9,7 +9,7 @@
"require": {
"php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"phpstan/phpstan": "^1.10",
"struggle-for-php/resource-operations": "^4.0.1"
"struggle-for-php/resource-operations": "^4.0.2"
},
"require-dev": {
"laminas/laminas-coding-standard": "^2.0.0",
Expand Down
4 changes: 4 additions & 0 deletions example/example.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<?php
namespace SfpExample\PHPStan\DontOperationInsideConstructor;

use function fopen;

class Example
{
public function __construct()
{
$fileInfo = new \SplFileInfo('test');
$fileInfo->openFile('r');

fopen('test', 'r');
}

public static function factory() : self
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ parameters:
- test/Rules/data/*

includes:
# - vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
1 change: 1 addition & 0 deletions rules.neon
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
rules:
- Sfp\PHPStan\DontOperationInsideConstructor\Rules\ResourceOperationMethodCallRule
- Sfp\PHPStan\DontOperationInsideConstructor\Rules\ResourceOperationFuncCallRule
46 changes: 46 additions & 0 deletions src/Rules/ResourceOperationFuncCallRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Sfp\PHPStan\DontOperationInsideConstructor\Rules;

use PhpParser\Node;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use Sfp\ResourceOperations\ResourceOperations;

use function in_array;
use function sprintf;

/**
* @implements Rule<Node\Expr\FuncCall>
*/
final class ResourceOperationFuncCallRule implements Rule
{
public function getNodeType(): string
{
return Node\Expr\FuncCall::class;
}

public function processNode(Node $node, Scope $scope): array
{
if ($scope->getFunctionName() !== '__construct') {
return [];
}

if (! $node->name instanceof Name) {
return [];
}

$errors = [];
if (in_array($node->name->toString(), ResourceOperations::getFunctions(), true)) {
$errors[] = RuleErrorBuilder::message(
sprintf("Don't resource operation inside constructor. function %s() is called.", $node->name->toString())
)->identifier('sfp-dont-operation.resourceOperationFuncCall')->build();
}

return $errors;
}
}
72 changes: 38 additions & 34 deletions src/Rules/ResourceOperationMethodCallRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,47 @@
use PHPStan\Rules\RuleErrorBuilder;
use Sfp\ResourceOperations\ResourceOperations;

use function in_array;
use function sprintf;
use function strtolower;

/**
* @implements Rule<Node\Expr\MethodCall>
*/
final class ResourceOperationMethodCallRule implements Rule
{
public function getNodeType(): string
{
return Node\Expr\MethodCall::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (! $node->name instanceof Node\Identifier) {
// @codeCoverageIgnoreStart
return []; // @codeCoverageIgnoreEnd
}

if ($scope->getFunctionName() !== '__construct') {
return [];
}

$calledOnType = $scope->getType($node->var);

$methodNames = [];
foreach ($calledOnType->getObjectClassNames() as $objectClassName) {
$methodNames [] = $objectClassName . '::' .strtolower($node->name->name);
}

$errors = [];
foreach ($methodNames as $methodName) {
if (in_array($methodName, ResourceOperations::getMethods(), true)) {
$errors[] = RuleErrorBuilder::message(
sprintf("Don't resource operation inside constructor. Method %s() is called.", $methodName)
)->identifier('sfp-dont-operation.resourceOperationMethodCall')->build();
}
}

return $errors;
}
public function getNodeType(): string
{
return Node\Expr\MethodCall::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (! $node->name instanceof Node\Identifier) {
// @codeCoverageIgnoreStart
return []; // @codeCoverageIgnoreEnd
}

if ($scope->getFunctionName() !== '__construct') {
return [];
}

$calledOnType = $scope->getType($node->var);

$methodNames = [];
foreach ($calledOnType->getObjectClassNames() as $objectClassName) {
$methodNames [] = $objectClassName . '::' . strtolower($node->name->name);
}

$errors = [];
foreach ($methodNames as $methodName) {
if (in_array($methodName, ResourceOperations::getMethods(), true)) {
$errors[] = RuleErrorBuilder::message(
sprintf("Don't resource operation inside constructor. Method %s() is called.", $methodName)
)->identifier('sfp-dont-operation.resourceOperationMethodCall')->build();
}
}

return $errors;
}
}
31 changes: 31 additions & 0 deletions test/Rules/ResourceOperationFuncCallRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace SfpTest\PHPStan\DontOperationInsideConstructor\Rules;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use Sfp\PHPStan\DontOperationInsideConstructor\Rules\ResourceOperationFuncCallRule;

/**
* @extends RuleTestCase<ResourceOperationFuncCallRule>
* @covers \Sfp\PHPStan\DontOperationInsideConstructor\Rules\ResourceOperationFuncCallRule
*/
class ResourceOperationFuncCallRuleTest extends RuleTestCase
{
public function getRule(): Rule
{
return new ResourceOperationFuncCallRule();
}

public function testProcess(): void
{
$this->analyse([__DIR__ . '/data/ResourceOperationFuncCall.php'], [
[
"Don't resource operation inside constructor. function fopen() is called.",
13,
],
]);
}
}
29 changes: 16 additions & 13 deletions test/Rules/ResourceOperationMethodCallRuleTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace SfpTest\PHPStan\DontOperationInsideConstructor\Rules;

use PHPStan\Rules\Rule;
Expand All @@ -8,21 +10,22 @@

/**
* @extends RuleTestCase<ResourceOperationMethodCallRule>
* @covers \Sfp\PHPStan\DontOperationInsideConstructor\Rules\ResourceOperationMethodCallRule
*/
class ResourceOperationMethodCallRuleTest extends RuleTestCase
{
public function getRule(): Rule
{
return new ResourceOperationMethodCallRule();
}
public function getRule(): Rule
{
return new ResourceOperationMethodCallRule();
}

public function testProcess(): void
{
$this->analyse([__DIR__ . '/data/resourceOperationMethodCall.php'], [
[
"Don't resource operation inside constructor. Method SplFileInfo::openfile() is called.",
8
]
]);
}
public function testProcess(): void
{
$this->analyse([__DIR__ . '/data/ResourceOperationMethodCall.php'], [
[
"Don't resource operation inside constructor. Method SplFileInfo::openfile() is called.",
14,
],
]);
}
}
18 changes: 18 additions & 0 deletions test/Rules/data/ResourceOperationFuncCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace SfpTest\PHPStan\DontOperationInsideConstructor\Rules\data;

use function fopen;

class ResourceOperationFuncCall
{
public function __construct()
{
$fp = fopen('test', 'r');

(function () {
})();
}
}
16 changes: 16 additions & 0 deletions test/Rules/data/ResourceOperationMethodCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace SfpTest\PHPStan\DontOperationInsideConstructor\Rules\data;

use SplFileInfo;

class ResourceOperationMethodCall
{
public function __construct()
{
$fileInfo = new SplFileInfo('test');
$fileInfo->openFile('r');
}
}
10 changes: 0 additions & 10 deletions test/Rules/data/resourceOperationMethodCall.php

This file was deleted.

9 changes: 9 additions & 0 deletions test/example.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" failures="2" name="phpstan" tests="2" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/junit-team/junit5/r5.5.1/platform-tests/src/test/resources/jenkins-junit.xsd">
<testcase name="example/example.php:11">
<failure type="ERROR" message="Don't resource operation inside constructor. Method SplFileInfo::openfile() is called."/>
</testcase>
<testcase name="example/example.php:13">
<failure type="ERROR" message="Don't resource operation inside constructor. function fopen() is called."/>
</testcase>
</testsuite>

0 comments on commit 143cebb

Please sign in to comment.