3
3
namespace SymfonyTools \CodeBlockChecker \Service \CodeValidator ;
4
4
5
5
use Doctrine \RST \Nodes \CodeNode ;
6
- use Symfony \Component \Process \Process ;
6
+ use PhpParser \ErrorHandler ;
7
+ use PhpParser \Parser ;
8
+ use PhpParser \ParserFactory ;
7
9
use SymfonyTools \CodeBlockChecker \Issue \Issue ;
8
10
use SymfonyTools \CodeBlockChecker \Issue \IssueCollection ;
9
11
10
12
class PhpValidator implements Validator
11
13
{
14
+ private ?Parser $ parser = null ;
15
+
12
16
public function validate (CodeNode $ node , IssueCollection $ issues ): void
13
17
{
14
18
$ language = $ node ->getLanguage () ?? ($ node ->isRaw () ? null : 'php ' );
15
19
if (!in_array ($ language , ['php ' , 'php-symfony ' , 'php-standalone ' , 'php-annotations ' , 'html+php ' ])) {
16
20
return ;
17
21
}
18
22
19
- $ file = sys_get_temp_dir (). ' / ' . uniqid ( ' doc_builder ' , true ). ' .php ' ;
20
-
21
- file_put_contents ( $ file , $ this ->getContents ( $ node , $ language ));
23
+ $ linesPrepended = 0 ;
24
+ $ code = ' html+php ' === $ language ? $ node -> getValue () : $ this -> getContents ( $ node , $ linesPrepended );
25
+ $ this ->getParser ()-> parse ( $ code , $ errorHandler = new ErrorHandler \ Collecting ( ));
22
26
23
- $ process = new Process (['php ' , '-l ' , $ file ]);
24
- $ process ->run ();
25
- if ($ process ->isSuccessful ()) {
26
- return ;
27
+ foreach ($ errorHandler ->getErrors () as $ error ) {
28
+ $ issues ->addIssue (new Issue ($ node , $ error ->getRawMessage (), 'PHP syntax ' , $ node ->getEnvironment ()->getCurrentFileName (), $ error ->getStartLine () - $ linesPrepended ));
27
29
}
30
+ }
28
31
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"
32
+ private function getParser (): Parser
33
+ {
34
+ if (null === $ this ->parser ) {
35
+ $ this ->parser = (new ParserFactory ())->create (ParserFactory::ONLY_PHP7 );
34
36
}
35
- $ issues ->addIssue (new Issue ($ node , $ text , 'PHP syntax ' , $ node ->getEnvironment ()->getCurrentFileName (), $ line ));
37
+
38
+ return $ this ->parser ;
36
39
}
37
40
38
- private function getContents (CodeNode $ node , string $ language ): string
41
+ private function getContents (CodeNode $ node , & $ linesPrepended = null ): string
39
42
{
40
43
$ contents = $ node ->getValue ();
41
- if ('html+php ' === $ language ) {
42
- return $ contents ;
43
- }
44
-
45
44
if (!preg_match ('#(class|interface) [a-zA-Z]+#s ' , $ contents ) && preg_match ('#(public|protected|private)( static)? (\$[a-z]+|function)#s ' , $ contents )) {
46
45
$ contents = 'class Foobar { ' .$ contents .'} ' ;
47
46
}
@@ -52,6 +51,7 @@ private function getContents(CodeNode $node, string $language): string
52
51
$ lines = explode ("\n" , $ contents );
53
52
if (!str_contains ($ lines [0 ] ?? '' , '<?php ' ) && !str_contains ($ lines [1 ] ?? '' , '<?php ' ) && !str_contains ($ lines [2 ] ?? '' , '<?php ' )) {
54
53
$ contents = '<?php ' ."\n" .$ contents ;
54
+ $ linesPrepended = 1 ;
55
55
}
56
56
57
57
return $ contents ;
0 commit comments