Passed
Pull Request — master (#861)
by SignpostMarv
06:33 queued 02:07
created

StatementsProvider::parseStatements()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 9
nop 1
dl 0
loc 30
rs 8.8177
c 0
b 0
f 0
1
<?php
2
namespace Psalm\Provider;
3
4
use PhpParser;
5
6
class StatementsProvider
7
{
8
    /**
9
     * @var FileProvider
10
     */
11
    private $file_provider;
12
13
    /**
14
     * @var ParserCacheProvider
15
     */
16
    private $cache_provider;
17
18
    /**
19
     * @var int
20
     */
21
    private $this_modified_time;
22
23
    /**
24
     * @var FileStorageCacheProvider
25
     */
26
    private $file_storage_cache_provider;
27
28
    /**
29
     * @var PhpParser\Parser|null
30
     */
31
    protected static $parser;
32
33
    public function __construct(
34
        FileProvider $file_provider,
35
        ParserCacheProvider $cache_provider,
36
        FileStorageCacheProvider $file_storage_cache_provider
37
    ) {
38
        $this->file_provider = $file_provider;
39
        $this->cache_provider = $cache_provider;
40
        $this->this_modified_time = filemtime(__FILE__);
41
        $this->file_storage_cache_provider = $file_storage_cache_provider;
42
    }
43
44
    /**
45
     * @param  string  $file_path
46
     * @param  bool    $debug_output
47
     *
48
     * @return array<int, \PhpParser\Node\Stmt>
49
     */
50
    public function getStatementsForFile($file_path, $debug_output = false)
51
    {
52
        $from_cache = false;
53
54
        $version = (string) PHP_PARSER_VERSION . $this->this_modified_time;
55
56
        $file_contents = $this->file_provider->getContents($file_path);
57
        $modified_time = $this->file_provider->getModifiedTime($file_path);
58
59
        $file_content_hash = md5($version . $file_contents);
60
        $file_cache_key = $this->cache_provider->getParserCacheKey($file_path, $this->cache_provider->use_igbinary);
61
62
        $stmts = $this->cache_provider->loadStatementsFromCache(
63
            $modified_time,
64
            $file_content_hash,
65
            $file_cache_key
66
        );
67
68
        if ($stmts === null) {
69
            if ($debug_output) {
70
                echo 'Parsing ' . $file_path . "\n";
71
            }
72
73
            $stmts = self::parseStatements($file_contents);
74
            $this->file_storage_cache_provider->removeCacheForFile($file_path);
75
        } else {
76
            $from_cache = true;
77
        }
78
79
        $this->cache_provider->saveStatementsToCache($file_cache_key, $file_content_hash, $stmts, $from_cache);
80
81
        if (!$stmts) {
82
            return [];
83
        }
84
85
        return $stmts;
86
    }
87
88
    /**
89
     * @param  string   $file_contents
90
     *
91
     * @return array<int, \PhpParser\Node\Stmt>
92
     */
93
    public static function parseStatements($file_contents)
94
    {
95
        if (!self::$parser) {
96
            $lexer = version_compare(PHP_VERSION, '7.0.0dev', '>=')
97
                ? new PhpParser\Lexer([
98
                    'usedAttributes' => [
99
                        'comments', 'startLine', 'startFilePos', 'endFilePos',
100
                    ],
101
                ])
102
                : new PhpParser\Lexer\Emulative([
103
                    'usedAttributes' => [
104
                        'comments', 'startLine', 'startFilePos', 'endFilePos',
105
                    ],
106
                ]);
107
108
            self::$parser = (new PhpParser\ParserFactory())->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
109
        }
110
111
        $error_handler = new \PhpParser\ErrorHandler\Collecting();
112
113
        /** @var array<int, \PhpParser\Node\Stmt> */
114
        $stmts = self::$parser->parse($file_contents, $error_handler);
115
116
        if (!$stmts && $error_handler->hasErrors()) {
0 ignored issues
show
Bug introduced by
The method hasErrors() does not exist on PhpParser\ErrorHandler. It seems like you code against a sub-type of PhpParser\ErrorHandler such as PhpParser\ErrorHandler\Collecting. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

116
        if (!$stmts && $error_handler->/** @scrutinizer ignore-call */ hasErrors()) {
Loading history...
117
            foreach ($error_handler->getErrors() as $error) {
0 ignored issues
show
Bug introduced by
The method getErrors() does not exist on PhpParser\ErrorHandler. It seems like you code against a sub-type of PhpParser\ErrorHandler such as PhpParser\ErrorHandler\Collecting. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

117
            foreach ($error_handler->/** @scrutinizer ignore-call */ getErrors() as $error) {
Loading history...
118
                throw $error;
119
            }
120
        }
121
122
        return $stmts;
123
    }
124
}
125