Completed
Push — master ( 6fd4d1...dab041 )
by Carlos
01:32
created

Linter::createLintProcess()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 1
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
     * Constructor.
61
     *
62
     * @param string|array $path
63
     * @param array        $excludes
64
     * @param array        $extensions
65
     */
66
    public function __construct($path, array $excludes = [], array $extensions = ['php'])
67
    {
68
        $this->path = (array) $path;
69
        $this->excludes = $excludes;
70
        $this->extensions = $extensions;
71
    }
72
73
    /**
74
     * Check the files.
75
     *
76
     * @param SplFileInfo[] $files
77
     * @param bool          $cache
78
     *
79
     * @return array
80
     */
81
    public function lint($files = [], $cache = true)
82
    {
83
        if (empty($files)) {
84
            $files = $this->getFiles();
85
        }
86
87
        $processCallback = is_callable($this->processCallback) ? $this->processCallback : function () {
88
        };
89
90
        $errors = [];
91
        $running = [];
92
        $newCache = [];
93
94
        while (!empty($files) || !empty($running)) {
95
            for ($i = count($running); !empty($files) && $i < $this->processLimit; ++$i) {
96
                $file = array_shift($files);
97
                $filename = $file->getRealPath();
98
                $relativePathname = $file->getRelativePathname();
99
                if (!isset($this->cache[$relativePathname]) || $this->cache[$relativePathname] !== md5_file($filename)) {
100
                    $lint = $this->createLintProcess($filename);
101
                    $running[$filename] = [
102
                        'process' => $lint,
103
                        'file' => $file,
104
                        'relativePath' => $relativePathname,
105
                    ];
106
                    $lint->start();
107
                } else {
108
                    $newCache[$relativePathname] = $this->cache[$relativePathname];
109
                }
110
            }
111
112
            foreach ($running as $filename => $item) {
113
                /** @var Lint $lint */
114
                $lint = $item['process'];
115
                if ($lint->isRunning()) {
116
                    continue;
117
                }
118
119
                unset($running[$filename]);
120
                if ($lint->hasSyntaxError()) {
121
                    $processCallback('error', $item['file']);
122
                    $errors[$filename] = array_merge(['file' => $filename], $lint->getSyntaxError());
123
                } else {
124
                    $newCache[$item['relativePath']] = md5_file($filename);
125
                    $processCallback('ok', $item['file']);
126
                }
127
            }
128
        }
129
130
        $cache && Cache::put($newCache);
131
132
        return $errors;
133
    }
134
135
    /**
136
     * Cache setter.
137
     *
138
     * @param array $cache
139
     */
140
    public function setCache($cache = [])
141
    {
142
        if (is_array($cache)) {
143
            $this->cache = $cache;
144
        } else {
145
            $this->cache = [];
146
        }
147
    }
148
149
    /**
150
     * Fetch files.
151
     *
152
     * @return SplFileInfo[]
153
     */
154
    public function getFiles()
155
    {
156
        if (empty($this->files)) {
157
            foreach ($this->path as $path) {
158
                if (is_dir($path)) {
159
                    $this->files = array_merge($this->files, $this->getFilesFromDir($path));
160
                } elseif (is_file($path)) {
161
                    $this->files[$path] = new SplFileInfo($path, $path, $path);
162
                }
163
            }
164
        }
165
166
        return $this->files;
167
    }
168
169
    /**
170
     * Get files from directory.
171
     *
172
     * @param string $dir
173
     *
174
     * @return SplFileInfo[]
175
     */
176
    protected function getFilesFromDir($dir)
177
    {
178
        $finder = new Finder();
179
        $finder->files()->ignoreUnreadableDirs()->in(realpath($dir));
180
181
        foreach ($this->excludes as $exclude) {
182
            $finder->notPath($exclude);
183
        }
184
185
        foreach ($this->extensions as $extension) {
186
            $finder->name('*.'.$extension);
187
        }
188
189
        return iterator_to_array($finder);
190
    }
191
192
    /**
193
     * Set Files.
194
     *
195
     * @param string[] $files
196
     *
197
     * @return \Overtrue\PHPLint\Linter
198
     */
199
    public function setFiles(array $files)
200
    {
201
        foreach ($files as $file) {
202
            if (is_file($file)) {
203
                $file = new SplFileInfo($file, $file, $file);
204
            }
205
206
            if (!($file instanceof SplFileInfo)) {
207
                throw new InvalidArgumentException("File $file not exists.");
208
            }
209
210
            $this->files[$file->getRealPath()] = $file;
211
        }
212
213
        return $this;
214
    }
215
216
    /**
217
     * Set process callback.
218
     *
219
     * @param callable $processCallback
220
     *
221
     * @return Linter
222
     */
223
    public function setProcessCallback($processCallback)
224
    {
225
        $this->processCallback = $processCallback;
226
227
        return $this;
228
    }
229
230
    /**
231
     * Set process limit.
232
     *
233
     * @param int $processLimit
234
     *
235
     * @return \Overtrue\PHPLint\Linter
236
     */
237
    public function setProcessLimit($processLimit)
238
    {
239
        $this->processLimit = $processLimit;
240
241
        return $this;
242
    }
243
244
    /**
245
     * @param string $filename
246
     *
247
     * @return mixed
248
     */
249
    protected function createLintProcess($filename)
250
    {
251
        $command = [
252
            PHP_SAPI == 'cli' ? PHP_BINARY : PHP_BINDIR.'/php',
253
            '-d error_reporting=E_ALL',
254
            '-d display_errors=On',
255
            '-l', $filename,
256
        ];
257
258
        return new Lint($command);
259
    }
260
}
261