diff --git a/docs/custom-rules.md b/docs/custom-rules.md index 3aa2e83..c563c3a 100644 --- a/docs/custom-rules.md +++ b/docs/custom-rules.md @@ -10,6 +10,7 @@ There are several different types (and configuration keys) that can be disallowe 6. `disallowedSuperglobals` - for usages of superglobal variables like `$GLOBALS` or `$_POST` 7. `disallowedAttributes` - for attributes like `#[Entity(class: Foo::class, something: true)]` 8. `disallowedEnums` - for enums, both pure & backed, like `Suit::Hearts` (like class constants, enums need to be split to `enum: Suit` & `case: Hearts` in the configuration, see notes below) +9. `disallowedControlStructures` - for control structures like `if`, `else`, `elseif`, loops, `require` & `include`, and `goto` Use them to add rules to your `phpstan.neon` config file. I like to use a separate file (`disallowed-calls.neon`) for these which I'll include later on in the main `phpstan.neon` config file. Here's an example, update to your needs: @@ -165,6 +166,8 @@ You can treat some language constructs as functions and disallow it in `disallow To disallow naive object creation (`new ClassName()` or `new $classname`), disallow `NameSpace\ClassName::__construct` in `disallowedMethodCalls`. Works even when there's no constructor defined in that class. +You can also disallow control structures, see below. + ### Constants When [disallowing constants](disallowing-constants.md) please be aware of limitations and special requirements, see [docs](disallowing-constants.md). @@ -172,3 +175,27 @@ When [disallowing constants](disallowing-constants.md) please be aware of limita ### Enums Similar to disallowing constants, enums have some limitations, see [docs](disallowing-enums.md). + +### Control structures + +You can forbid [control structures](https://www.php.net/language.control-structures) like `if`, `else`, `elseif` (don't write it as `else if` because `else if` is parsed as `else` followed by `if` producing unexpected results), loops, `break`, `continue`, `goto`, `require`, `include` (and the `_once` variants). + +Currently, parameters are not checked, so it's not possible to disallow for example `declare`, but re-allow `declare(strict-types = 1)`. + +You can use `controlStructure` or `structure` directives, and both can be specified as arrays: +```neon +parameters: + disallowedControlStructures: + - + controlStructure: + - 'elseif' + - 'return' + allowIn: + - 'tests/foo/bar.php' + - + structure: 'goto' + message: "restructure the program's flow" + errorTip: 'https://xkcd.com/292/' + allowIn: + - 'tests/waldo.php' +``` diff --git a/extension.neon b/extension.neon index c4584c0..c7950d3 100644 --- a/extension.neon +++ b/extension.neon @@ -10,6 +10,7 @@ parameters: disallowedEnums: [] disallowedSuperglobals: [] disallowedAttributes: [] + disallowedControlStructures: [] parametersSchema: allowInRootDir: schema(string(), nullable()) @@ -224,6 +225,18 @@ parametersSchema: ?errorTip: string(), ]) ) + disallowedControlStructures: listOf( + structure([ + ?controlStructure: anyOf(string(), listOf(string())), + ?structure: anyOf(string(), listOf(string())), + ?message: string(), + ?allowIn: listOf(string()), + ?allowExceptIn: list(string()), + ?disallowIn: list(string()), + ?errorIdentifier: string(), + ?errorTip: string(), + ]) + ) services: - Spaze\PHPStan\Rules\Disallowed\Allowed\Allowed @@ -231,6 +244,7 @@ services: - Spaze\PHPStan\Rules\Disallowed\DisallowedAttributeFactory - Spaze\PHPStan\Rules\Disallowed\DisallowedCallFactory - Spaze\PHPStan\Rules\Disallowed\DisallowedConstantFactory + - Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory - Spaze\PHPStan\Rules\Disallowed\DisallowedNamespaceFactory - Spaze\PHPStan\Rules\Disallowed\DisallowedSuperglobalFactory - Spaze\PHPStan\Rules\Disallowed\File\FilePath(rootDir: %filesRootDir%) @@ -239,6 +253,7 @@ services: - Spaze\PHPStan\Rules\Disallowed\Normalizer\Normalizer - Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedAttributeRuleErrors - Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedConstantRuleErrors + - Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedControlStructureRuleErrors - Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedMethodRuleErrors - Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedNamespaceRuleErrors - Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedCallsRuleErrors @@ -312,3 +327,63 @@ services: factory: Spaze\PHPStan\Rules\Disallowed\Usages\AttributeUsages(disallowedAttributes: %disallowedAttributes%) tags: - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\BreakControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\ContinueControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\DeclareControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\DoWhileControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\ElseControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\ElseIfControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\ForControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\ForeachControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\GotoControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\IfControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\MatchControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\RequireIncludeControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\ReturnControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\SwitchControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule + - + factory: Spaze\PHPStan\Rules\Disallowed\ControlStructures\WhileControlStructure(disallowedControlStructures: @Spaze\PHPStan\Rules\Disallowed\DisallowedControlStructureFactory::getDisallowedControlStructures(%disallowedControlStructures%)) + tags: + - phpstan.rules.rule diff --git a/src/ControlStructures/BreakControlStructure.php b/src/ControlStructures/BreakControlStructure.php new file mode 100644 index 0000000..a15b6f3 --- /dev/null +++ b/src/ControlStructures/BreakControlStructure.php @@ -0,0 +1,59 @@ + + */ +class BreakControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Break_::class; + } + + + /** + * @param Break_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'break', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/ContinueControlStructure.php b/src/ControlStructures/ContinueControlStructure.php new file mode 100644 index 0000000..0b4f026 --- /dev/null +++ b/src/ControlStructures/ContinueControlStructure.php @@ -0,0 +1,59 @@ + + */ +class ContinueControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Continue_::class; + } + + + /** + * @param Continue_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'continue', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/DeclareControlStructure.php b/src/ControlStructures/DeclareControlStructure.php new file mode 100644 index 0000000..7772eea --- /dev/null +++ b/src/ControlStructures/DeclareControlStructure.php @@ -0,0 +1,59 @@ + + */ +class DeclareControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Declare_::class; + } + + + /** + * @param Declare_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'declare', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/DoWhileControlStructure.php b/src/ControlStructures/DoWhileControlStructure.php new file mode 100644 index 0000000..aa210a9 --- /dev/null +++ b/src/ControlStructures/DoWhileControlStructure.php @@ -0,0 +1,59 @@ + + */ +class DoWhileControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Do_::class; + } + + + /** + * @param Do_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'do-while', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/ElseControlStructure.php b/src/ControlStructures/ElseControlStructure.php new file mode 100644 index 0000000..a589975 --- /dev/null +++ b/src/ControlStructures/ElseControlStructure.php @@ -0,0 +1,59 @@ + + */ +class ElseControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Else_::class; + } + + + /** + * @param Else_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'else', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/ElseIfControlStructure.php b/src/ControlStructures/ElseIfControlStructure.php new file mode 100644 index 0000000..57b3c92 --- /dev/null +++ b/src/ControlStructures/ElseIfControlStructure.php @@ -0,0 +1,60 @@ + + */ +class ElseIfControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return ElseIf_::class; + } + + + /** + * @param ElseIf_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'elseif', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/ForControlStructure.php b/src/ControlStructures/ForControlStructure.php new file mode 100644 index 0000000..1c8f4af --- /dev/null +++ b/src/ControlStructures/ForControlStructure.php @@ -0,0 +1,59 @@ + + */ +class ForControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return For_::class; + } + + + /** + * @param For_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'for', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/ForeachControlStructure.php b/src/ControlStructures/ForeachControlStructure.php new file mode 100644 index 0000000..ee42d9b --- /dev/null +++ b/src/ControlStructures/ForeachControlStructure.php @@ -0,0 +1,59 @@ + + */ +class ForeachControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Foreach_::class; + } + + + /** + * @param Foreach_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'foreach', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/GotoControlStructure.php b/src/ControlStructures/GotoControlStructure.php new file mode 100644 index 0000000..c8f0a55 --- /dev/null +++ b/src/ControlStructures/GotoControlStructure.php @@ -0,0 +1,59 @@ + + */ +class GotoControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Goto_::class; + } + + + /** + * @param Goto_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'goto', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/IfControlStructure.php b/src/ControlStructures/IfControlStructure.php new file mode 100644 index 0000000..a642df9 --- /dev/null +++ b/src/ControlStructures/IfControlStructure.php @@ -0,0 +1,59 @@ + + */ +class IfControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return If_::class; + } + + + /** + * @param If_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'if', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/MatchControlStructure.php b/src/ControlStructures/MatchControlStructure.php new file mode 100644 index 0000000..046ceed --- /dev/null +++ b/src/ControlStructures/MatchControlStructure.php @@ -0,0 +1,59 @@ + + */ +class MatchControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Match_::class; + } + + + /** + * @param Match_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'match', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/RequireIncludeControlStructure.php b/src/ControlStructures/RequireIncludeControlStructure.php new file mode 100644 index 0000000..0cd8c68 --- /dev/null +++ b/src/ControlStructures/RequireIncludeControlStructure.php @@ -0,0 +1,77 @@ + + */ +class RequireIncludeControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Include_::class; + } + + + /** + * @param Include_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + $type = null; + switch ($node->type) { + case Include_::TYPE_INCLUDE: + $type = 'include'; + break; + case Include_::TYPE_REQUIRE: + $type = 'require'; + break; + case Include_::TYPE_INCLUDE_ONCE: + $type = 'include_once'; + break; + case Include_::TYPE_REQUIRE_ONCE: + $type = 'require_once'; + break; + } + if ($type === null) { + return []; + } + return $this->disallowedControlStructureRuleErrors->get($scope, $type, $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/ReturnControlStructure.php b/src/ControlStructures/ReturnControlStructure.php new file mode 100644 index 0000000..397a3d6 --- /dev/null +++ b/src/ControlStructures/ReturnControlStructure.php @@ -0,0 +1,59 @@ + + */ +class ReturnControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Return_::class; + } + + + /** + * @param Return_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'return', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/SwitchControlStructure.php b/src/ControlStructures/SwitchControlStructure.php new file mode 100644 index 0000000..ea3aa82 --- /dev/null +++ b/src/ControlStructures/SwitchControlStructure.php @@ -0,0 +1,59 @@ + + */ +class SwitchControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return Switch_::class; + } + + + /** + * @param Switch_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'switch', $this->disallowedControlStructures); + } + +} diff --git a/src/ControlStructures/WhileControlStructure.php b/src/ControlStructures/WhileControlStructure.php new file mode 100644 index 0000000..8822677 --- /dev/null +++ b/src/ControlStructures/WhileControlStructure.php @@ -0,0 +1,59 @@ + + */ +class WhileControlStructure implements Rule +{ + + /** @var DisallowedControlStructureRuleErrors */ + private $disallowedControlStructureRuleErrors; + + /** @var list */ + private $disallowedControlStructures; + + + /** + * @param DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors + * @param list $disallowedControlStructures + */ + public function __construct(DisallowedControlStructureRuleErrors $disallowedControlStructureRuleErrors, array $disallowedControlStructures) + { + $this->disallowedControlStructureRuleErrors = $disallowedControlStructureRuleErrors; + $this->disallowedControlStructures = $disallowedControlStructures; + } + + + public function getNodeType(): string + { + return While_::class; + } + + + /** + * @param While_ $node + * @param Scope $scope + * @return list + * @throws ShouldNotHappenException + */ + public function processNode(Node $node, Scope $scope): array + { + return $this->disallowedControlStructureRuleErrors->get($scope, 'while', $this->disallowedControlStructures); + } + +} diff --git a/src/DisallowedControlStructure.php b/src/DisallowedControlStructure.php new file mode 100644 index 0000000..c6ce39a --- /dev/null +++ b/src/DisallowedControlStructure.php @@ -0,0 +1,104 @@ + */ + private $allowIn; + + /** @var list */ + private $allowExceptIn; + + /** @var string|null */ + private $errorIdentifier; + + /** @var string|null */ + private $errorTip; + + + /** + * @param string $controlStructure + * @param string|null $message + * @param list $allowIn + * @param list $allowExceptIn + * @param string|null $errorIdentifier + * @param string|null $errorTip + */ + public function __construct( + string $controlStructure, + ?string $message, + array $allowIn, + array $allowExceptIn, + ?string $errorIdentifier, + ?string $errorTip + ) { + $this->controlStructure = $controlStructure; + $this->message = $message; + $this->allowIn = $allowIn; + $this->allowExceptIn = $allowExceptIn; + $this->errorIdentifier = $errorIdentifier; + $this->errorTip = $errorTip; + } + + + public function getControlStructure(): string + { + return $this->controlStructure; + } + + + public function getMessage(): ?string + { + return $this->message; + } + + + /** @inheritDoc */ + public function getAllowIn(): array + { + return $this->allowIn; + } + + + /** @inheritDoc */ + public function getAllowExceptIn(): array + { + return $this->allowExceptIn; + } + + + public function getAllowInCalls(): array + { + throw new NotImplementedYetException(); + } + + + public function getAllowExceptInCalls(): array + { + throw new NotImplementedYetException(); + } + + + public function getErrorIdentifier(): ?string + { + return $this->errorIdentifier; + } + + + public function getErrorTip(): ?string + { + return $this->errorTip; + } + +} diff --git a/src/DisallowedControlStructureFactory.php b/src/DisallowedControlStructureFactory.php new file mode 100644 index 0000000..d81b164 --- /dev/null +++ b/src/DisallowedControlStructureFactory.php @@ -0,0 +1,71 @@ +, structure?:string|list, message?:string, allowIn?:list, allowExceptIn?:list, disallowIn?:list, errorIdentifier?:string, errorTip?:string}> $config + * @return list + * @throws ShouldNotHappenException + */ + public function getDisallowedControlStructures(array $config): array + { + $disallowedControlStructures = []; + foreach ($config as $disallowed) { + $controlStructures = $disallowed['controlStructure'] ?? $disallowed['structure'] ?? null; + unset($disallowed['controlStructure'], $disallowed['structure']); + if (!$controlStructures) { + throw new ShouldNotHappenException("Either 'controlStructure' or 'structure' must be set in configuration items"); + } + foreach ((array)$controlStructures as $controlStructure) { + if ($controlStructure === 'else if') { + throw new ShouldNotHappenException("Use 'elseif' instead of 'else if', because 'else if' is parsed as 'else' followed by 'if' and the behaviour may be unexpected if using 'else if' in the configuration"); + } + if (!in_array($controlStructure, self::CONTROL_STRUCTURES, true)) { + throw new ShouldNotHappenException(sprintf('%s is not a supported control structure, use one of %s', $controlStructure, implode(', ', self::CONTROL_STRUCTURES))); + } + $disallowedControlStructure = new DisallowedControlStructure( + $controlStructure, + $disallowed['message'] ?? null, + $disallowed['allowIn'] ?? [], + $disallowed['allowExceptIn'] ?? $disallowed['disallowIn'] ?? [], + $disallowed['errorIdentifier'] ?? null, + $disallowed['errorTip'] ?? null + ); + $disallowedControlStructures[$disallowedControlStructure->getControlStructure()] = $disallowedControlStructure; + } + } + return array_values($disallowedControlStructures); + } + +} diff --git a/src/RuleErrors/DisallowedControlStructureRuleErrors.php b/src/RuleErrors/DisallowedControlStructureRuleErrors.php new file mode 100644 index 0000000..113eeb6 --- /dev/null +++ b/src/RuleErrors/DisallowedControlStructureRuleErrors.php @@ -0,0 +1,64 @@ +allowedPath = $allowedPath; + $this->formatter = $formatter; + } + + + /** + * @param Scope $scope + * @param string $controlStructure + * @param list $disallowedControlStructures + * @return list + * @throws ShouldNotHappenException + */ + public function get(Scope $scope, string $controlStructure, array $disallowedControlStructures): array + { + foreach ($disallowedControlStructures as $disallowedControlStructure) { + if ( + $disallowedControlStructure->getControlStructure() === $controlStructure + && !$this->allowedPath->isAllowedPath($scope, $disallowedControlStructure) + ) { + $errorBuilder = RuleErrorBuilder::message(sprintf( + 'Using the %s control structure is forbidden%s', + $controlStructure, + $this->formatter->formatDisallowedMessage($disallowedControlStructure->getMessage()) + )); + if ($disallowedControlStructure->getErrorIdentifier()) { + $errorBuilder->identifier($disallowedControlStructure->getErrorIdentifier()); + } + if ($disallowedControlStructure->getErrorTip()) { + $errorBuilder->tip($disallowedControlStructure->getErrorTip()); + } + return [ + $errorBuilder->build(), + ]; + } + } + return []; + } + +} diff --git a/tests/ControlStructures/BreakControlStructureTest.php b/tests/ControlStructures/BreakControlStructureTest.php new file mode 100644 index 0000000..695c0fc --- /dev/null +++ b/tests/ControlStructures/BreakControlStructureTest.php @@ -0,0 +1,98 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'break', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the break control structure is forbidden.', + // on this line: + 32, + ], + [ + 'Using the break control structure is forbidden.', + 42, + ], + [ + 'Using the break control structure is forbidden.', + 52, + ], + [ + 'Using the break control structure is forbidden.', + 62, + ], + [ + 'Using the break control structure is forbidden.', + 72, + ], + [ + 'Using the break control structure is forbidden.', + 82, + ], + [ + 'Using the break control structure is forbidden.', + 92, + ], + [ + 'Using the break control structure is forbidden.', + 99, + ], + [ + 'Using the break control structure is forbidden.', + 101, + ], + [ + 'Using the break control structure is forbidden.', + 107, + ], + [ + 'Using the break control structure is forbidden.', + 109, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/ContinueControlStructureTest.php b/tests/ControlStructures/ContinueControlStructureTest.php new file mode 100644 index 0000000..946be0f --- /dev/null +++ b/tests/ControlStructures/ContinueControlStructureTest.php @@ -0,0 +1,82 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'continue', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the continue control structure is forbidden.', + // on this line: + 29, + ], + [ + 'Using the continue control structure is forbidden.', + 39, + ], + [ + 'Using the continue control structure is forbidden.', + 49, + ], + [ + 'Using the continue control structure is forbidden.', + 59, + ], + [ + 'Using the continue control structure is forbidden.', + 69, + ], + [ + 'Using the continue control structure is forbidden.', + 79, + ], + [ + 'Using the continue control structure is forbidden.', + 89, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/DeclareControlStructureTest.php b/tests/ControlStructures/DeclareControlStructureTest.php new file mode 100644 index 0000000..3073b39 --- /dev/null +++ b/tests/ControlStructures/DeclareControlStructureTest.php @@ -0,0 +1,62 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'declare', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the declare control structure is forbidden.', + // on this line: + 2, + ], + [ + 'Using the declare control structure is forbidden.', + 117, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/DoWhileControlStructureTest.php b/tests/ControlStructures/DoWhileControlStructureTest.php new file mode 100644 index 0000000..da190bb --- /dev/null +++ b/tests/ControlStructures/DoWhileControlStructureTest.php @@ -0,0 +1,58 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'do-while', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the do-while control structure is forbidden.', + // on this line: + 46, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/ElseControlStructureTest.php b/tests/ControlStructures/ElseControlStructureTest.php new file mode 100644 index 0000000..a759a36 --- /dev/null +++ b/tests/ControlStructures/ElseControlStructureTest.php @@ -0,0 +1,67 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'structure' => 'else', + 'message' => 'what else?', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the else control structure is forbidden, what else?', + // on this line: + 12, // `else if` is parsed as `else` followed by `if` + ], + [ + 'Using the else control structure is forbidden, what else?', + 14, + ], + [ + 'Using the else control structure is forbidden, what else?', + 22, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/ElseIfControlStructureTest.php b/tests/ControlStructures/ElseIfControlStructureTest.php new file mode 100644 index 0000000..e39d069 --- /dev/null +++ b/tests/ControlStructures/ElseIfControlStructureTest.php @@ -0,0 +1,81 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'structure' => 'elseif', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the elseif control structure is forbidden.', + // on this line: + 10, + ], + [ + 'Using the elseif control structure is forbidden.', + 20, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + /** + * @throws ShouldNotHappenException + */ + public function testElseSpaceIfException(): void + { + $this->expectException(ShouldNotHappenException::class); + $this->expectExceptionMessage("Use 'elseif' instead of 'else if', because 'else if' is parsed as 'else' followed by 'if' and the behaviour may be unexpected if using 'else if' in the configuration"); + $container = self::getContainer(); + new ElseIfControlStructure( + $container->getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'structure' => 'else if', + ], + ]) + ); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/ForControlStructureTest.php b/tests/ControlStructures/ForControlStructureTest.php new file mode 100644 index 0000000..9bd3415 --- /dev/null +++ b/tests/ControlStructures/ForControlStructureTest.php @@ -0,0 +1,62 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'for', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the for control structure is forbidden.', + // on this line: + 56, + ], + [ + 'Using the for control structure is forbidden.', + 66, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/ForeachControlStructureTest.php b/tests/ControlStructures/ForeachControlStructureTest.php new file mode 100644 index 0000000..50ad1b6 --- /dev/null +++ b/tests/ControlStructures/ForeachControlStructureTest.php @@ -0,0 +1,62 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'foreach', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the foreach control structure is forbidden.', + // on this line: + 76, + ], + [ + 'Using the foreach control structure is forbidden.', + 86, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/GotoControlStructureTest.php b/tests/ControlStructures/GotoControlStructureTest.php new file mode 100644 index 0000000..ae5d018 --- /dev/null +++ b/tests/ControlStructures/GotoControlStructureTest.php @@ -0,0 +1,58 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'goto', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the goto control structure is forbidden.', + // on this line: + 116, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/IfControlStructureTest.php b/tests/ControlStructures/IfControlStructureTest.php new file mode 100644 index 0000000..1611612 --- /dev/null +++ b/tests/ControlStructures/IfControlStructureTest.php @@ -0,0 +1,141 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'if', + 'message' => 'what if?', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + 'errorTip' => 'So long and thanks for all the ifs', + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the if control structure is forbidden, what if?', + // on this line: + 8, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 12, // `else if` is parsed as `else` followed by `if` + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 18, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 28, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 31, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 38, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 41, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 48, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 51, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 58, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 61, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 68, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 71, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 78, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 81, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 88, + 'So long and thanks for all the ifs', + ], + [ + 'Using the if control structure is forbidden, what if?', + 91, + 'So long and thanks for all the ifs', + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/MatchControlStructureTest.php b/tests/ControlStructures/MatchControlStructureTest.php new file mode 100644 index 0000000..dcc5172 --- /dev/null +++ b/tests/ControlStructures/MatchControlStructureTest.php @@ -0,0 +1,58 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'match', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the match control structure is forbidden.', + // on this line: + 112, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/RequireIncludeControlStructureTest.php b/tests/ControlStructures/RequireIncludeControlStructureTest.php new file mode 100644 index 0000000..5a9dc13 --- /dev/null +++ b/tests/ControlStructures/RequireIncludeControlStructureTest.php @@ -0,0 +1,91 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => [ + 'require', + 'include', + 'require_once', + 'include_once', + ], + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the require control structure is forbidden.', + // on this line: + 120, + ], + [ + 'Using the include control structure is forbidden.', + 121, + ], + [ + 'Using the require_once control structure is forbidden.', + 122, + ], + [ + 'Using the include_once control structure is forbidden.', + 123, + ], + [ + 'Using the require control structure is forbidden.', + 124, + ], + [ + 'Using the include control structure is forbidden.', + 125, + ], + [ + 'Using the require_once control structure is forbidden.', + 126, + ], + [ + 'Using the include_once control structure is forbidden.', + 127, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/ReturnControlStructureTest.php b/tests/ControlStructures/ReturnControlStructureTest.php new file mode 100644 index 0000000..fb88454 --- /dev/null +++ b/tests/ControlStructures/ReturnControlStructureTest.php @@ -0,0 +1,58 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'return', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the return control structure is forbidden.', + // on this line: + 13, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/SwitchControlStructureTest.php b/tests/ControlStructures/SwitchControlStructureTest.php new file mode 100644 index 0000000..72597e7 --- /dev/null +++ b/tests/ControlStructures/SwitchControlStructureTest.php @@ -0,0 +1,62 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'switch', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the switch control structure is forbidden.', + // on this line: + 96, + ], + [ + 'Using the switch control structure is forbidden.', + 104, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/ControlStructures/WhileControlStructureTest.php b/tests/ControlStructures/WhileControlStructureTest.php new file mode 100644 index 0000000..4024b2e --- /dev/null +++ b/tests/ControlStructures/WhileControlStructureTest.php @@ -0,0 +1,62 @@ +getByType(DisallowedControlStructureRuleErrors::class), + $container->getByType(DisallowedControlStructureFactory::class)->getDisallowedControlStructures([ + [ + 'controlStructure' => 'while', + 'allowIn' => [ + __DIR__ . '/../src/disallowed-allow/*.php', + __DIR__ . '/../src/*-allow/*.*', + ], + ], + ]) + ); + } + + + public function testRule(): void + { + // Based on the configuration above, in this file: + $this->analyse([__DIR__ . '/../src/disallowed/controlStructures.php'], [ + [ + // expect this error message: + 'Using the while control structure is forbidden.', + // on this line: + 26, + ], + [ + 'Using the while control structure is forbidden.', + 36, + ], + ]); + $this->analyse([__DIR__ . '/../src/disallowed-allow/controlStructures.php'], []); + } + + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + ]; + } + +} diff --git a/tests/src/disallowed-allow/controlStructures.php b/tests/src/disallowed-allow/controlStructures.php new file mode 100644 index 0000000..70b93fb --- /dev/null +++ b/tests/src/disallowed-allow/controlStructures.php @@ -0,0 +1,127 @@ + $value) { + echo $value; + if (0) { + continue; + } + If (1) { + break; + } +} + +ForEach ([] as $key => $value): + echo $value; + if (0) { + Continue; + } + if (1) { + Break; + } +EndForEach; + +switch ($i) { + case 10: + echo 0; + break; + default: + break; +} + +Switch ($i): + Case 10: + echo 0; + Break; + Default: + Break; +EndSwitch; + +match ($true) { + null => 'null', +}; + +goto main_sub3; +declare(ticks = 123); +main_sub3: + +require __FILE__; +include __FILE__; +require_once __FILE__; +include_once __FILE__; +Require __FILE__; +Include __FILE__; +Require_Once __FILE__; +Include_Once __FILE__; diff --git a/tests/src/disallowed/controlStructures.php b/tests/src/disallowed/controlStructures.php new file mode 100644 index 0000000..1837f19 --- /dev/null +++ b/tests/src/disallowed/controlStructures.php @@ -0,0 +1,127 @@ + $value) { + echo $value; + if (0) { + continue; + } + If (1) { + break; + } +} + +ForEach ([] as $key => $value): + echo $value; + if (0) { + Continue; + } + if (1) { + Break; + } +EndForEach; + +switch ($i) { + case 10: + echo 0; + break; + default: + break; +} + +Switch ($i): + Case 10: + echo 0; + Break; + Default: + Break; +EndSwitch; + +match ($true) { + null => 'null', +}; + +goto main_sub3; +declare(ticks = 123); +main_sub3: + +require __FILE__; +include __FILE__; +require_once __FILE__; +include_once __FILE__; +Require __FILE__; +Include __FILE__; +Require_Once __FILE__; +Include_Once __FILE__;