Skip to content

Commit

Permalink
Add disallowedMethodCalls.allowCount to allow method calls N times
Browse files Browse the repository at this point in the history
See:
- #86
  • Loading branch information
ruudk committed Oct 22, 2021
1 parent 14138dc commit ceb9e72
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 21 deletions.
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ parameters:
- src
level: max
typeAliases:
ForbiddenCallsConfig: 'array<array{function?:string, method?:string, message?:string, allowIn?:string[], allowInFunctions?:string[], allowInMethods?:string[], allowParamsInAllowed?:array<integer, integer|boolean|string>, allowParamsInAllowedAnyValue?:array<integer, integer>, allowParamsAnywhere?:array<integer, integer|boolean|string>, allowParamsAnywhereAnyValue?:array<integer, integer>, allowExceptParamsInAllowed?:array<integer, integer|boolean|string>, allowExceptParams?:array<integer, integer|boolean|string>, allowExceptCaseInsensitiveParams?:array<integer, integer|boolean|string>}>'
ForbiddenCallsConfig: 'array<array{function?:string, method?:string, message?:string, allowIn?:string[], allowInFunctions?:string[], allowInMethods?:string[], allowParamsInAllowed?:array<integer, integer|boolean|string>, allowParamsInAllowedAnyValue?:array<integer, integer>, allowParamsAnywhere?:array<integer, integer|boolean|string>, allowParamsAnywhereAnyValue?:array<integer, integer>, allowExceptParamsInAllowed?:array<integer, integer|boolean|string>, allowExceptParams?:array<integer, integer|boolean|string>, allowExceptCaseInsensitiveParams?:array<integer, integer|boolean|string>, allowCount?:int}>'
ForbiddenCalls: 'PhpParser\Node\Expr\FuncCall|PhpParser\Node\Expr\MethodCall|PhpParser\Node\Expr\StaticCall|PhpParser\Node\Expr\New_'

includes:
Expand Down
2 changes: 1 addition & 1 deletion src/Calls/ShellExecCalls.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function processNode(Node $node, Scope $scope): array
'shell_exec',
null,
$this->disallowedCalls,
'Using the backtick operator (`...`) is forbidden because shell_exec() is forbidden, %2$s%3$s'
'Using the backtick operator (`...`) is forbidden because shell_exec() is forbidden, %3$s%4$s'
);
}

Expand Down
28 changes: 27 additions & 1 deletion src/DisallowedCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ class DisallowedCall
/** @var array<int, DisallowedCallParam> */
private $allowExceptParams;

/** @var int */
private $allowCount;

/** @var int */
private $timesAllowed = 0;


/**
* DisallowedCall constructor.
Expand All @@ -44,8 +50,9 @@ class DisallowedCall
* @param array<int, DisallowedCallParam> $allowParamsAnywhere
* @param array<int, DisallowedCallParam> $allowExceptParamsInAllowed
* @param array<int, DisallowedCallParam> $allowExceptParams
* @param int $allowCount
*/
public function __construct(string $call, ?string $message, array $allowIn, array $allowInCalls, array $allowParamsInAllowed, array $allowParamsAnywhere, array $allowExceptParamsInAllowed, array $allowExceptParams)
public function __construct(string $call, ?string $message, array $allowIn, array $allowInCalls, array $allowParamsInAllowed, array $allowParamsAnywhere, array $allowExceptParamsInAllowed, array $allowExceptParams, int $allowCount)
{
$this->call = $call;
$this->message = $message;
Expand All @@ -55,6 +62,7 @@ public function __construct(string $call, ?string $message, array $allowIn, arra
$this->allowParamsAnywhere = $allowParamsAnywhere;
$this->allowExceptParamsInAllowed = $allowExceptParamsInAllowed;
$this->allowExceptParams = $allowExceptParams;
$this->allowCount = $allowCount;
}


Expand Down Expand Up @@ -124,6 +132,24 @@ public function getAllowExceptParams(): array
}


public function getAllowCount(): int
{
return $this->allowCount;
}


public function hasRemainingAllowCount(): bool
{
return $this->allowCount > $this->timesAllowed;
}


public function trackAllowedCall(): void
{
$this->timesAllowed++;
}


public function getKey(): string
{
// The key consists of "initial" config values that would be overwritten with more specific details in a custom config.
Expand Down
3 changes: 2 additions & 1 deletion src/DisallowedCallFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public function createFromConfig(array $config): array
$allowParamsInAllowed,
$allowParamsAnywhere,
$allowExceptParamsInAllowed,
$allowExceptParams
$allowExceptParams,
$disallowedCall['allowCount'] ?? 0
);
$calls[$disallowedCall->getKey()] = $disallowedCall;
}
Expand Down
8 changes: 7 additions & 1 deletion src/DisallowedHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,16 @@ public function getDisallowedMessage(?Node $node, Scope $scope, string $name, ?s
{
foreach ($disallowedCalls as $disallowedCall) {
if ($this->callMatches($disallowedCall, $name) && !$this->isAllowed($scope, $node, $disallowedCall)) {
if ($disallowedCall->hasRemainingAllowCount()) {
$disallowedCall->trackAllowedCall();
return [];
}

return [
sprintf(
$message ?? 'Calling %s is forbidden, %s%s',
$message ?? 'Calling %s%s is forbidden, %s%s',
($displayName && $displayName !== $name) ? "{$name}() (as {$displayName}())" : "{$name}()",
$disallowedCall->getAllowCount() > 0 ? sprintf(' more than %s', $disallowedCall->getAllowCount() === 1 ? 'once' : sprintf('%d times', $disallowedCall->getAllowCount())) : '',
$disallowedCall->getMessage(),
$disallowedCall->getCall() !== $name ? " [{$name}() matches {$disallowedCall->getCall()}()]" : ''
),
Expand Down
54 changes: 42 additions & 12 deletions tests/Calls/MethodCallsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ protected function getRule(): Rule
1 => 'y',
],
],
[
'function' => 'Fiction\Pulp\GeneratedClass::execute*()',
'message' => 'use another generated class',
'allowCount' => 2,
'allowIn' => [
'../src/disallowed-allowed/*.php',
'../src/*-allow/*.*',
],
],
[
'function' => 'Fiction\Pulp\GeneratedClass::canBeCalledOnce()',
'message' => 'use another generated class',
'allowCount' => 1,
'allowIn' => [
'../src/disallowed-allowed/*.php',
'../src/*-allow/*.*',
],
],
]
);
}
Expand All @@ -97,53 +115,65 @@ public function testRule(): void
// expect this error message:
"Calling Waldo\Quux\Blade::runner() is forbidden, I've seen tests you people wouldn't believe [Waldo\Quux\Blade::runner() matches Waldo\Quux\Blade::run*()]",
// on this line:
10,
11,
],
[
"Calling Waldo\Quux\Blade::runner() is forbidden, I've seen tests you people wouldn't believe [Waldo\Quux\Blade::runner() matches Waldo\Quux\Blade::run*()]",
11,
12,
],
[
"Calling Waldo\Quux\Blade::runner() is forbidden, I've seen tests you people wouldn't believe [Waldo\Quux\Blade::runner() matches Waldo\Quux\Blade::run*()]",
14,
15,
],
[
'Calling Inheritance\Base::x() (as Inheritance\Sub::x()) is forbidden, Base::x*() methods are dangerous [Inheritance\Base::x() matches Inheritance\Base::x*()]',
22,
23,
],
[
'Calling Traits\TestTrait::x() (as Traits\TestClass::x()) is forbidden, all TestTrait methods are dangerous [Traits\TestTrait::x() matches Traits\TestTrait::*()]',
26,
27,
],
[
'Calling Traits\TestTrait::y() (as Traits\AnotherTestClass::y()) is forbidden, all TestTrait methods are dangerous [Traits\TestTrait::y() matches Traits\TestTrait::*()]',
28,
29,
],
[
'Calling Traits\AnotherTestClass::zzTop() is forbidden, method AnotherTestClass::zzTop() is dangerous',
29,
30,
],
[
'Calling PhpOption\None::getIterator() is forbidden, no PhpOption',
46,
47,
],
[
'Calling PhpOption\Some::getIterator() is forbidden, no PhpOption',
52,
53,
],
[
'Calling DateTime::format() is forbidden, why too kay',
55,
56,
],
[
'Calling Fiction\Pulp\GeneratedClass::execute() more than 2 times is forbidden, use another generated class [Fiction\Pulp\GeneratedClass::execute() matches Fiction\Pulp\GeneratedClass::execute*()]',
63,
],
[
'Calling Fiction\Pulp\GeneratedClass::executeOrThrow() more than 2 times is forbidden, use another generated class [Fiction\Pulp\GeneratedClass::executeOrThrow() matches Fiction\Pulp\GeneratedClass::execute*()]',
64,
],
[
'Calling Fiction\Pulp\GeneratedClass::canBeCalledOnce() more than once is forbidden, use another generated class',
66,
],
]);
$this->analyse([__DIR__ . '/../src/disallowed-allow/methodCalls.php'], [
[
"Calling Waldo\Quux\Blade::runner() is forbidden, I've seen tests you people wouldn't believe [Waldo\Quux\Blade::runner() matches Waldo\Quux\Blade::run*()]",
10,
11,
],
[
"Calling Waldo\Quux\Blade::runner() is forbidden, I've seen tests you people wouldn't believe [Waldo\Quux\Blade::runner() matches Waldo\Quux\Blade::run*()]",
11,
12,
],
]);
}
Expand Down
8 changes: 4 additions & 4 deletions tests/Calls/NewCallsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,19 @@ public function testRule(): void
$this->analyse([__DIR__ . '/../src/disallowed/methodCalls.php'], [
[
'Calling Constructor\ClassWithConstructor::__construct() is forbidden, class ClassWithConstructor should not be created',
32,
33,
],
[
'Calling Constructor\ClassWithoutConstructor::__construct() is forbidden, class ClassWithoutConstructor should not be created',
34,
35,
],
[
'Calling Constructor\ClassWithoutConstructor::__construct() is forbidden, class ClassWithoutConstructor should not be created',
36,
37,
],
[
'Calling DateTime::__construct() is forbidden, no future',
57,
58,
]
]);
// Based on the configuration above, no errors in this file:
Expand Down
1 change: 1 addition & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/libs/Bar.php';
require_once __DIR__ . '/libs/Blade.php';
require_once __DIR__ . '/libs/GeneratedClass.php';
require_once __DIR__ . '/libs/Constructor.php';
require_once __DIR__ . '/libs/Inheritance.php';
require_once __DIR__ . '/libs/Royale.php';
Expand Down
11 changes: 11 additions & 0 deletions tests/libs/GeneratedClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
declare(strict_types = 1);

namespace Fiction\Pulp;

class GeneratedClass
{
public function execute() {}
public function executeOrThrow() {}
public function canBeCalledOnce() {}
}
9 changes: 9 additions & 0 deletions tests/src/disallowed-allow/methodCalls.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
declare(strict_types = 1);

use Constructor\ClassWithConstructor;
use Fiction\Pulp\GeneratedClass;
use Waldo\Quux;

$blade = new Quux\Blade();
Expand Down Expand Up @@ -55,3 +56,11 @@
(new DateTime())->format('y');
(new DateTime())->format('Y');
new DateTime('tOmOrRoW');

$class = new GeneratedClass();
$class->execute();
$class->executeOrThrow();
$class->execute();
$class->executeOrThrow();
$class->canBeCalledOnce();
$class->canBeCalledOnce();
9 changes: 9 additions & 0 deletions tests/src/disallowed/methodCalls.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
declare(strict_types = 1);

use Constructor\ClassWithConstructor;
use Fiction\Pulp\GeneratedClass;
use Waldo\Quux;

$blade = new Quux\Blade();
Expand Down Expand Up @@ -55,3 +56,11 @@
(new DateTime())->format('y');
(new DateTime())->format('Y');
new DateTime('tOmOrRoW');

$class = new GeneratedClass();
$class->execute();
$class->executeOrThrow();
$class->execute();
$class->executeOrThrow();
$class->canBeCalledOnce();
$class->canBeCalledOnce();

0 comments on commit ceb9e72

Please sign in to comment.