Skip to content
This repository was archived by the owner on Dec 19, 2019. It is now read-only.

Commit 9dfc765

Browse files
authored
Merge pull request #5109 from magento-mpi/MC-23067
[MPI] Introduce PHPStan code analysis tool to the static build
2 parents fffa041 + 649a973 commit 9dfc765

File tree

18 files changed

+760
-62
lines changed

18 files changed

+760
-62
lines changed

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
"pdepend/pdepend": "2.5.2",
9393
"phpcompatibility/php-compatibility": "^9.3",
9494
"phpmd/phpmd": "@stable",
95+
"phpstan/phpstan": "^0.12.2",
9596
"phpunit/phpunit": "~6.5.0",
9697
"sebastian/phpcpd": "~3.0.0",
9798
"squizlabs/php_codesniffer": "~3.4.0"
@@ -341,7 +342,8 @@
341342
"Magento\\Tools\\": "dev/tools/Magento/Tools/",
342343
"Magento\\Tools\\Sanity\\": "dev/build/publication/sanity/Magento/Tools/Sanity/",
343344
"Magento\\TestFramework\\Inspection\\": "dev/tests/static/framework/Magento/TestFramework/Inspection/",
344-
"Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/"
345+
"Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/",
346+
"Magento\\PhpStan\\": "dev/tests/static/framework/Magento/PhpStan/"
345347
}
346348
},
347349
"prefer-stable": true

composer.lock

Lines changed: 99 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dev/tests/integration/testsuite/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/GeneralTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class GeneralTest extends \PHPUnit\Framework\TestCase
1616
/** @var \Magento\Framework\View\Design\ThemeInterface */
1717
protected $_theme;
1818

19-
/** @var \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab_General */
19+
/** @var \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab\General */
2020
protected $_block;
2121

2222
protected function setUp()
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\PhpStan\Formatters;
9+
10+
use PHPStan\Command\AnalysisResult;
11+
use PHPStan\Command\ErrorFormatter\TableErrorFormatter;
12+
use PHPStan\Command\Output;
13+
14+
/**
15+
* To mute the PHPStan error message add a comment above the reported error line.
16+
*
17+
* Example of usage:
18+
*
19+
* // phpstan:ignore "Method Magento\TestModule\TestClass::testMethod() invoked with 1 parameter, 0 required."
20+
* $this->testMethod(1);
21+
*
22+
* or replace some part of error message with *
23+
*
24+
* // phpstan:ignore "Method * invoked with 1 parameter, 0 required."
25+
* $this->testMethod(1);
26+
*
27+
* or just
28+
*
29+
* // phpstan:ignore
30+
* $this->testMethod(1);
31+
*
32+
* or
33+
*
34+
* $this->testMethod(1); // phpstan:ignore
35+
*
36+
* The error message will be suppressed.
37+
*
38+
* @see \Magento\PhpStan\Formatters\Fixtures\ClassWithIgnoreAnnotation
39+
*/
40+
class FilteredErrorFormatter extends TableErrorFormatter
41+
{
42+
private const MUTE_ERROR_ANNOTATION = 'phpstan:ignore';
43+
44+
private const NO_ERRORS = 0;
45+
46+
/**
47+
* @inheritdoc
48+
*/
49+
public function formatErrors(AnalysisResult $analysisResult, Output $output): int
50+
{
51+
if (!$analysisResult->hasErrors()) {
52+
$style = $output->getStyle();
53+
$style->success('No errors');
54+
return self::NO_ERRORS;
55+
}
56+
57+
$fileSpecificErrorsWithoutIgnoredErrors = $this->clearIgnoredErrors(
58+
$analysisResult->getFileSpecificErrors()
59+
);
60+
61+
$clearedAnalysisResult = new AnalysisResult(
62+
$fileSpecificErrorsWithoutIgnoredErrors,
63+
$analysisResult->getNotFileSpecificErrors(),
64+
$analysisResult->isDefaultLevelUsed(),
65+
$analysisResult->hasInferrablePropertyTypesFromConstructor(),
66+
$analysisResult->getProjectConfigFile()
67+
);
68+
69+
return parent::formatErrors($clearedAnalysisResult, $output);
70+
}
71+
72+
/**
73+
* Filters error list.
74+
*
75+
* @param array $fileSpecificErrors
76+
* @return array
77+
*/
78+
private function clearIgnoredErrors(array $fileSpecificErrors): array
79+
{
80+
foreach ($fileSpecificErrors as $index => $error) {
81+
$fileName = $error->getFile();
82+
// phpcs:ignore Magento2.Functions.DiscouragedFunction
83+
if (!file_exists($fileName)) {
84+
continue;
85+
}
86+
87+
$line = $error->getLine() ? $this->getLineWithMuteErrorAnnotation($error->getLine(), $fileName) : null;
88+
if ($line === null) {
89+
continue;
90+
}
91+
92+
$extractErrorPattern = '@' . self::MUTE_ERROR_ANNOTATION . '\s+"(.*?)"@';
93+
$errorPattern = preg_match($extractErrorPattern, $line, $result) ? $this->preparePattern($result[1]) : '';
94+
if ($errorPattern && !preg_match('@' . $errorPattern . '@i', $error->getMessage())) {
95+
continue;
96+
}
97+
98+
unset($fileSpecificErrors[$index]);
99+
}
100+
101+
return $fileSpecificErrors;
102+
}
103+
104+
/**
105+
* Returns context of the line with mute error annotation.
106+
*
107+
* @param int $errorLine
108+
* @param string $fileName
109+
* @return string|null
110+
*/
111+
private function getLineWithMuteErrorAnnotation(int $errorLine, string $fileName): ?string
112+
{
113+
$file = new \SplFileObject($fileName);
114+
$lineNumbersToCheck = [
115+
$errorLine - 2, // the line above to the line that caused the error
116+
$errorLine - 1, // the line that caused the error
117+
$errorLine - 3, // the line two lines above to the line that caused the error
118+
];
119+
120+
foreach ($lineNumbersToCheck as $lineNumber) {
121+
$file->seek($lineNumber > 0 ? $lineNumber : 0);
122+
$line = $file->current();
123+
if (strpos($line, self::MUTE_ERROR_ANNOTATION) !== false) {
124+
return $line;
125+
}
126+
}
127+
128+
return null;
129+
}
130+
131+
/**
132+
* Prepares error pattern.
133+
*
134+
* @param string $errorDescription
135+
* @return string
136+
*/
137+
private function preparePattern(string $errorDescription)
138+
{
139+
// phpcs:ignore Magento2.Functions.DiscouragedFunction
140+
return str_replace('*', '(?:.*?)', addcslashes(trim($errorDescription), '\()[]'));
141+
}
142+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
// phpcs:disable
8+
9+
use Magento\Framework\App\Filesystem\DirectoryList;
10+
use Magento\Framework\Code\Generator\Io;
11+
use Magento\Framework\Filesystem\Driver\File;
12+
use Magento\Framework\TestFramework\Unit\Autoloader\ExtensionAttributesGenerator;
13+
use Magento\Framework\TestFramework\Unit\Autoloader\ExtensionAttributesInterfaceGenerator;
14+
use Magento\Framework\TestFramework\Unit\Autoloader\FactoryGenerator;
15+
use Magento\Framework\TestFramework\Unit\Autoloader\GeneratedClassesAutoloader;
16+
17+
if (!defined('TESTS_TEMP_DIR')) {
18+
//phpcs:ignore Magento2.Functions.DiscouragedFunction
19+
define('TESTS_TEMP_DIR', dirname(__DIR__) . '/../../tmp');
20+
}
21+
22+
$generatorIo = new Io(
23+
new File(),
24+
TESTS_TEMP_DIR . '/' . DirectoryList::getDefaultConfig()[DirectoryList::GENERATED_CODE][DirectoryList::PATH]
25+
);
26+
$generatedCodeAutoloader = new GeneratedClassesAutoloader(
27+
[
28+
new ExtensionAttributesGenerator(),
29+
new ExtensionAttributesInterfaceGenerator(),
30+
new FactoryGenerator(),
31+
],
32+
$generatorIo
33+
);
34+
spl_autoload_register([$generatedCodeAutoloader, 'load']);

0 commit comments

Comments
 (0)