Skip to content

Commit 95d9d2a

Browse files
committed
First working version
1 parent abfbcd3 commit 95d9d2a

29 files changed

+899
-0
lines changed

.editorconfig

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
charset = utf-8
7+
trim_trailing_whitespace = true
8+
9+
[*.{php,phpt}]
10+
indent_style = tab
11+
indent_size = 4
12+
13+
[*.xml]
14+
indent_style = tab
15+
indent_size = 4
16+
17+
[*.neon]
18+
indent_style = tab
19+
indent_size = 4
20+
21+
[*.{yaml,yml}]
22+
indent_style = space
23+
indent_size = 2
24+
25+
[composer.json]
26+
indent_style = tab
27+
indent_size = 4

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/tests export-ignore

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/composer.lock
2+
/vendor

.travis.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
language: php
2+
php:
3+
- 7.0
4+
- 7.1
5+
- 7.2
6+
before_script:
7+
- composer self-update
8+
- composer install
9+
script:
10+
- vendor/bin/phing

README.md

+26
Original file line numberDiff line numberDiff line change
@@ -1 +1,27 @@
11
# Extra strict and opinionated rules for PHPStan
2+
3+
[![Build Status](https://travis-ci.org/phpstan/phpstan-strict-rules.svg)](https://travis-ci.org/phpstan/phpstan-strict-rules)
4+
[![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-strict-rules/v/stable)](https://packagist.org/packages/phpstan/phpstan-strict-rules)
5+
[![License](https://poser.pugx.org/phpstan/phpstan-strict-rules/license)](https://packagist.org/packages/phpstan/phpstan-strict-rules)
6+
7+
[PHPStan](https://github.com/phpstan/phpstan) focuses on finding bugs in your code. But in PHP there's a lot of leeway in how stuff can be written. This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming:
8+
9+
* Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `||`.
10+
* Functions `in_array` and `array_search` must be called with third parameter `$strict` set to `true` to search values with matching types only.
11+
12+
Additional rules are coming in subsequent releases!
13+
14+
## Usage
15+
16+
To use these rules, require it in [Composer](https://getcomposer.org/):
17+
18+
```
19+
composer require --dev phpstan/phpstan-strict-rules
20+
```
21+
22+
And include rules.neon in your project's PHPStan config:
23+
24+
```
25+
includes:
26+
- vendor/phpstan/phpstan-strict-rules/rules.neon
27+
```

build.xml

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<project name="Extra strict and opinionated rules for PHPStan" default="check">
3+
4+
<target name="check" depends="
5+
composer,
6+
lint,
7+
cs,
8+
tests,
9+
phpstan
10+
"/>
11+
12+
<target name="composer">
13+
<exec
14+
executable="composer"
15+
logoutput="true"
16+
passthru="true"
17+
checkreturn="true"
18+
>
19+
<arg value="install"/>
20+
</exec>
21+
</target>
22+
23+
<target name="lint">
24+
<exec
25+
executable="vendor/bin/parallel-lint"
26+
logoutput="true"
27+
passthru="true"
28+
checkreturn="true"
29+
>
30+
<arg value="--exclude"/>
31+
<arg path="tests/PHPStan/Analyser/data"/>
32+
<arg path="src" />
33+
<arg path="tests" />
34+
</exec>
35+
</target>
36+
37+
<target name="cs">
38+
<exec
39+
executable="vendor/bin/phpcs"
40+
logoutput="true"
41+
passthru="true"
42+
checkreturn="true"
43+
>
44+
<arg value="--extensions=php"/>
45+
<arg value="--encoding=utf-8"/>
46+
<arg value="--tab-width=4"/>
47+
<arg value="--ignore=tests/*/data"/>
48+
<arg value="-sp"/>
49+
<arg path="src"/>
50+
<arg path="tests"/>
51+
</exec>
52+
</target>
53+
54+
<target name="cs-fix">
55+
<exec
56+
executable="vendor/bin/phpcbf"
57+
logoutput="true"
58+
passthru="true"
59+
checkreturn="true"
60+
>
61+
<arg value="--extensions=php"/>
62+
<arg value="--encoding=utf-8"/>
63+
<arg value="--tab-width=4"/>
64+
<arg value="--ignore=tests/*/data"/>
65+
<arg value="-sp"/>
66+
<arg path="src"/>
67+
<arg path="tests"/>
68+
</exec>
69+
</target>
70+
71+
<target name="tests">
72+
<exec
73+
executable="vendor/bin/phpunit"
74+
logoutput="true"
75+
passthru="true"
76+
checkreturn="true"
77+
>
78+
<arg value="-c"/>
79+
<arg value="tests/phpunit.xml"/>
80+
<arg path="tests"/>
81+
</exec>
82+
</target>
83+
84+
<target name="phpstan">
85+
<exec
86+
executable="vendor/bin/phpstan"
87+
logoutput="true"
88+
passthru="true"
89+
checkreturn="true"
90+
>
91+
<arg value="analyse"/>
92+
<arg value="-l"/>
93+
<arg value="7"/>
94+
<arg value="-c"/>
95+
<arg path="phpstan.neon"/>
96+
<arg path="src"/>
97+
<arg path="tests"/>
98+
</exec>
99+
</target>
100+
101+
</project>

composer.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "phpstan/phpstan-strict-rules",
3+
"description": "Extra strict and opinionated rules for PHPStan",
4+
"license": ["MIT"],
5+
"minimum-stability": "dev",
6+
"prefer-stable": true,
7+
"extra": {
8+
"branch-alias": {
9+
"dev-master": "0.9-dev"
10+
}
11+
},
12+
"require": {
13+
"php": "~7.0",
14+
"phpstan/phpstan": "^0.9"
15+
},
16+
"require-dev": {
17+
"consistence/coding-standard": "^2.0.0",
18+
"jakub-onderka/php-parallel-lint": "^0.9.2",
19+
"phing/phing": "^2.16.0",
20+
"phpstan/phpstan-phpunit": "^0.9",
21+
"phpunit/phpunit": "^6.4",
22+
"slevomat/coding-standard": "^3.3.0"
23+
},
24+
"autoload": {
25+
"psr-4": {
26+
"PHPStan\\": "src/"
27+
}
28+
},
29+
"autoload-dev": {
30+
"classmap": ["tests/"]
31+
}
32+
}

phpcs.xml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0"?>
2+
<ruleset name="PHPStan">
3+
<rule ref="vendor/consistence/coding-standard/Consistence/ruleset.xml">
4+
<exclude name="Squiz.Functions.GlobalFunction.Found"/>
5+
</rule>
6+
<rule ref="vendor/slevomat/coding-standard/SlevomatCodingStandard/ruleset.xml">
7+
<exclude name="SlevomatCodingStandard.Classes.ClassConstantVisibility.MissingConstantVisibility"/>
8+
<exclude name="SlevomatCodingStandard.Files.TypeNameMatchesFileName"/>
9+
<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameAfterKeyword"/>
10+
<exclude name="SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces"/>
11+
<exclude name="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly"/>
12+
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableParameterTypeHintSpecification"/>
13+
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableReturnTypeHintSpecification"/>
14+
<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation.NonFullyQualifiedClassName"/>
15+
<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalConstants"/>
16+
<exclude name="SlevomatCodingStandard.Namespaces.FullyQualifiedGlobalFunctions"/>
17+
<exclude name="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue"/>
18+
</rule>
19+
<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses">
20+
<properties>
21+
<property name="caseSensitive" value="false"/>
22+
</properties>
23+
</rule>
24+
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
25+
<properties>
26+
<property name="newlinesCountBetweenOpenTagAndDeclare" value="0"/>
27+
</properties>
28+
</rule>
29+
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration">
30+
<properties>
31+
<property name="usefulAnnotations" type="array" value="
32+
@dataProvider,
33+
@requires
34+
"/>
35+
<property name="enableNullableTypeHints" type="false" />
36+
<property name="enableVoidTypeHint" type="false" />
37+
</properties>
38+
</rule>
39+
</ruleset>

phpstan.neon

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
includes:
2+
- vendor/phpstan/phpstan-phpunit/extension.neon
3+
- vendor/phpstan/phpstan-phpunit/rules.neon
4+
- rules.neon
5+
6+
parameters:
7+
excludes_analyse:
8+
- */tests/*/data/*

rules.neon

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
services:
2+
-
3+
class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanAndRule
4+
tags:
5+
- phpstan.rules.rule
6+
7+
-
8+
class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanNotRule
9+
tags:
10+
- phpstan.rules.rule
11+
12+
-
13+
class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule
14+
tags:
15+
- phpstan.rules.rule
16+
17+
-
18+
class: PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule
19+
tags:
20+
- phpstan.rules.rule
21+
22+
-
23+
class: PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule
24+
tags:
25+
- phpstan.rules.rule
26+
27+
-
28+
class: PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule
29+
tags:
30+
- phpstan.rules.rule
31+
32+
-
33+
class: PHPStan\Rules\StrictCalls\StrictFunctionCallsRule
34+
tags:
35+
- phpstan.rules.rule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\BooleansInConditions;
4+
5+
class BooleanInBooleanAndRule implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\BooleanAnd::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
22+
$messages = [];
23+
if (!BooleanRuleHelper::passesAsBoolean($leftType)) {
24+
$messages[] = sprintf(
25+
'Only booleans are allowed in &&, %s given on the left side.',
26+
$leftType->describe()
27+
);
28+
}
29+
30+
$rightType = $scope->getType($node->right);
31+
if (!BooleanRuleHelper::passesAsBoolean($rightType)) {
32+
$messages[] = sprintf(
33+
'Only booleans are allowed in &&, %s given on the right side.',
34+
$rightType->describe()
35+
);
36+
}
37+
38+
return $messages;
39+
}
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\BooleansInConditions;
4+
5+
class BooleanInBooleanNotRule implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BooleanNot::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BooleanNot $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$expressionType = $scope->getType($node->expr);
21+
if (BooleanRuleHelper::passesAsBoolean($expressionType)) {
22+
return [];
23+
}
24+
25+
return [
26+
sprintf(
27+
'Only booleans are allowed in a negated boolean, %s given.',
28+
$expressionType->describe()
29+
),
30+
];
31+
}
32+
33+
}

0 commit comments

Comments
 (0)