Skip to content

Commit fbd7a2f

Browse files
committed
Initial commit
0 parents  commit fbd7a2f

16 files changed

+319
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
*~
2+
/vendor/*
3+
.*~
4+
/composer.lock
5+
/phpunit.xml
6+
/tmp
7+
/build/

.travis.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
language: php
2+
sudo: false
3+
php:
4+
- 7.1
5+
- 7.2
6+
env:
7+
global:
8+
# We need to prefer source to get PHPStan test directory. Otherwise, it is removed from ZIP
9+
- DEFAULT_COMPOSER_FLAGS="--no-interaction --no-progress --optimize-autoloader --prefer-source"
10+
- TASK_TESTS=1
11+
- TASK_TESTS_COVERAGE=0
12+
- TASK_CS=1
13+
- TASK_SCA=0
14+
matrix:
15+
- COMPOSER_FLAGS="--prefer-lowest"
16+
- COMPOSER_FLAGS=""
17+
cache:
18+
directories:
19+
- "$HOME/.composer/cache"
20+
before_install:
21+
- travis_retry composer global require $DEFAULT_COMPOSER_FLAGS hirak/prestissimo
22+
install:
23+
- travis_retry composer update $DEFAULT_COMPOSER_FLAGS $COMPOSER_FLAGS
24+
- composer info -D | sort
25+
- mkdir tmp
26+
script:
27+
- vendor/bin/phpunit --verbose;
28+
- composer phpstan
29+
- composer cs-check
30+
after_success:
31+
- vendor/bin/coveralls -v

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[![Latest Stable Version](https://poser.pugx.org/thecodingmachine/phpstan-safe-rules/v/stable)](https://packagist.org/packages/thecodingmachine/phpstan-safe-rules)
2+
[![Total Downloads](https://poser.pugx.org/thecodingmachine/phpstan-safe-rules/downloads)](https://packagist.org/packages/thecodingmachine/phpstan-safe-rules)
3+
[![Latest Unstable Version](https://poser.pugx.org/thecodingmachine/phpstan-safe-rules/v/unstable)](https://packagist.org/packages/thecodingmachine/phpstan-safe-rules)
4+
[![License](https://poser.pugx.org/thecodingmachine/phpstan-safe-rules/license)](https://packagist.org/packages/thecodingmachine/phpstan-safe-rules)
5+
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/thecodingmachine/phpstan-safe-rules/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/thecodingmachine/phpstan-safe-rules/?branch=master)
6+
[![Build Status](https://travis-ci.org/thecodingmachine/phpstan-safe-rules.svg?branch=master)](https://travis-ci.org/thecodingmachine/phpstan-safe-rules)
7+
[![Coverage Status](https://coveralls.io/repos/thecodingmachine/phpstan-safe-rules/badge.svg?branch=master&service=github)](https://coveralls.io/github/thecodingmachine/phpstan-safe-rules?branch=master)
8+
9+
10+
PHPStan rules for thecodingmachine/safe
11+
=======================================
12+
13+
The [thecodingmachine/safe](https://github.com/thecodingmachine/safe) package provides a set of core PHP functions rewritten to throw exceptions instead of returning `false` when an error is encountered.
14+
15+
This PHPStan rule will help you detect unsafe function call and will propose you to use the `thecodingmachine/safe` variant instead.
16+
17+
Please read [thecodingmachine/safe documentation](https://github.com/thecodingmachine/safe) for details about installation and usage.

composer.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "thecodingmachine/phpstan-safe-rule",
3+
"description": "A PHPStan rule to detect safety issues. Must be used in conjunction with thecodingmachine/safe",
4+
"type": "library",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "David Négrier",
9+
"email": "d.negrier@thecodingmachine.com"
10+
}
11+
],
12+
"require": {
13+
"php": "^7.1",
14+
"phpstan/phpstan": "^0.10",
15+
"thecodingmachine/safe": "^0.1"
16+
},
17+
"require-dev": {
18+
"phpunit/phpunit": "^7.1",
19+
"php-coveralls/php-coveralls": "^2.1",
20+
"squizlabs/php_codesniffer": "^3.2"
21+
},
22+
"autoload": {
23+
"psr-4": {
24+
"TheCodingMachine\\Safe\\PHPStan\\": "src/"
25+
}
26+
},
27+
"autoload-dev": {
28+
"psr-4": {
29+
"TheCodingMachine\\Safe\\PHPStan\\": "tests/"
30+
}
31+
},
32+
"scripts": {
33+
"phpstan": "phpstan analyse src -c phpstan.neon --level=7 --no-progress -vvv",
34+
"cs-fix": "phpcbf",
35+
"cs-check": "phpcs"
36+
},
37+
"extra": {
38+
"branch-alias": {
39+
"dev-master": "0.1-dev"
40+
}
41+
}
42+
}

phpstan-safe-rule.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
services:
2+
-
3+
class: TheCodingMachine\Safe\PHPStan\Rules\UseSafeFunctionsRule
4+
tags:
5+
- phpstan.rules.rule

phpstan.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
parameters:
2+
ignoreErrors:
3+
includes:
4+
- phpstan-safe-rule.neon

phpunit.xml.dist

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<phpunit
2+
bootstrap="tests/bootstrap.php"
3+
colors="true"
4+
backupGlobals="false"
5+
backupStaticAttributes="false"
6+
beStrictAboutChangesToGlobalState="true"
7+
beStrictAboutOutputDuringTests="true"
8+
beStrictAboutTestsThatDoNotTestAnything="true"
9+
beStrictAboutTodoAnnotatedTests="true"
10+
failOnRisky="true"
11+
failOnWarning="true"
12+
convertErrorsToExceptions="true"
13+
convertNoticesToExceptions="true"
14+
convertWarningsToExceptions="true"
15+
>
16+
<testsuites>
17+
<testsuite name="Test suite">
18+
<directory>./tests/</directory>
19+
</testsuite>
20+
</testsuites>
21+
<filter>
22+
<whitelist>
23+
<directory suffix=".php">./src</directory>
24+
</whitelist>
25+
</filter>
26+
<logging>
27+
<log
28+
type="coverage-text"
29+
target="php://stdout"
30+
showUncoveredFiles="true"
31+
showOnlySummary="true"
32+
/>
33+
<log type="coverage-html" target="build/coverage"/>
34+
<log type="coverage-clover" target="build/logs/clover.xml"/>
35+
</logging>
36+
</phpunit>

src/Rules/UseSafeFunctionsRule.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\Safe\PHPStan\Rules;
5+
6+
use PhpParser\Node;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Rules\Rule;
11+
use PHPStan\ShouldNotHappenException;
12+
use TheCodingMachine\Safe\PHPStan\Utils\FunctionListLoader;
13+
14+
/**
15+
* This rule checks that no superglobals are used in code.
16+
*/
17+
class UseSafeFunctionsRule implements Rule
18+
{
19+
public function getNodeType(): string
20+
{
21+
return Node\Expr\FuncCall::class;
22+
}
23+
24+
/**
25+
* @param Node\Expr\FuncCall $node
26+
* @param \PHPStan\Analyser\Scope $scope
27+
* @return string[]
28+
*/
29+
public function processNode(Node $node, Scope $scope): array
30+
{
31+
if (!$node->name instanceof Node\Name) {
32+
return [];
33+
}
34+
$functionName = $node->name->toString();
35+
$unsafeFunctions = FunctionListLoader::getFunctionList();
36+
37+
if (isset($unsafeFunctions[$functionName])) {
38+
return ["Function $functionName is unsafe to use. It can return FALSE instead of throwing an exception. Please add 'use function Safe\\$functionName;' at the beginning of the file to use the variant provided by the 'thecodingmachine/safe' library."];
39+
}
40+
41+
return [];
42+
}
43+
}

src/Utils/FunctionListLoader.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\Safe\PHPStan\Utils;
5+
6+
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Reflection\MethodReflection;
10+
11+
class FunctionListLoader
12+
{
13+
private static $functions;
14+
15+
/**
16+
* @return string[]
17+
*/
18+
public static function getFunctionList(): array
19+
{
20+
if (self::$functions === null) {
21+
if (\file_exists(__DIR__.'/../../../safe/generated/functionsList.php')) {
22+
$functions = require __DIR__.'/../../../safe/generated/functionsList.php';
23+
} elseif (\file_exists(__DIR__.'/../../vendor/thecodingmachine/safe/generated/functionsList.php')) {
24+
$functions = require __DIR__.'/../../vendor/thecodingmachine/safe/generated/functionsList.php';
25+
} else {
26+
throw new \RuntimeException('Could not find thecodingmachine/safe\'s functionsList.php file.');
27+
}
28+
// Let's index these functions by their name
29+
self::$functions = \array_combine($functions, $functions);
30+
}
31+
return self::$functions;
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace TheCodingMachine\Safe\PHPStan\Rules;
4+
5+
use PHPStan\Testing\RuleTestCase;
6+
7+
class UseSafeFunctionsRuleTest extends RuleTestCase
8+
{
9+
protected function getRule(): \PHPStan\Rules\Rule
10+
{
11+
return new UseSafeFunctionsRule();
12+
}
13+
14+
public function testCatch()
15+
{
16+
$this->analyse([__DIR__ . '/data/fopen.php'], [
17+
[
18+
"Function fopen is unsafe to use. It can return FALSE instead of throwing an exception. Please add 'use function Safe\\fopen;' at the beginning of the file to use the variant provided by the 'thecodingmachine/safe' library.",
19+
4,
20+
],
21+
]);
22+
}
23+
24+
public function testNoCatchSafe()
25+
{
26+
$this->analyse([__DIR__ . '/data/safe_fopen.php'], []);
27+
}
28+
29+
public function testExprCall()
30+
{
31+
$this->analyse([__DIR__ . '/data/undirect_call.php'], []);
32+
}
33+
}

0 commit comments

Comments
 (0)