Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ Table::hasMany, Table::belongsToMany, Table::hasOne and AssociationCollection::l
### AddBehaviorExistsClassRule
This rule check if the target behavior has a valid class when calling to Table::addBehavior and BehaviorRegistry::load.

### DisallowDebugFunctionsRule
This rule disallow use of debug functions (dd, debug, debug_print_backtrace, debug_zval_dump, pr, print_r, stacktrace, var_dump and var_export).
The use of these functions in shipped code is discouraged because they can leak sensitive information or clutter output.

### DisallowEntityArrayAccessRule
This rule disallow array access to entity in favor of object notation, is easier to detect a wrong property and to refactor code.

Expand Down
6 changes: 6 additions & 0 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ parameters:
getMailerExistsClassRule: true
loadComponentExistsClassRule: true
controllerMethodMustBeUsedRule: true
disallowDebugFunctionsRule: true
parametersSchema:
cakeDC: structure([
addAssociationExistsTableClassRule: anyOf(bool(), arrayOf(bool()))
Expand All @@ -20,6 +21,7 @@ parametersSchema:
getMailerExistsClassRule: anyOf(bool(), arrayOf(bool()))
loadComponentExistsClassRule: anyOf(bool(), arrayOf(bool()))
controllerMethodMustBeUsedRule: anyOf(bool(), arrayOf(bool()))
disallowDebugFunctionsRule: anyOf(bool(), arrayOf(bool()))
])

conditionalTags:
Expand All @@ -43,6 +45,8 @@ conditionalTags:
phpstan.rules.rule: %cakeDC.tableGetMatchOptionsTypesRule%
CakeDC\PHPStan\Rule\Model\OrmSelectQueryFindMatchOptionsTypesRule:
phpstan.rules.rule: %cakeDC.ormSelectQueryFindMatchOptionsTypesRule%
CakeDC\PHPStan\Rule\Debug\DisallowDebugFunctionsRule:
phpstan.rules.rule: %cakeDC.disallowDebugFunctionsRule%

services:
-
Expand All @@ -65,3 +69,5 @@ services:
class: CakeDC\PHPStan\Rule\Model\TableGetMatchOptionsTypesRule
-
class: CakeDC\PHPStan\Rule\Model\OrmSelectQueryFindMatchOptionsTypesRule
-
class: CakeDC\PHPStan\Rule\Debug\DisallowDebugFunctionsRule
72 changes: 72 additions & 0 deletions src/Rule/Debug/DisallowDebugFunctionsRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);

namespace CakeDC\PHPStan\Rule\Debug;

use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

class DisallowDebugFunctionsRule implements Rule
{
protected const BASIC = 'basic';
protected const RETURNABLE = 'returnable';

/**
* @var array<string, string>
*/
private array $disallowedFunctions = [
'dd' => self::BASIC,
'debug' => self::BASIC,
'debug_print_backtrace' => self::BASIC,
'debug_zval_dump' => self::BASIC,
'pr' => self::BASIC,
'print_r' => self::RETURNABLE,
'stacktrace' => self::BASIC,
'var_dump' => self::BASIC,
'var_export' => self::RETURNABLE,
];

/**
* @inheritDoc
*/
public function getNodeType(): string
{
return FuncCall::class;
}

/**
* @inheritDoc
*/
public function processNode(Node $node, Scope $scope): array
{
assert($node instanceof FuncCall);
if (!$node->name instanceof Node\Name) {
return [];
}
$usedName = $node->name->name;
$name = strtolower($usedName);
if (!isset($this->disallowedFunctions[$name])) {
return [];
}
if ($this->disallowedFunctions[$name] === self::RETURNABLE) {
$arg = $node->getArgs()[1]->value ?? null;
if ($arg instanceof ConstFetch && $arg->name->name === 'true') {
return [];
}
}

return [
RuleErrorBuilder::message(sprintf(
'Use of debug function "%s" is not allowed. %s',
$usedName,
'The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
))
->identifier('cake.debug.debugFunctionUse')
->build(),
];
}
}
84 changes: 84 additions & 0 deletions tests/TestCase/Rule/Debug/DisallowDebugFunctionsRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);

namespace CakeDC\PHPStan\Test\TestCase\Rule\Debug;

use CakeDC\PHPStan\Rule\Debug\DisallowDebugFunctionsRule;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

class DisallowDebugFunctionsRuleTest extends RuleTestCase
{
/**
* @return \PHPStan\Rules\Rule
*/
protected function getRule(): Rule
{
// getRule() method needs to return an instance of the tested rule
return new DisallowDebugFunctionsRule();
}

/**
* @return void
*/
public function testRule(): void
{
// first argument: path to the example file that contains some errors that should be reported by MyRule
// second argument: an array of expected errors,
// each error consists of the asserted error message, and the asserted error file line
$this->analyse([__DIR__ . '/Fake/FailingDebugUseLogic.php'], [
[
'Use of debug function "debug" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
14, // asserted error line
],
[
'Use of debug function "debug_print_backtrace" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
15, // asserted error line
],
[
'Use of debug function "debug_zval_dump" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
16, // asserted error line
],
[
'Use of debug function "print_r" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
19, // asserted error line
],
[
'Use of debug function "print_r" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
20, // asserted error line
],
[
'Use of debug function "var_dump" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
21, // asserted error line
],
[
'Use of debug function "var_export" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
23, // asserted error line
],
[
'Use of debug function "var_export" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
24, // asserted error line
],
[
'Use of debug function "stackTrace" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
25, // asserted error line
],
[
'Use of debug function "pr" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
26, // asserted error line
],
[
'Use of debug function "dd" is not allowed. The use in shipped code is discouraged because they can leak sensitive information or clutter output.',
28, // asserted error line
],
]);
}

/**
* @inheritDoc
*/
public static function getAdditionalConfigFiles(): array
{
return [__DIR__ . '/../../../../extension.neon'];
}
}
30 changes: 30 additions & 0 deletions tests/TestCase/Rule/Debug/Fake/FailingDebugUseLogic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);

namespace CakeDC\PHPStan\Test\TestCase\Rule\Debug\Fake;

class FailingDebugUseLogic
{
/**
* @return void
*/
public function execute(): void
{
$list = [1, 2, 3, 4];
debug($list);//Error
debug_print_backtrace();//Error
debug_zval_dump('Hello World');//Error
ksort($list);//Not a debug should not fail
print_r(['Hello World!'], true);//No error, text is returned
print_r(['Hello World!']);//Error
print_r(['Hello World!'], false);//Error
var_dump(['a' => 1, 'b' => 2, 'c' => 3]);//Error
var_export($list, true);//No error, text is returned
var_export($list);//Error
var_export($list, false);//Error
stackTrace();
pr(['b' => 2, 'c' => 3]);
//Last
dd(['a' => 1, 'b' => 2, 'c' => 3]);
}
}