Skip to content

Commit 86cd118

Browse files
authored
Merge pull request #63 from magento-ecg/ecgm2-added-extdn-rules
Ecgm2 added extdn rules
2 parents c15fe87 + 4dadff8 commit 86cd118

23 files changed

+700
-80
lines changed

Ecg/Sniffs/PHP/NamespaceSniff.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,42 @@
99

1010
class NamespaceSniff implements Sniff
1111
{
12+
const NAME_T_NS_NAMESPACE = 'T_NS_SEPARATOR';
13+
14+
private $tokens = [];
15+
16+
/**
17+
* @return array|int[]|mixed[]
18+
*/
1219
public function register()
1320
{
1421
return [
1522
T_CATCH
1623
];
1724
}
1825

26+
/**
27+
* @param File $phpcsFile
28+
* @param $stackPtr
29+
* @return int|void
30+
*/
1931
public function process(File $phpcsFile, $stackPtr)
2032
{
2133
if ($phpcsFile->findNext(T_NAMESPACE, 0) === false) {
2234
return;
2335
}
2436

25-
$tokens = $phpcsFile->getTokens();
37+
$this->tokens = $phpcsFile->getTokens();
2638

2739
$endOfTryStatement = $phpcsFile->findEndOfStatement($stackPtr);
28-
2940
$posOfCatchVariable = $phpcsFile->findNext(T_VARIABLE, $stackPtr, $endOfTryStatement);
30-
3141
$posOfExceptionClassName = $phpcsFile->findNext(T_STRING, $stackPtr, $posOfCatchVariable);
32-
3342
$posOfNsSeparator = $phpcsFile->findNext(T_NS_SEPARATOR, $stackPtr, $posOfExceptionClassName);
3443

3544
if ($posOfNsSeparator === false) {
36-
$exceptionClassName = trim($tokens[$posOfExceptionClassName]['content']);
37-
$posOfClassInUse = $phpcsFile->findNext(T_STRING, 0, $stackPtr, false, $exceptionClassName);
38-
if ($posOfClassInUse === false || $tokens[$posOfClassInUse]['level'] != 0) {
45+
$exceptionClassName = trim($this->tokens[$posOfExceptionClassName]['content']);
46+
$posOfClassInUse = $this->findNextClassName($phpcsFile, 0, $stackPtr, $exceptionClassName);
47+
if ($posOfClassInUse === false || $this->tokens[$posOfClassInUse]['level'] !== 0) {
3948
$phpcsFile->addError(
4049
'Namespace for "' . $exceptionClassName . '" class is not specified.',
4150
$posOfExceptionClassName,
@@ -44,4 +53,21 @@ public function process(File $phpcsFile, $stackPtr)
4453
}
4554
}
4655
}
56+
57+
/**
58+
* @param File $phpcsFile
59+
* @param int $startPtr
60+
* @param int $endPtr
61+
* @param string $exceptionClassName
62+
* @return bool|int
63+
*/
64+
private function findNextClassName(File $phpcsFile, $startPtr, $endPtr, $exceptionClassName)
65+
{
66+
$position = $phpcsFile->findNext(T_STRING, $startPtr, $endPtr, false, $exceptionClassName);
67+
if ($this->tokens[$position + 1]['type'] === self::NAME_T_NS_NAMESPACE) {
68+
$position = $this->findNextClassName($phpcsFile, $position + 1, $endPtr, $exceptionClassName);
69+
}
70+
71+
return $position;
72+
}
4773
}

Ecg/Sniffs/Performance/LoopSniff.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
class LoopSniff implements Sniff
88
{
9+
private const GET_BY_METHOD_LSD_PATTERN = 'getBy';
10+
911
protected $countFunctions = [
1012
'sizeof',
1113
'count'
@@ -14,7 +16,9 @@ class LoopSniff implements Sniff
1416
protected $modelLsdMethods = [
1517
'load',
1618
'save',
17-
'delete'
19+
'delete',
20+
'get',
21+
'getList'
1822
];
1923

2024
protected $dataLoadMethods = [
@@ -77,7 +81,10 @@ public function process(File $phpcsFile, $stackPtr)
7781
if (in_array($content, $this->countFunctions)) {
7882
$error = 'Array size calculation function %s detected in loop';
7983
$code = 'ArraySize';
80-
} elseif (in_array($content, $this->modelLsdMethods)) {
84+
} elseif (
85+
in_array($content, $this->modelLsdMethods)
86+
|| 0 === strpos($content, self::GET_BY_METHOD_LSD_PATTERN)
87+
) {
8188
$error = 'Model LSD method %s detected in loop';
8289
$code = 'ModelLSD';
8390
} elseif (in_array($content, $this->dataLoadMethods)) {

Ecg/Tests/PHP/NamespaceUnitTest1.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace N1;
4+
5+
use Magento\Framework\Exception\LocalizedException;
6+
7+
class NSException extends LocalizedException
8+
{
9+
public function checkFalsePositiveNamespaceIssue()
10+
{
11+
$checked = true;
12+
try {
13+
throw new NSException(__('Check false positive NS sniffer'));
14+
} catch (Exception $e) {
15+
$checked = false;
16+
}
17+
18+
return $checked;
19+
}
20+
}
21+
22+
try {
23+
throw new \N1\NSException('Check falsepositive');
24+
} catch (\N1\NSException $e) {
25+
$error = $e->getMessage();
26+
}

Ecg/Tests/Performance/LoopUnitTest.inc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,26 @@ class TestLoop
1010
$collection = Mage::getModel('catalog/product')->getCollection();
1111
do {
1212
$product = Mage::getModel('catalog/product')->load($id);
13-
$c = count($data) = sizeof($data);
13+
$c = count($data) == sizeof($data);
1414
$product2 = $collection->getFirstItem();
1515
$product2->save();
1616
Mage::getModel('catalog/product')->setId($id)->delete();
1717

1818
$id--;
1919
} while ($id);
2020

21-
for ($i = 1; $i <= 100; $i++) {
21+
$array = [];
22+
for ($i = 1; $i <= count($array); $i++) {
2223
$product = Mage::getModel('catalog/product')->load($id);
23-
$c = count($data) = sizeof($data);
24+
$c = count($data) == sizeof($data);
2425
$product2 = $collection->getFirstItem();
2526
$product2->save();
2627
Mage::getModel('catalog/product')->setId($id)->delete();
2728
}
2829

2930
foreach($collection as $product) {
3031
$product = Mage::getModel('catalog/product')->load($id);
31-
$c = count($data) = sizeof($data);
32+
$c = count($data) == sizeof($data);
3233
$product2 = $collection->getFirstItem();
3334
$product2->save();
3435
Mage::getModel('catalog/product')->setId($id)->delete();
@@ -37,7 +38,7 @@ class TestLoop
3738

3839
while ($id) {
3940
$product = Mage::getModel('catalog/product')->load($id);
40-
$c = count($data) = sizeof($data);
41+
$c = count($data) == sizeof($data);
4142
$product2 = $collection->getFirstItem();
4243
$product2->save();
4344
Mage::getModel('catalog/product')->setId($id)->delete();
@@ -51,4 +52,4 @@ foreach ($collection as $item) {
5152
foreach ($item->getData() as $product) {
5253
$product = Mage::getModel('catalog/product')->load($id);
5354
}
54-
}
55+
}

Ecg/ruleset.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
<rule ref="Squiz.Functions.GlobalFunction"/>
3030
<rule ref="Squiz.Operators.IncrementDecrementUsage"/>
31-
<rule ref="Squiz.PHP.DiscouragedFunctions"/>
3231
<rule ref="Squiz.PHP.Eval"/>
3332
<rule ref="Squiz.PHP.GlobalKeyword"/>
3433
<rule ref="Squiz.PHP.NonExecutableCode"/>
@@ -39,6 +38,9 @@
3938
<exclude-pattern>*.phtml</exclude-pattern>
4039
</rule>
4140

41+
<rule ref="Ecg.Security.DiscouragedFunction"/>
42+
<rule ref="Ecg.Security.ForbiddenFunction"/>
43+
4244
<rule ref="Ecg.Security.LanguageConstruct.DirectOutput">
4345
<exclude-pattern>*.phtml</exclude-pattern>
4446
</rule>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace EcgM2\Sniffs\Deprecated;
5+
6+
use PHP_CodeSniffer\Files\File;
7+
use PHP_CodeSniffer\Sniffs\Sniff;
8+
9+
class InheritDocSniff implements Sniff
10+
{
11+
private const ERROR_MESSAGE = 'This DocBlock has been deprecated and should not be used. For more info see ' .
12+
'https://devdocs.magento.com/guides/v2.4/coding-standards/docblock-standard-general.html#inheritdoc';
13+
14+
private const INHERITDOC_COMMENT_TAG = '@inheritdoc';
15+
16+
private const T_DOC_COMMENT_TYPES = [
17+
'T_DOC_COMMENT_TAG',
18+
'T_DOC_COMMENT_STRING'
19+
];
20+
21+
/**
22+
* @return array|int[]|mixed[]
23+
*/
24+
public function register()
25+
{
26+
return [
27+
T_DOC_COMMENT_TAG
28+
];
29+
}
30+
31+
/**
32+
* @param File $phpcsFile
33+
* @param $stackPtr
34+
* @return int|void
35+
*/
36+
public function process(File $phpcsFile, $stackPtr)
37+
{
38+
$tokens = $phpcsFile->getTokens();
39+
foreach ($tokens as $line => $token) {
40+
if (!$this->hasTokenMatch($token)) {
41+
continue;
42+
}
43+
44+
$error = self::ERROR_MESSAGE . ' Found: %s';
45+
$phpcsFile->addError($error, $line, 'Found', $token['content']);
46+
}
47+
}
48+
49+
/**
50+
* @param array $token
51+
* @return bool
52+
*/
53+
private function hasTokenMatch(array $token): bool
54+
{
55+
return false !== stripos($token['content'], self::INHERITDOC_COMMENT_TAG)
56+
&& in_array($token['type'], self::T_DOC_COMMENT_TYPES, true);
57+
}
58+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace EcgM2\Sniffs\Deprecated;
5+
6+
use PHP_CodeSniffer\Files\File;
7+
use PHP_CodeSniffer\Sniffs\Sniff;
8+
9+
class SpaceDocBlockSniff implements Sniff
10+
{
11+
private const ERROR_MESSAGE = 'This DocBlock has been deprecated and should not be used. For more info see ' .
12+
'https://devdocs.magento.com/guides/v2.4/coding-standards/docblock-standard-general.html#documentation-space';
13+
14+
private const INHERITDOC_COMMENT_TAG = [
15+
'@author',
16+
'@category',
17+
'@package',
18+
'@subpackage'
19+
];
20+
21+
/**
22+
* @return array|int[]|mixed[]
23+
*/
24+
public function register()
25+
{
26+
return [
27+
T_DOC_COMMENT_TAG
28+
];
29+
}
30+
31+
/**
32+
* @param File $phpcsFile
33+
* @param $stackPtr
34+
* @return int|void
35+
*/
36+
public function process(File $phpcsFile, $stackPtr)
37+
{
38+
$tokens = $phpcsFile->getTokens();
39+
foreach ($tokens as $line => $token) {
40+
if (!$this->hasTokenMatch($token)) {
41+
continue;
42+
}
43+
44+
$error = self::ERROR_MESSAGE . ' Found: %s';
45+
$phpcsFile->addError($error, $line, 'Found', $token['content']);
46+
}
47+
}
48+
49+
private function hasTokenMatch($token): bool
50+
{
51+
foreach (self::INHERITDOC_COMMENT_TAG as $deprecatedDocBlock) {
52+
if (
53+
$token['type'] === 'T_DOC_COMMENT_TAG'
54+
&& false !== stripos($token['content'], $deprecatedDocBlock)
55+
) {
56+
return true;
57+
}
58+
}
59+
60+
return false;
61+
}
62+
}

EcgM2/Sniffs/Templates/TemplateObjectManagerSniff.md renamed to EcgM2/Sniffs/Performance/ObjectManagerUsageSniff.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Rule: Do not use the object manager in templates
1+
# Rule: Do not use the object manager in templates or Classes
22
## Background
33
The Object Manager is responsible to create concrete objects for a required interface, and to generate code e.g. for adding plugins, factories, proxies.
44
It is used transparently by requesting a class/interface name as constructor dependency or in layout XML.
@@ -16,7 +16,7 @@ Bypassing this system leads to hidden dependencies and potentially to too many o
1616
Besides, for separation of appearance and logic, a template should contain as little and uncomplex PHP code as possible.
1717

1818
## How it works
19-
For all PHTML files, the sniff looks for string tokens "ObjectManager".
19+
For all PHTML and PHP files, the sniff looks for string tokens "ObjectManager".
2020

2121
## How to fix
2222
Given, your template contains code like this:

0 commit comments

Comments
 (0)