Skip to content

Commit 2dfe7a0

Browse files
committed
[FEATURE] First working prototype
* CLI interface * Basic syntax check * Component structure check (with strict option) * Parameter naming check (patterns, length) * Parameter count (min/max) * Used variables check
1 parent 9e003cc commit 2dfe7a0

30 files changed

+1303
-146
lines changed

.ecrc

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"Verbose": false,
3+
"Debug": false,
4+
"IgnoreDefaults": false,
5+
"SpacesAftertabs": false,
6+
"NoColor": false,
7+
"Exclude": [
8+
"vendor",
9+
".git",
10+
".DS_Store",
11+
"node_modules"
12+
],
13+
"AllowedContentTypes": [],
14+
"PassedFiles": [],
15+
"Disable": {
16+
"EndOfLine": false,
17+
"Indentation": false,
18+
"InsertFinalNewline": false,
19+
"TrimTrailingWhitespace": false
20+
}
21+
}

.editorconfig

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 4
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true
10+
11+
[*.md]
12+
trim_trailing_whitespace = false
13+
indent_style = unset
14+
15+
[LICENSE]
16+
indent_style = unset

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor/
2+
/composer.lock

composer.json

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "sitegeist/fluid-components-linter",
3+
"description": "Tool to validate fluid components based on a specified ruleset",
4+
"homepage": "https://github.com/sitegeist/fluid-components-linter",
5+
"license": "GPL-2.0-or-later",
6+
"authors": [
7+
{
8+
"name": "Simon Praetorius",
9+
"email": "[email protected]"
10+
}
11+
],
12+
"support": {
13+
"issues": "https://github.com/sitegeist/fluid-components-linter/issues"
14+
},
15+
"require": {
16+
"php": ">=7.2.0 <8",
17+
"symfony/config": "^4.4 || ^5.0",
18+
"symfony/console": "^4.4 || ^5.0",
19+
"typo3fluid/fluid": "^2.6"
20+
},
21+
"require-dev": {
22+
"editorconfig-checker/editorconfig-checker": "^10.0",
23+
"squizlabs/php_codesniffer": "^3.0",
24+
"phpunit/phpunit": "^9.2",
25+
"symfony/var-dumper": "^5.1"
26+
},
27+
"autoload": {
28+
"psr-4": {
29+
"Sitegeist\\FluidComponentsLinter\\": "src/Classes/"
30+
}
31+
},
32+
"bin": [
33+
"bin/fclint"
34+
],
35+
"scripts": {
36+
"lint": [
37+
"@lint:php",
38+
"@lint:editorconfig"
39+
],
40+
"lint:php": "phpcs --standard=PSR2 --extensions=php --exclude=Generic.Files.LineLength --ignore='vendor' .",
41+
"lint:editorconfig": "ec .",
42+
"test": "phpunit tests/"
43+
}
44+
}

fclint

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
require __DIR__.'/src/FcLint.php';

fclint.json

-146
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Sitegeist\FluidComponentsLinter\CodeQuality\Check;
5+
6+
use Sitegeist\FluidComponentsLinter\CodeQuality\Component;
7+
use Sitegeist\FluidComponentsLinter\Service\FluidService;
8+
9+
abstract class AbstractCheck implements CheckInterface
10+
{
11+
protected $component;
12+
protected $configuration;
13+
protected $fluidService;
14+
15+
public function __construct(Component $component, array $configuration)
16+
{
17+
$this->component = $component;
18+
$this->configuration = $configuration;
19+
20+
$this->fluidService = new FluidService;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Sitegeist\FluidComponentsLinter\CodeQuality\Check;
5+
6+
use Sitegeist\FluidComponentsLinter\CodeQuality\Component;
7+
8+
interface CheckInterface
9+
{
10+
public function __construct(Component $component, array $configuration);
11+
public function check(): array;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Sitegeist\FluidComponentsLinter\CodeQuality\Check;
5+
6+
use Sitegeist\FluidComponentsLinter\CodeQuality\Component;
7+
use Sitegeist\FluidComponentsLinter\Exception\CodeQualityException;
8+
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ObjectAccessorNode;
9+
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
10+
11+
class ComponentVariablesCheck extends AbstractCheck
12+
{
13+
protected $predefinedVariables = [
14+
'settings(\.|$)',
15+
'class$',
16+
'content$',
17+
'component\.(class|prefix|namespace)$'
18+
];
19+
20+
public function check(): array
21+
{
22+
$allowedVariables = array_merge($this->predefinedVariables, $this->generateParamNamePatterns());
23+
$allowedVariablesPattern = '#^(' . implode('|', $allowedVariables) . ')#';
24+
25+
$usedVariables = $this->fluidService->extractNodeType(
26+
$this->component->rootNode,
27+
ObjectAccessorNode::class
28+
);
29+
$usedVariableNames = array_map(function (ObjectAccessorNode $node) {
30+
return $node->getObjectPath();
31+
}, $usedVariables);
32+
33+
$invalidVariables = array_filter(
34+
$usedVariableNames,
35+
function (string $usedVariable) use ($allowedVariablesPattern) {
36+
return !preg_match($allowedVariablesPattern, $usedVariable);
37+
}
38+
);
39+
40+
if (!empty($invalidVariables)) {
41+
throw new CodeQualityException(sprintf(
42+
'The following variables are used in the component, but were never defined: %s',
43+
'{' . implode('}, {', $invalidVariables) . '}'
44+
), 1595870402);
45+
}
46+
47+
return [];
48+
}
49+
50+
protected function generateParamNamePatterns(): array
51+
{
52+
return array_map(function (ViewHelperNode $node) {
53+
return preg_quote((string) $node->getArguments()['name'], '#') . '(\.|$)';
54+
}, $this->component->paramNodes);
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Sitegeist\FluidComponentsLinter\CodeQuality\Check;
5+
6+
use Sitegeist\FluidComponentsLinter\Exception\CodeQualityException;
7+
8+
class ParamCountCheck extends AbstractCheck
9+
{
10+
public function check(): array
11+
{
12+
$count = $this->configuration['params']['count'];
13+
14+
if (count($this->component->paramNodes) > $count['max']) {
15+
throw new CodeQualityException(sprintf(
16+
'The component has %d parameters, but only %d are allowed.',
17+
count($this->component->paramNodes),
18+
$count['max']
19+
));
20+
}
21+
22+
if (count($this->component->paramNodes) < $count['min']) {
23+
throw new CodeQualityException(sprintf(
24+
'The component has %d parameters, but at least %d are required.',
25+
count($this->component->paramNodes),
26+
$count['min']
27+
));
28+
}
29+
30+
return [];
31+
}
32+
}

0 commit comments

Comments
 (0)