3
3
namespace SymfonyTools \CodeBlockChecker \Service \CodeValidator ;
4
4
5
5
use Doctrine \RST \Nodes \CodeNode ;
6
+ use PhpParser \Parser ;
7
+ use PhpParser \ParserFactory ;
6
8
use Symfony \Component \Process \Process ;
7
9
use SymfonyTools \CodeBlockChecker \Issue \Issue ;
8
10
use SymfonyTools \CodeBlockChecker \Issue \IssueCollection ;
11
+ use PhpParser \ErrorHandler ;
9
12
10
13
class PhpValidator implements Validator
11
14
{
15
+ private ?Parser $ parser = null ;
16
+
12
17
public function validate (CodeNode $ node , IssueCollection $ issues ): void
13
18
{
14
19
$ 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 ' ])) {
16
21
return ;
17
22
}
18
23
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 ();
22
26
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 ));
27
30
}
31
+ }
28
32
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 );
34
37
}
35
- $ issues ->addIssue (new Issue ($ node , $ text , 'PHP syntax ' , $ node ->getEnvironment ()->getCurrentFileName (), $ line ));
38
+
39
+ return $ this ->parser ;
36
40
}
37
41
38
- private function getContents (CodeNode $ node , string $ language ): string
42
+ private function getContents (CodeNode $ node , & $ linesPrepended = null ): string
39
43
{
40
44
$ contents = $ node ->getValue ();
41
- if ('html+php ' === $ language ) {
42
- return $ contents ;
43
- }
44
-
45
45
if (!preg_match ('#(class|interface) [a-zA-Z]+#s ' , $ contents ) && preg_match ('#(public|protected|private)( static)? (\$[a-z]+|function)#s ' , $ contents )) {
46
46
$ contents = 'class Foobar { ' .$ contents .'} ' ;
47
47
}
@@ -52,6 +52,7 @@ private function getContents(CodeNode $node, string $language): string
52
52
$ lines = explode ("\n" , $ contents );
53
53
if (!str_contains ($ lines [0 ] ?? '' , '<?php ' ) && !str_contains ($ lines [1 ] ?? '' , '<?php ' ) && !str_contains ($ lines [2 ] ?? '' , '<?php ' )) {
54
54
$ contents = '<?php ' ."\n" .$ contents ;
55
+ $ linesPrepended = 1 ;
55
56
}
56
57
57
58
return $ contents ;
0 commit comments