8000 Reorganize: parse first, validate later (#7) · symfony-tools/code-block-checker@2b803ed · GitHub
[go: up one dir, main page]

Skip to content

Commit 2b803ed

Browse files
authored
Reorganize: parse first, validate later (#7)
* Reorganize: parse first, validate later * Fxied the tests
1 parent c52e9c0 commit 2b803ed

File tree

9 files changed

+164
-111
lines changed

9 files changed

+164
-111
lines changed

psalm.baseline.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<code>\CachedContainer</code>
66
</UndefinedClass>
77
</file>
8-
<file src="src/Listener/ValidCodeNodeListener.php">
8+
<file src="src/Service/CodeValidator.php">
99
<InvalidArgument occurrences="1"/>
1010
</file>
1111
</files>

src/Command/CheckDocsCommand.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
use Doctrine\RST\Builder\Documents;
88
use Doctrine\RST\Builder\ParseQueue;
99
use Doctrine\RST\Builder\ParseQueueProcessor;
10+
use Doctrine\RST\ErrorManager;
1011
use Doctrine\RST\Event\PostNodeCreateEvent;
1112
use Doctrine\RST\Meta\Metas;
12-
use Symfony\CodeBlockChecker\Issue\IssueManger;
13-
use Symfony\CodeBlockChecker\Listener\ValidCodeNodeListener;
13+
use Symfony\CodeBlockChecker\Issue\IssueCollection;
14+
use Symfony\CodeBlockChecker\Listener\CodeNodeCollector;
15+
use Symfony\CodeBlockChecker\Service\CodeValidator;
1416
use Symfony\Component\Console\Command\Command;
1517
use Symfony\Component\Console\Input\InputArgument;
1618
use Symfony\Component\Console\Input\InputInterface;
@@ -25,12 +27,15 @@ class CheckDocsCommand extends Command
2527
protected static $defaultName = 'verify:docs';
2628

2729
private SymfonyStyle $io;
28-
private IssueManger $errorManager;
30+
private IssueCollection $errorManager;
2931
private ParseQueueProcessor $queueProcessor;
32+
private CodeNodeCollector $collector;
33+
private CodeValidator $validator;
3034

31-
public function __construct()
35+
public function __construct(CodeValidator $validator)
3236
{
3337
parent::__construct(self::$defaultName);
38+
$this->validator = $validator;
3439
}
3540

3641
protected function configure()
@@ -58,14 +63,13 @@ protected function initialize(InputInterface $input, OutputInterface $output)
5863
$kernel = \SymfonyDocsBuilder\KernelFactory::createKernel($buildConfig);
5964
$configuration = $kernel->getConfiguration();
6065
$configuration->silentOnError(true);
61-
$this->errorManager = new IssueManger($configuration);
6266
$eventManager = $configuration->getEventManager();
63-
$eventManager->addEventListener(PostNodeCreateEvent::POST_NODE_CREATE, new ValidCodeNodeListener($this->errorManager));
67+
$eventManager->addEventListener(PostNodeCreateEvent::POST_NODE_CREATE, $this->collector = new CodeNodeCollector());
6468

6569
$metas = new Metas();
6670
$documents = new Documents(new Filesystem(), $metas);
6771

68-
$this->queueProcessor = new ParseQueueProcessor($kernel, $this->errorManager, $metas, $documents, $sourceDir, '/foo/target', 'rst');
72+
$this->queueProcessor = new ParseQueueProcessor($kernel, new ErrorManager($configuration), $metas, $documents, $sourceDir, '/foo/target', 'rst');
6973
}
7074

7175
protected function execute(InputInterface $input, OutputInterface $output): int
@@ -80,9 +84,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8084
$parseQueue->addFile(ltrim($filename, '/'), true);
8185
}
8286

87+
// This will collect all CodeNodes
8388
$this->queueProcessor->process($parseQueue);
89+
$issues = $this->validator->validateNodes($this->collector->getNodes());
8490

85-
$issues = $this->errorManager->getIssues();
8691
$issueCount = count($issues);
8792
if ($issueCount > 0) {
8893
$format = $input->getOption('output-format');

src/Issue/IssueCollection.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Symfony\CodeBlockChecker\Issue;
4+
5+
class IssueCollection implements \Iterator, \Countable
6+
{
7+
/**
8+
* @var list<Issue>
9+
*/
10+
private array $issues = [];
11+
private int $key = 0;
12+
13+
public function addIssue(Issue $issue)
14+
{
15+
$this->issues[] = $issue;
16+
}
17+
18+
public function current()
19+
{
20+
return $this->issues[$this->key];
21+
}
22+
23+
public function next()
24+
{
25+
++$this->key;
26+
}
27+
28+
public function key()
29+
{
30+
return $this->key;
31+
}
32+
33+
public function valid()
34+
{
35+
return isset($this->issues[$this->key()]);
36+
}
37+
38+
public function rewind()
39+
{
40+
$this->key = 0;
41+
}
42+
43+
public function count()
44+
{
45+
return count($this->issues);
46+
}
47+
48+
/**
49+
* Get first issue or null.
50+
*/
51+
public function first(): ?Issue
52+
{
53+
return $this->issues[0] ?? null;
54+
}
55+
}

src/Issue/IssueManger.php

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/Listener/CodeNodeCollector.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symfony\CodeBlockChecker\Listener;
6+
7+
use Doctrine\RST\Event\PostNodeCreateEvent;
8+
use Doctrine\RST\Nodes\CodeNode;
9+
10+
/**
11+
* Collect all code nodes.
12+
*
13+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
14+
*/
15+
class CodeNodeCollector
16+
{
17+
private array $nodes;
18+
19+
public function getNodes(): array
20+
{
21+
return $this->nodes;
22+
}
23+
24+
public function postNodeCreate(PostNodeCreateEvent $event)
25+
{
26+
$node = $event->getNode();
27+
if (!$node instanceof CodeNode) {
28+
return;
29+
}
30+
$this->nodes[] = $node;
31+
}
32+
}

src/Listener/ValidCodeNodeListener.php renamed to src/Service/CodeValidator.php

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
declare(strict_types=1);
44

5-
namespace Symfony\CodeBlockChecker\Listener;
5+
namespace Symfony\CodeBlockChecker\Service;
66

7-
use Doctrine\RST\Event\PostNodeCreateEvent;
87
use Doctrine\RST\Nodes\CodeNode;
98
use Symfony\CodeBlockChecker\Issue\Issue;
10-
use Symfony\CodeBlockChecker\Issue\IssueManger;
9+
use Symfony\CodeBlockChecker\Issue\IssueCollection;
1110
use Symfony\CodeBlockChecker\Twig\DummyExtension;
1211
use Symfony\Component\Process\Process;
1312
use Symfony\Component\Yaml\Exception\ParseException;
@@ -22,23 +21,23 @@
2221
*
2322
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
2423
*/
25-
class ValidCodeNodeListener
24+
class CodeValidator
2625
{
27-
private $errorManager;
2826
private $twig;
27+
private IssueCollection $issues;
2928

30-
public function __construct(IssueManger $errorManager)
29+
public function validateNodes(array $nodes): IssueCollection
3130
{
32-
$this->errorManager = $errorManager;
31+
$this->issues = new IssueCollection();
32+
foreach ($nodes as $node) {
33+
$this->validateNode($node);
34+
}
35+
36+
return $this->issues;
3337
}
3438

35-
public function postNodeCreate(PostNodeCreateEvent $event)
39+
private function validateNode(CodeNode $node): void
3640
{
37-
$node = $event->getNode();
38-
if (!$node instanceof CodeNode) {
39-
return;
40-
}
41-
4241
$language = $node->getLanguage() ?? ($node->isRaw() ? null : 'php');
4342
if (in_array($language, ['php', 'php-symfony', 'php-standalone', 'php-annotations'])) {
4443
$this->validatePhp($node);
@@ -75,9 +74,9 @@ private function validatePhp(CodeNode $node)
7574
$text = str_replace($file, 'example.php', $process->getErrorOutput());
7675
if (preg_match('| in example.php on line ([0-9]+)|s', $text, $matches)) {
7776
$text = str_replace($matches[0], '', $text);
78-
$line = (int) $matches[1];
77+
$line = ((int) $matches[1]) - 1; // we added "<?php"
7978
}
80-
$this->errorManager->addIssue(new Issue($node, $text, 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), $line));
79+
$this->issues->addIssue(new Issue($node, $text, 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), $line));
8180
}
8281

8382
private function validateXml(CodeNode $node)
@@ -101,7 +100,7 @@ private function validateXml(CodeNode $node)
101100
return;
102101
}
103102

104-
$this->errorManager->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
103+
$this->issues->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
105104
}
106105
}
107106

@@ -116,7 +115,7 @@ private function validateYaml(CodeNode $node)
116115
return;
117116
}
118117

119-
$this->errorManager->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
118+
$this->issues->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
120119
}
121120
}
122121

@@ -132,7 +131,7 @@ private function validateTwig(CodeNode $node)
132131
// We cannot parse the TokenStream because we dont have all extensions loaded.
133132
$this->twig->parse($tokens);
134133
} catch (SyntaxError $e) {
135-
$this->errorManager->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
134+
$this->issues->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
136135
}
137136
}
138137

@@ -141,7 +140,7 @@ private function validateJson(CodeNode $node)
141140
try {
142141
$data = json_decode($node->getValue(), true, 512, JSON_THROW_ON_ERROR);
143142
} catch (\JsonException $e) {
144-
$this->errorManager->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
143+
$this->issues->addIssue(new Issue($node, $e->getMessage(), 'Invalid syntax', $node->getEnvironment()->getCurrentFileName(), 0));
145144
}
146145
}
147146
}

src/Service/LineDetector.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ public static function find(CodeNode $node): int
3232
}
3333
}
3434

35-
// The file's first row is 1 and our arrays first index is 0.
36-
return $i + 1;
35+
return $i;
3736
}
3837

3938
return 0;

tests/Listener/ValidCodeNodeListenerTest.php

Lines changed: 0 additions & 54 deletions
This file was deleted.

tests/Service/CodeValidatorTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Symfony\CodeBlockChecker\Tests\Service;
4+
5+
use Doctrine\RST\Configuration;
6+
use Doctrine\RST\Environment;
7+
use Doctrine\RST\ErrorManager;
8+
use Doctrine\RST\Event\PostNodeCreateEvent;
9+
use Doctrine\RST\Nodes\CodeNode;
10+
use PHPUnit\Framework\TestCase;
11+
use Symfony\CodeBlockChecker\Issue\IssueCollection;
12+
use Symfony\CodeBlockChecker\Listener\CodeNodeCollector;
13+
use Symfony\CodeBlockChecker\Service\CodeValidator;
14+
15+
class CodeValidatorTest extends TestCase
16+
{
17+
private CodeValidator $validator;
18+
private Environment $environment;
19+
20+
protected function setUp(): void
21+
{
22+
$this->environment = new Environment(new Configuration());
23+
$this->validator = new CodeValidator();
24+
}
25+
< A0B6 code>26+
public function testInvalidYaml()
27+
{
28+
$node = new CodeNode(['foobar: "test']);
29+
$node->setEnvironment($this->environment);
30+
$node->setLanguage('yaml');
31+
$errors = $this->validator->validateNodes([$node]);
32+
$this->assertCount(1, $errors);
33+
$this->assertStringContainsString('Malformed inline YAML', $errors->first());
34+
}
35+
36+
public function testParseTwig()
37+
{
38+
$node = new CodeNode(['{{ form(form) }}']);
39+
$node->setEnvironment($this->environment);
40+
$node->setLanguage('twig');
41+
$errors = $this->validator->validateNodes([$node]);
42+
$this->assertCount(0, $errors);
43+
}
44+
}

0 commit comments

Comments
 (0)
0