Linter   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 254
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 4
dl 0
loc 254
rs 9.84
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
C lint() 0 58 15
A setCache() 0 8 2
A getFiles() 0 14 5
A getFilesFromDir() 0 16 1
A setFiles() 0 16 4
A setProcessCallback() 0 6 1
A setProcessLimit() 0 6 1
A createLintProcess() 0 11 2
1
<?php
2
3
/*
4
 * This file is part of the overtrue/phplint
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Overtrue\PHPLint;
13
14
use InvalidArgumentException;
15
use Overtrue\PHPLint\Process\Lint;
16
use Symfony\Component\Finder\Finder;
17
use Symfony\Component\Finder\SplFileInfo;
18
19
/**
20
 * Class Linter.
21
 */
22
class Linter
23
{
24
    /**
25
     * @var callable
26
     */
27
    private $processCallback;
28
29
    /**
30
     * @var SplFileInfo[]
31
     */
32
    private $files = [];
33
34
    /**
35
     * @var array
36
     */
37
    private $cache = [];
38
39
    /**
40
     * @var array
41
     */
42
    private $path;
43
44
    /**
45
     * @var array
46
     */
47
    private $excludes;
48
49
    /**
50
     * @var array
51
     */
52
    private $extensions;
53
54
    /**
55
     * @var int
56
     */
57
    private $processLimit = 5;
58
59
    /**
60
     * @var bool
61
     */
62
    private $warning;
63
64
    /**
65
     * Constructor.
66
     *
67
     * @param string|array $path
68
     * @param array        $excludes
69
     * @param array        $extensions
70
     * @param bool         $warning
71
     */
72
    public function __construct($path, array $excludes = [], array $extensions = ['php'], $warning = false)
73
    {
74
        $this->path = (array)$path;
75
        $this->excludes = $excludes;
76
        $this->extensions = \array_map(function ($extension) {
77
            return \sprintf('*.%s', \ltrim($extension, '.'));
78
        }, $extensions);
79
        $this->warning = $warning;
80
    }
81
82
    /**
83
     * Check the files.
84
     *
85
     * @param SplFileInfo[] $files
86
     * @param bool          $cache
87
     *
88
     * @return array
89
     */
90
    public function lint($files = [], $cache = true)
91
    {
92
        if (empty($files)) {
93
            $files = $this->getFiles();
94
        }
95
96
        $processCallback = is_callable($this->processCallback) ? $this->processCallback : function () {
97
        };
98
99
        $errors = [];
100
        $running = [];
101
        $newCache = [];
102
103
        while (!empty($files) || !empty($running)) {
104
            for ($i = count($running); !empty($files) && $i < $this->processLimit; ++$i) {
105
                $file = array_shift($files);
106
                $filename = $file->getRealPath();
107
                $relativePathname = $file->getRelativePathname();
108
                if (!isset($this->cache[$relativePathname]) || $this->cache[$relativePathname] !== md5_file($filename)) {
109
                    $lint = $this->createLintProcess($filename);
110
                    $running[$filename] = [
111
                        'process' => $lint,
112
                        'file' => $file,
113
                        'relativePath' => $relativePathname,
114
                    ];
115
                    $lint->start();
116
                } else {
117
                    $newCache[$relativePathname] = $this->cache[$relativePathname];
118
                }
119
            }
120
121
            foreach ($running as $filename => $item) {
122
                /** @var Lint $lint */
123
                $lint = $item['process'];
124
125
                if ($lint->isRunning()) {
126
                    continue;
127
                }
128
129
                unset($running[$filename]);
130
131
                if ($lint->hasSyntaxError()) {
132
                    $processCallback('error', $item['file']);
133
                    $errors[$filename] = array_merge(['file' => $filename, 'file_name' => $item['relativePath']], $lint->getSyntaxError());
134
                } elseif ($this->warning && $lint->hasSyntaxIssue()) {
135
                    $processCallback('warning', $item['file']);
136
                    $errors[$filename] = array_merge(['file' => $filename, 'file_name' => $item['relativePath']], $lint->getSyntaxIssue());
137
                } else {
138
                    $newCache[$item['relativePath']] = md5_file($filename);
139
                    $processCallback('ok', $item['file']);
140
                }
141
            }
142
        }
143
144
        $cache && Cache::put($newCache);
145
146
        return $errors;
147
    }
148
149
    /**
150
     * Cache setter.
151
     *
152
     * @param array $cache
153
     */
154
    public function setCache($cache = [])
155
    {
156
        if (is_array($cache)) {
157
            $this->cache = $cache;
158
        } else {
159
            $this->cache = [];
160
        }
161
    }
162
163
    /**
164
     * Fetch files.
165
     *
166
     * @return SplFileInfo[]
167
     */
168
    public function getFiles()
169
    {
170
        if (empty($this->files)) {
171
            foreach ($this->path as $path) {
172
                if (is_dir($path)) {
173
                    $this->files = array_merge($this->files, $this->getFilesFromDir($path));
174
                } elseif (is_file($path)) {
175
                    $this->files[$path] = new SplFileInfo($path, $path, $path);
176
                }
177
            }
178
        }
179
180
        return $this->files;
181
    }
182
183
    /**
184
     * Get files from directory.
185
     *
186
     * @param string $dir
187
     *
188
     * @return SplFileInfo[]
189
     */
190
    protected function getFilesFromDir($dir)
191
    {
192
        $finder = new Finder();
193
        $finder->files()
194
            ->ignoreUnreadableDirs()
195
            ->ignoreVCS(true)
196
            ->filter(function (SplFileInfo $file) {
197
                return $file->isReadable();
198
            })
199
            ->in(realpath($dir));
200
201
        array_map([$finder, 'name'], $this->extensions);
202
        array_map([$finder, 'notPath'], $this->excludes);
203
204
        return iterator_to_array($finder);
205
    }
206
207
    /**
208
     * Set Files.
209
     *
210
     * @param string[] $files
211
     *
212
     * @return \Overtrue\PHPLint\Linter
213
     */
214
    public function setFiles(array $files)
215
    {
216
        foreach ($files as $file) {
217
            if (is_file($file)) {
218
                $file = new SplFileInfo($file, $file, $file);
219
            }
220
221
            if (!($file instanceof SplFileInfo)) {
222
                throw new InvalidArgumentException("File $file not exists.");
223
            }
224
225
            $this->files[$file->getRealPath()] = $file;
226
        }
227
228
        return $this;
229
    }
230
231
    /**
232
     * Set process callback.
233
     *
234
     * @param callable $processCallback
235
     *
236
     * @return Linter
237
     */
238
    public function setProcessCallback($processCallback)
239
    {
240
        $this->processCallback = $processCallback;
241
242
        return $this;
243
    }
244
245
    /**
246
     * Set process limit.
247
     *
248
     * @param int $processLimit
249
     *
250
     * @return \Overtrue\PHPLint\Linter
251
     */
252
    public function setProcessLimit($processLimit)
253
    {
254
        $this->processLimit = $processLimit;
255
256
        return $this;
257
    }
258
259
    /**
260
     * @param string $filename
261
     *
262
     * @return mixed
263
     */
264
    protected function createLintProcess($filename)
265
    {
266
        $command = [
267
            PHP_SAPI == 'cli' ? PHP_BINARY : PHP_BINDIR . '/php',
268
            '-d error_reporting=E_ALL',
269
            '-d display_errors=On',
270
            '-l', $filename,
271
        ];
272
273
        return new Lint($command);
274
    }
275
}
276