-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #95 from bestit/feature/PHPCS-318-concat-security
PHPCS-318 Add Sniff against no-brackets-calcs
- Loading branch information
Showing
5 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
src/Standards/BestIt/Sniffs/Strings/ConcatCalculationSniff.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BestIt\Sniffs\Strings; | ||
|
||
use BestIt\CodeSniffer\CodeError; | ||
use BestIt\Sniffs\AbstractSniff; | ||
use const T_CLOSE_PARENTHESIS; | ||
use const T_MINUS; | ||
use const T_OPEN_CURLY_BRACKET; | ||
use const T_OPEN_PARENTHESIS; | ||
use const T_PLUS; | ||
use const T_SEMICOLON; | ||
use const T_STRING_CONCAT; | ||
|
||
/** | ||
* You MUST encapsulate your calculation with brackets. | ||
* | ||
* @author blange <[email protected]> | ||
* @package BestIt\Sniffs\Strings | ||
*/ | ||
class ConcatCalculationSniff extends AbstractSniff | ||
{ | ||
/** | ||
* You MUST encapsulate your calculation with brackets. | ||
* | ||
* @var string | ||
*/ | ||
public const CODE_CALCULATION_WITHOUT_BRACKETS = 'CalculationWithoutBrackets'; | ||
|
||
/** | ||
* The error message for the error. | ||
* | ||
* @var string | ||
*/ | ||
private const MESSAGE_CALCULATION_WITHOUT_BRACKETS = 'You should encapsulate your calculation with brackets.'; | ||
|
||
/** | ||
* Caching of the checks, already done. | ||
* | ||
* @var array | ||
*/ | ||
private static array $alreadyDoneChecks = []; | ||
|
||
/** | ||
* Checks for a possible wrong calculation after the token. | ||
* | ||
* @return void | ||
* @throws CodeError | ||
* | ||
*/ | ||
protected function checkForCalculationAfterToken(): void | ||
{ | ||
$nextClosingPos = $this->file->findNext( | ||
[T_OPEN_PARENTHESIS, T_SEMICOLON, T_STRING_CONCAT], | ||
$this->getStackPos() + 1, | ||
); | ||
|
||
$nextMathPos = $this->file->findNext( | ||
[T_MINUS, T_PLUS], | ||
$this->getStackPos() + 1, | ||
(int) $nextClosingPos, | ||
); | ||
|
||
if ($nextMathPos !== false) { | ||
throw new CodeError( | ||
static::CODE_CALCULATION_WITHOUT_BRACKETS, | ||
self::MESSAGE_CALCULATION_WITHOUT_BRACKETS, | ||
$nextMathPos, | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* Checks for a possible wrong calculation before the token. | ||
* | ||
* @return $this | ||
* @throws CodeError | ||
* | ||
*/ | ||
protected function checkForCalculationBeforeToken(): self | ||
{ | ||
$prevClosingPos = $this->file->findPrevious( | ||
[T_CLOSE_PARENTHESIS, T_OPEN_CURLY_BRACKET, T_SEMICOLON], | ||
$this->getStackPos() + 1, | ||
); | ||
|
||
$prevMathPos = $this->file->findPrevious( | ||
[T_MINUS, T_PLUS], | ||
$this->getStackPos() + 1, | ||
(int) $prevClosingPos, | ||
); | ||
|
||
if (($prevMathPos !== false) && !$this->isCheckAlreadyDone($prevMathPos)) { | ||
throw new CodeError( | ||
static::CODE_CALCULATION_WITHOUT_BRACKETS, | ||
self::MESSAGE_CALCULATION_WITHOUT_BRACKETS, | ||
$prevMathPos, | ||
); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Checks if the check for the given position in the actual file was already done. | ||
* | ||
* @param int $prevMathPos | ||
* @param bool $withSave | ||
* | ||
* @return bool | ||
*/ | ||
private function isCheckAlreadyDone(int $prevMathPos, bool $withSave = true): bool | ||
{ | ||
$checkMarker = $this->file->getFilename() . $prevMathPos; | ||
$isDone = @self::$alreadyDoneChecks[$checkMarker]; | ||
|
||
if ($withSave) { | ||
self::$alreadyDoneChecks[$checkMarker] = true; | ||
} | ||
|
||
return (bool) $isDone; | ||
} | ||
|
||
/** | ||
* Processes the token. | ||
* | ||
* @return void | ||
* @throws CodeError | ||
* | ||
*/ | ||
protected function processToken(): void | ||
{ | ||
// Who does 1 + 2 . 1 + 2 in reality? So I skipped it for now! | ||
$this | ||
->checkForCalculationBeforeToken() | ||
->checkForCalculationAfterToken(); | ||
} | ||
|
||
/** | ||
* Registers the tokens on string concatinations. | ||
* | ||
* @return array | ||
*/ | ||
public function register(): array | ||
{ | ||
return [T_STRING_CONCAT]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BestIt\Sniffs\Strings; | ||
|
||
use BestIt\Sniffs\DefaultSniffIntegrationTestTrait; | ||
use BestIt\Sniffs\TestTokenRegistrationTrait; | ||
use BestIt\SniffTestCase; | ||
use BestIt\TestRequiredConstantsTrait; | ||
use const T_STRING_CONCAT; | ||
|
||
/** | ||
* Test ConcatCalculationSniff. | ||
* | ||
* @author blange <[email protected]> | ||
* @package BestIt\Sniffs\Strings | ||
*/ | ||
class ConcatCalculationSniffTest extends SniffTestCase | ||
{ | ||
use DefaultSniffIntegrationTestTrait; | ||
use TestRequiredConstantsTrait; | ||
use TestTokenRegistrationTrait; | ||
|
||
/** | ||
* Checks the registration of the sniff. | ||
* | ||
* @return array | ||
*/ | ||
protected function getExpectedTokens(): array | ||
{ | ||
return [T_STRING_CONCAT]; | ||
} | ||
|
||
/** | ||
* Checks the required constants. | ||
* | ||
* @return iterable | ||
*/ | ||
public function getRequiredConstantAsserts(): iterable | ||
{ | ||
return [ | ||
'CODE_CALCULATION_WITHOUT_BRACKETS' => [ | ||
'CODE_CALCULATION_WITHOUT_BRACKETS', | ||
'CalculationWithoutBrackets', | ||
], | ||
]; | ||
} | ||
|
||
/** | ||
* Sets up the test. | ||
* | ||
* @return void | ||
*/ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->fixture = new ConcatCalculationSniff(); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
tests/Sniffs/Strings/Fixtures/ConcatCalculationSniff/correct/Correct.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BestIt\Sniffs\Strings\Fixtures\ConcatCalculationSniff\correct; | ||
|
||
class Correct | ||
{ | ||
public function __construct() | ||
{ | ||
$b = 10; | ||
echo 'This string is korrect + ' . (1 + $b) . ' und enthält mehrere Concats: ' . ($b -1); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
...rings/Fixtures/ConcatCalculationSniff/with_errors/CalculationWithoutBrackets.12,13,15.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BestIt\Sniffs\Strings\Fixtures\ConcatCalculationSniff\with_errors; | ||
|
||
class Errors | ||
{ | ||
public function __construct() | ||
{ | ||
$b = 10; | ||
echo $b + 2 . | ||
'This string is korrect + ' . 1 + $b . ' und enthält mehrere Concats: ' . | ||
$b | ||
- | ||
1 | ||
; | ||
} | ||
} |