Skip to content

Commit 6fae348

Browse files
committed
Speed up things by using nikic/php-parser
1 parent 1be6d99 commit 6fae348

File tree

4 files changed

+62
-19
lines changed

4 files changed

+62
-19
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"ext-iconv": "*",
99
"ext-libxml": "*",
1010
"doctrine/rst-parser": "^0.4",
11+
"nikic/php-parser": "^4.10",
1112
"symfony/console": "^5.2",
1213
"symfony/docs-builder": "^0.15.0",
1314
"symfony/dotenv": "^5.2",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace SymfonyTools\CodeBlockChecker\Service\CodeValidator;
4+
5+
use Doctrine\RST\Nodes\CodeNode;
6+
use Symfony\Component\Process\Process;
7+
use SymfonyTools\CodeBlockChecker\Issue\Issue;
8+
use SymfonyTools\CodeBlockChecker\Issue\IssueCollection;
9+
10+
class HtmlPhpValidator implements Validator
11+
{
12+
public function validate(CodeNode $node, IssueCollection $issues): void
13+
{
14+
if ('html+php' !== $node->getLanguage()) {
15+
return;
16+
}
17+
18+
$file = sys_get_temp_dir().'/'.uniqid('doc_builder', true).'.php';
19+
file_put_contents($file, $node->getValue());
20+
21+
$process = new Process(['php', '-l', $file]);
22+
$process->run();
23+
if ($process->isSuccessful()) {
24+
return;
25+
}
26+
27+
$line = 0;
28+
$text = str_replace($file, 'example.php', $process->getErrorOutput());
29+
if (preg_match('| in example.php on line ([0-9]+)|s', $text, $matches)) {
30+
$text = str_replace($matches[0], '', $text);
31+
$line = ((int) $matches[1]);
32+
}
33+
$issues->addIssue(new Issue($node, $text, 'PHP syntax', $node->getEnvironment()->getCurrentFileName(), $line));
34+
}
35+
}

src/Service/CodeValidator/PhpValidator.php

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,45 @@
33
namespace SymfonyTools\CodeBlockChecker\Service\CodeValidator;
44

55
use Doctrine\RST\Nodes\CodeNode;
6+
use PhpParser\Parser;
7+
use PhpParser\ParserFactory;
68
use Symfony\Component\Process\Process;
79
use SymfonyTools\CodeBlockChecker\Issue\Issue;
810
use SymfonyTools\CodeBlockChecker\Issue\IssueCollection;
11+
use PhpParser\ErrorHandler;
912

1013
class PhpValidator implements Validator
1114
{
15+
private ?Parser $parser = null;
16+
1217
public function validate(CodeNode $node, IssueCollection $issues): void
1318
{
1419
$language = $node->getLanguage() ?? ($node->isRaw() ? null : 'php');
15-
if (!in_array($language, ['php', 'php-symfony', 'php-standalone', 'php-annotations', 'html+php'])) {
20+
if (!in_array($language, ['php', 'php-symfony', 'php-standalone', 'php-annotations'])) {
1621
return;
1722
}
1823

19-
$file = sys_get_temp_dir().'/'.uniqid('doc_builder', true).'.php';
20-
21-
file_put_contents($file, $this->getContents($node, $language));
24+
$parser = $this->getParser();
25+
$errorHandler = new ErrorHandler\Collecting();
2226

23-
$process = new Process(['php', '-l', $file]);
24-
$process->run();
25-
if ($process->isSuccessful()) {
26-
return;
27+
$parser->parse($this->getContents($node, $linesPrepended), $errorHandler);
28+
foreach ($errorHandler->getErrors() as $error) {
29+
$issues->addIssue(new Issue($node, $error->getRawMessage(), 'PHP syntax', $node->getEnvironment()->getCurrentFileName(), $error->getStartLine() - $linesPrepended));
2730
}
31+
}
2832

29-
$line = 0;
30-
$text = str_replace($file, 'example.php', $process->getErrorOutput());
31-
if (preg_match('| in example.php on line ([0-9]+)|s', $text, $matches)) {
32-
$text = str_replace($matches[0], '', $text);
33-
$line = ((int) $matches[1]) - 1; // we added "<?php"
33+
private function getParser(): Parser
34+
{
35+
if (null === $this->parser) {
36+
$this->parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7);
3437
}
35-
$issues->addIssue(new Issue($node, $text, 'PHP syntax', $node->getEnvironment()->getCurrentFileName(), $line));
38+
39+
return $this->parser;
3640
}
3741

38-
private function getContents(CodeNode $node, string $language): string
42+
private function getContents(CodeNode $node, &$linesPrepended = null): string
3943
{
4044
$contents = $node->getValue();
41-
if ('html+php' === $language) {
42-
return $contents;
43-
}
44-
4545
if (!preg_match('#(class|interface) [a-zA-Z]+#s', $contents) && preg_match('#(public|protected|private)( static)? (\$[a-z]+|function)#s', $contents)) {
4646
$contents = 'class Foobar {'.$contents.'}';
4747
}
@@ -52,6 +52,7 @@ private function getContents(CodeNode $node, string $language): string
5252
$lines = explode("\n", $contents);
5353
if (!str_contains($lines[0] ?? '', '<?php') && !str_contains($lines[1] ?? '', '<?php') && !str_contains($lines[2] ?? '', '<?php')) {
5454
$contents = '<?php'."\n".$contents;
55+
$linesPrepended = 1;
5556
}
5657

5758
return $contents;

tests/Service/Validator/PhpValidatorTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ public static function validate($object, ExecutionContextInterface $context, $pa
6969
;
7070
}
7171
}
72+
'];
73+
yield [1, '
74+
public static function validate($object, ExecutionContextInterface $context, $payload)
75+
{
76+
$foo = 2a
77+
}
7278
'];
7379
}
7480
}

0 commit comments

Comments
 (0)