init
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\CodeMessDetector\Rule\Design;
|
||||
|
||||
use PDepend\Source\AST\ASTClass;
|
||||
use PHPMD\AbstractNode;
|
||||
use PHPMD\AbstractRule;
|
||||
use PHPMD\Node\ClassNode;
|
||||
use PHPMD\Rule\ClassAware;
|
||||
use Magento\Framework\App\ActionInterface;
|
||||
|
||||
/**
|
||||
* Actions must process a defined list of HTTP methods.
|
||||
*/
|
||||
class AllPurposeAction extends AbstractRule implements ClassAware
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param ClassNode|ASTClass $node
|
||||
*/
|
||||
public function apply(AbstractNode $node)
|
||||
{
|
||||
// Skip validation for Abstract Controllers
|
||||
if ($node->isAbstract()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!class_exists($node->getFullQualifiedName(), true)) {
|
||||
return;
|
||||
}
|
||||
$impl = class_implements($node->getFullQualifiedName(), true);
|
||||
} catch (\Throwable $exception) {
|
||||
//Couldn't load a class.
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($impl) && in_array(ActionInterface::class, $impl, true)) {
|
||||
$methodsDefined = false;
|
||||
foreach ($impl as $i) {
|
||||
if (preg_match('/\\\Http[a-z]+ActionInterface$/i', $i)) {
|
||||
$methodsDefined = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$methodsDefined) {
|
||||
$this->addViolation($node, [$node->getFullQualifiedName()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\CodeMessDetector\Rule\Design;
|
||||
|
||||
use Magento\Framework\GetParameterClassTrait;
|
||||
use Magento\Framework\Session\SessionManagerInterface;
|
||||
use Magento\Framework\Stdlib\Cookie\CookieReaderInterface;
|
||||
use PDepend\Source\AST\ASTClass;
|
||||
use PHPMD\AbstractNode;
|
||||
use PHPMD\AbstractRule;
|
||||
use PHPMD\Node\ClassNode;
|
||||
use PHPMD\Rule\ClassAware;
|
||||
|
||||
/**
|
||||
* Session and Cookies must be used only in HTML Presentation layer.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class CookieAndSessionMisuse extends AbstractRule implements ClassAware
|
||||
{
|
||||
use GetParameterClassTrait;
|
||||
|
||||
/**
|
||||
* Is given class a controller?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isController(\ReflectionClass $class): bool
|
||||
{
|
||||
return $class->isSubclassOf(\Magento\Framework\App\ActionInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given class a block?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isBlock(\ReflectionClass $class): bool
|
||||
{
|
||||
return $class->isSubclassOf(\Magento\Framework\View\Element\BlockInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given class an HTML UI data provider?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isUiDataProvider(\ReflectionClass $class): bool
|
||||
{
|
||||
return $class->isSubclassOf(
|
||||
\Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface::class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given class a Layout Processor?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isLayoutProcessor(\ReflectionClass $class): bool
|
||||
{
|
||||
return $class->isSubclassOf(
|
||||
\Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given class a View Model?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isViewModel(\ReflectionClass $class): bool
|
||||
{
|
||||
return $class->isSubclassOf(
|
||||
\Magento\Framework\View\Element\Block\ArgumentInterface::class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given class an HTML UI Document?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isUiDocument(\ReflectionClass $class): bool
|
||||
{
|
||||
return $class->isSubclassOf(\Magento\Framework\View\Element\UiComponent\DataProvider\Document::class)
|
||||
|| $class->getName() === \Magento\Framework\View\Element\UiComponent\DataProvider\Document::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given class a plugin for controllers?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isControllerPlugin(\ReflectionClass $class): bool
|
||||
{
|
||||
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
if (preg_match('/^(after|around|before).+/i', $method->getName())) {
|
||||
try {
|
||||
$parameters = $method->getParameters();
|
||||
if (count($parameters) === 0) {
|
||||
continue;
|
||||
}
|
||||
$argument = $this->getParameterClass($parameters[0]);
|
||||
} catch (\Throwable $exception) {
|
||||
//Non-existing class (autogenerated perhaps) or doesn't have an argument.
|
||||
continue;
|
||||
}
|
||||
if ($argument) {
|
||||
$isAction = $argument->isSubclassOf(\Magento\Framework\App\ActionInterface::class)
|
||||
|| $argument->getName() === \Magento\Framework\App\ActionInterface::class;
|
||||
if ($isAction) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given class a plugin for blocks?
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isBlockPlugin(\ReflectionClass $class): bool
|
||||
{
|
||||
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
if (preg_match('/^(after|around|before).+/i', $method->getName())) {
|
||||
try {
|
||||
$parameters = $method->getParameters();
|
||||
if (count($parameters) === 0) {
|
||||
continue;
|
||||
}
|
||||
$argument = $this->getParameterClass($parameters[0]);
|
||||
} catch (\Throwable $exception) {
|
||||
//Non-existing class (autogenerated perhaps) or doesn't have an argument.
|
||||
continue;
|
||||
}
|
||||
if ($argument) {
|
||||
$isBlock = $argument->isSubclassOf(\Magento\Framework\View\Element\BlockInterface::class)
|
||||
|| $argument->getName() === \Magento\Framework\View\Element\BlockInterface::class;
|
||||
if ($isBlock) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether given class depends on classes to pay attention to.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @return bool
|
||||
*/
|
||||
private function doesUseRestrictedClasses(\ReflectionClass $class): bool
|
||||
{
|
||||
$constructor = $class->getConstructor();
|
||||
if ($constructor) {
|
||||
foreach ($constructor->getParameters() as $argument) {
|
||||
try {
|
||||
$class = $this->getParameterClass($argument);
|
||||
if ($class === null) {
|
||||
continue;
|
||||
}
|
||||
if ($class->isSubclassOf(SessionManagerInterface::class)
|
||||
|| $class->getName() === SessionManagerInterface::class
|
||||
|| $class->isSubclassOf(CookieReaderInterface::class)
|
||||
|| $class->getName() === CookieReaderInterface::class
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
} catch (\ReflectionException $exception) {
|
||||
//Failed to load the argument's class information
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param ClassNode|ASTClass $node
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*/
|
||||
public function apply(AbstractNode $node)
|
||||
{
|
||||
try {
|
||||
$class = new \ReflectionClass($node->getFullQualifiedName());
|
||||
} catch (\Throwable $exception) {
|
||||
//Failed to load class, nothing we can do
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->doesUseRestrictedClasses($class)) {
|
||||
if (!$this->isController($class)
|
||||
&& !$this->isBlock($class)
|
||||
&& !$this->isUiDataProvider($class)
|
||||
&& !$this->isUiDocument($class)
|
||||
&& !$this->isControllerPlugin($class)
|
||||
&& !$this->isBlockPlugin($class)
|
||||
&& !$this->isLayoutProcessor($class)
|
||||
&& !$this->isViewModel($class)
|
||||
) {
|
||||
$this->addViolation($node, [$node->getFullQualifiedName()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\CodeMessDetector\Rule\UnusedCode;
|
||||
|
||||
use PHPMD\AbstractNode;
|
||||
use PHPMD\Node\ClassNode;
|
||||
use PHPMD\Node\MethodNode;
|
||||
use PHPMD\Rule\UnusedFormalParameter as PhpmdUnusedFormalParameter;
|
||||
|
||||
class UnusedFormalParameter extends PhpmdUnusedFormalParameter
|
||||
{
|
||||
/**
|
||||
* This method collects all local variables in the body of the currently
|
||||
* analyzed method or function and removes those parameters that are
|
||||
* referenced by one of the collected variables.
|
||||
*
|
||||
* @param AbstractNode $node
|
||||
* @return void
|
||||
*/
|
||||
protected function removeUsedParameters(AbstractNode $node)
|
||||
{
|
||||
parent::removeUsedParameters($node);
|
||||
$this->removeVariablesUsedInPlugins($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove required method variables used in plugins from given node
|
||||
*
|
||||
* @param AbstractNode $node
|
||||
*/
|
||||
private function removeVariablesUsedInPlugins(AbstractNode $node)
|
||||
{
|
||||
if (!$node instanceof MethodNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var ClassNode $classNode */
|
||||
$classNode = $node->getParentType();
|
||||
if (!$this->isPluginClass($classNode->getNamespaceName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Around and After plugins has 2 required params $subject and $proceed or $result
|
||||
* that should be ignored
|
||||
*/
|
||||
foreach (['around', 'after'] as $pluginMethodPrefix) {
|
||||
if ($this->isFunctionNameStartingWith($node, $pluginMethodPrefix)) {
|
||||
$this->removeVariablesByCount(2);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before plugins has 1 required params $subject
|
||||
* that should be ignored
|
||||
*/
|
||||
if ($this->isFunctionNameStartingWith($node, 'before')) {
|
||||
$this->removeVariablesByCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the first part of function fully qualified name is equal to $name
|
||||
*
|
||||
* Methods getImage and getName are equal. getImage used prior to usage in phpmd source
|
||||
*
|
||||
* @param MethodNode $node
|
||||
* @param string $name
|
||||
* @return boolean
|
||||
*/
|
||||
private function isFunctionNameStartingWith(MethodNode $node, string $name): bool
|
||||
{
|
||||
return (0 === strpos($node->getImage(), $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove first $countOfRemovingVariables from given node
|
||||
*
|
||||
* @param int $countOfRemovingVariables
|
||||
*/
|
||||
private function removeVariablesByCount(int $countOfRemovingVariables)
|
||||
{
|
||||
array_splice($this->nodes, 0, $countOfRemovingVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if namespace contain "Plugin". Case-sensitive ignored
|
||||
*
|
||||
* @param string $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isPluginClass(string $class): bool
|
||||
{
|
||||
return (stripos($class, 'plugin') !== false);
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\CodeMessDetector\Test\Unit\Rule\Design;
|
||||
|
||||
use Magento\CodeMessDetector\Rule\Design\AllPurposeAction;
|
||||
use Magento\Framework\App\Action\HttpGetActionInterface;
|
||||
use Magento\Framework\App\ActionInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject as MockObject;
|
||||
use PHPUnit\Framework\MockObject\Builder\InvocationMocker;
|
||||
use PHPMD\Report;
|
||||
use PHPMD\Node\ClassNode;
|
||||
|
||||
class AllPurposeActionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param object $fakeAction
|
||||
* @param bool $violates
|
||||
*
|
||||
* @dataProvider getCases
|
||||
*/
|
||||
public function testApply($fakeAction, bool $violates)
|
||||
{
|
||||
$node = $this->createNodeMock($fakeAction);
|
||||
$rule = new AllPurposeAction();
|
||||
$this->expectsRuleViolation($rule, $violates);
|
||||
$rule->apply($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCases(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
new class implements ActionInterface, HttpGetActionInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
},
|
||||
false
|
||||
],
|
||||
[
|
||||
new class implements ActionInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
},
|
||||
true
|
||||
],
|
||||
[
|
||||
new class implements HttpGetActionInterface
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
},
|
||||
false
|
||||
],
|
||||
[
|
||||
new class
|
||||
{
|
||||
|
||||
},
|
||||
false
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $dynamicObject
|
||||
*
|
||||
* @return ClassNode|MockObject
|
||||
*/
|
||||
private function createNodeMock($dynamicObject): MockObject
|
||||
{
|
||||
$node = $this->getMockBuilder(ClassNode::class)
|
||||
->disableOriginalConstructor()
|
||||
->disableProxyingToOriginalMethods()
|
||||
->setMethods(
|
||||
[
|
||||
// disable name lookup from AST artifact
|
||||
'getNamespaceName',
|
||||
'getParentName',
|
||||
'getName',
|
||||
'getFullQualifiedName',
|
||||
]
|
||||
)
|
||||
->getMock();
|
||||
$node->expects($this->any())
|
||||
->method('getFullQualifiedName')
|
||||
->willReturn(get_class($dynamicObject));
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AllPurposeAction $rule
|
||||
* @param bool $expects
|
||||
* @return InvocationMocker
|
||||
*/
|
||||
private function expectsRuleViolation(AllPurposeAction $rule, bool $expects): InvocationMocker
|
||||
{
|
||||
/** @var Report|MockObject $report */
|
||||
$report = $this->getMockBuilder(Report::class)->getMock();
|
||||
if ($expects) {
|
||||
$violationExpectation = $this->atLeastOnce();
|
||||
} else {
|
||||
$violationExpectation = $this->never();
|
||||
}
|
||||
$invokation = $report->expects($violationExpectation)
|
||||
->method('addRuleViolation');
|
||||
$rule->setReport($report);
|
||||
|
||||
return $invokation;
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Magento\CodeMessDetector\Test\Unit\Rule\UnusedCode;
|
||||
|
||||
use Magento\CodeMessDetector\Rule\UnusedCode\UnusedFormalParameter;
|
||||
use PHPMD\Node\ASTNode;
|
||||
use PHPMD\Node\MethodNode;
|
||||
use PHPMD\Report;
|
||||
use PHPUnit\Framework\MockObject\MockObject as MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
class UnusedFormalParameterTest extends TestCase
|
||||
{
|
||||
private const FAKE_PLUGIN_NAMESPACE = 'Magento\CodeMessDetector\Test\UnusedCode\Plugin';
|
||||
private const FAKE_NAMESPACE = 'Magento\CodeMessDetector\Test\UnusedCode';
|
||||
|
||||
/**
|
||||
*
|
||||
* @dataProvider getCases
|
||||
*/
|
||||
public function testApply($methodName, $methodParams, $namespace, $expectViolation)
|
||||
{
|
||||
$node = $this->createMethodNodeMock($methodName, $methodParams, $namespace);
|
||||
$rule = new UnusedFormalParameter();
|
||||
$this->expectsRuleViolation($rule, $expectViolation);
|
||||
$rule->apply($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare method node mock
|
||||
*
|
||||
* @param $methodName
|
||||
* @param $methodParams
|
||||
* @param $namespace
|
||||
* @return MethodNode|MockObject
|
||||
*/
|
||||
private function createMethodNodeMock($methodName, $methodParams, $namespace)
|
||||
{
|
||||
$methodNode = $this->createConfiguredMock(
|
||||
MethodNode::class,
|
||||
[
|
||||
'getName' => $methodName,
|
||||
'getImage' => $methodName,
|
||||
'isAbstract' => false,
|
||||
'isDeclaration' => true
|
||||
]
|
||||
);
|
||||
|
||||
$variableDeclarators = [];
|
||||
foreach ($methodParams as $methodParam) {
|
||||
$variableDeclarator = $this->createASTNodeMock();
|
||||
$variableDeclarator->method('getImage')
|
||||
->willReturn($methodParam);
|
||||
|
||||
$variableDeclarators[] = $variableDeclarator;
|
||||
}
|
||||
$parametersMock = $this->createASTNodeMock();
|
||||
$parametersMock->expects($this->once())
|
||||
->method('findChildrenOfType')
|
||||
->with('VariableDeclarator')
|
||||
->willReturn($variableDeclarators);
|
||||
|
||||
/**
|
||||
* Declare mock result for findChildrenOfType
|
||||
* with Dummy for removeCompoundVariables and removeVariablesUsedByFuncGetArgs
|
||||
*/
|
||||
$methodNode->expects($this->atLeastOnce())
|
||||
->method('findChildrenOfType')
|
||||
->withConsecutive(['FormalParameters'], ['CompoundVariable'], ['FunctionPostfix'])
|
||||
->willReturnOnConsecutiveCalls([$parametersMock], [], []);
|
||||
|
||||
// Dummy result for removeRegularVariables
|
||||
$methodNode->expects($this->once())
|
||||
->method('findChildrenOfTypeVariable')
|
||||
->willReturn([]);
|
||||
|
||||
$classNode = $this->createASTNodeMock();
|
||||
$classNode->expects($this->once())
|
||||
->method('getNamespaceName')
|
||||
->willReturn($namespace);
|
||||
$methodNode->expects($this->once())
|
||||
->method('getParentType')
|
||||
->willReturn($classNode);
|
||||
|
||||
return $methodNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ASTNode mock
|
||||
*
|
||||
* @return ASTNode|MockObject
|
||||
*/
|
||||
private function createASTNodeMock()
|
||||
{
|
||||
return $this->createMock(ASTNode::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnusedFormalParameter $rule
|
||||
* @param bool $expects
|
||||
*/
|
||||
private function expectsRuleViolation(UnusedFormalParameter $rule, bool $expects)
|
||||
{
|
||||
/** @var Report|MockObject $reportMock */
|
||||
$reportMock = $this->createMock(Report::class);
|
||||
if ($expects) {
|
||||
$violationExpectation = $this->atLeastOnce();
|
||||
} else {
|
||||
$violationExpectation = $this->never();
|
||||
}
|
||||
$reportMock->expects($violationExpectation)
|
||||
->method('addRuleViolation');
|
||||
$rule->setReport($reportMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getCases(): array
|
||||
{
|
||||
return [
|
||||
// Plugin methods
|
||||
[
|
||||
'beforePluginMethod',
|
||||
[
|
||||
'subject'
|
||||
],
|
||||
self::FAKE_PLUGIN_NAMESPACE,
|
||||
false
|
||||
],
|
||||
[
|
||||
'aroundPluginMethod',
|
||||
[
|
||||
'subject',
|
||||
'proceed'
|
||||
],
|
||||
self::FAKE_PLUGIN_NAMESPACE,
|
||||
false
|
||||
],
|
||||
[
|
||||
'aroundPluginMethod',
|
||||
[
|
||||
'subject',
|
||||
'result'
|
||||
],
|
||||
self::FAKE_PLUGIN_NAMESPACE,
|
||||
false
|
||||
],
|
||||
// Plugin method that contain unused parameter
|
||||
[
|
||||
'someMethod',
|
||||
[
|
||||
'unusedParameter'
|
||||
],
|
||||
self::FAKE_PLUGIN_NAMESPACE,
|
||||
true
|
||||
],
|
||||
// Non plugin method
|
||||
[
|
||||
'someMethod',
|
||||
[
|
||||
'subject',
|
||||
'result'
|
||||
],
|
||||
self::FAKE_NAMESPACE,
|
||||
true
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
-->
|
||||
<ruleset name="Magento Specific Design Rules"
|
||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
||||
<rule name="AllPurposeAction"
|
||||
class="Magento\CodeMessDetector\Rule\Design\AllPurposeAction"
|
||||
message= "The class {0} does not restrict processed HTTP methods by implementing a Http*Method name*ActionInterface">
|
||||
<description>
|
||||
<![CDATA[
|
||||
Controllers (classes implementing ActionInterface) have to implement marker Http<Method>ActionInterface
|
||||
to restrict incoming requests by methods.
|
||||
]]>
|
||||
</description>
|
||||
<priority>2</priority>
|
||||
<properties />
|
||||
<example>
|
||||
<![CDATA[
|
||||
class PostOrder implements ActionInterface
|
||||
{
|
||||
public function execute()
|
||||
{
|
||||
//I process GET, POST, PATCH etc. while only intended for POST
|
||||
...
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
<rule name="CookieAndSessionMisuse"
|
||||
class="Magento\CodeMessDetector\Rule\Design\CookieAndSessionMisuse"
|
||||
message= "The class {0} uses sessions or cookies while not being a part of HTML Presentation layer">
|
||||
<description>
|
||||
<![CDATA[
|
||||
Sessions and cookies must only be used in classes directly responsible for HTML presentation because Web APIs do not
|
||||
rely on cookies and sessions. If you need to get current user use Magento\Authorization\Model\UserContextInterface
|
||||
]]>
|
||||
</description>
|
||||
<priority>2</priority>
|
||||
<properties />
|
||||
<example>
|
||||
<![CDATA[
|
||||
class OrderProcessor
|
||||
{
|
||||
public function __construct(SessionManagerInterface $session) {
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function place(OrderInterface $order)
|
||||
{
|
||||
//Will not be present if processing a WebAPI request
|
||||
$currentOrder = $this->session->get('current_order');
|
||||
...
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
</ruleset>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
/**
|
||||
* Copyright © Magento, Inc. All rights reserved.
|
||||
* See COPYING.txt for license details.
|
||||
*/
|
||||
-->
|
||||
<ruleset name="Unused Code Rules"
|
||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
||||
|
||||
<rule name="UnusedFormalParameter"
|
||||
message="Avoid unused parameters such as '{0}'."
|
||||
class="Magento\CodeMessDetector\Rule\UnusedCode\UnusedFormalParameter">
|
||||
<description><![CDATA[Avoid passing parameters to methods or constructors and then not using those parameters except on plugins]]></description>
|
||||
<example>
|
||||
<![CDATA[
|
||||
class Foo
|
||||
{
|
||||
private function bar($howdy)
|
||||
{
|
||||
// $howdy is not used
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
||||
Reference in New Issue
Block a user