Completed
Push — master ( fff85e...2ddb57 )
by Maarten
01:44 queued 10s
created

LaravelEnvScanner::storeResult()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 9.488
c 0
b 0
f 0
cc 4
nc 6
nop 1
1
<?php
2
3
namespace Mtolhuys\LaravelEnvScanner;
4
5
use RecursiveDirectoryIterator;
6
use RecursiveIteratorIterator;
7
use RegexIterator;
8
9
class LaravelEnvScanner
10
{
11
    /**
12
     * The results of performed scan
13
     *
14
     * @var array
15
     */
16
    public $results = [
17
        'files' => 0,
18
        'defined' => 0,
19
        'undefined' => 0,
20
        'depending_on_default' => 0,
21
        'columns' => [],
22
    ];
23
24
    /**
25
     * Stores processed file and var names
26
     *
27
     * @var array
28
     */
29
    private $processed = [
30
        'files' => [],
31
        'vars' => [],
32
    ];
33
34
    /**
35
     * Stores undefined var names
36
     *
37
     * @var array
38
     */
39
    public $undefined = [];
40
41
    /**
42
     * Stores warnings for vars not passing validation
43
     *
44
     * @var array
45
     */
46
    public $warnings = [];
47
48
    /**
49
     * Current file being processed
50
     *
51
     * @var string
52
     */
53
    private $currentFile;
54
55
    /**
56
     * Root directory to start recursive search for env()'s from
57
     * Defaults to config_path()
58
     *
59
     * @var string $dir
60
     */
61
    public $dir;
62
63
    public function __construct(string $dir = null)
64
    {
65
        $this->dir = $dir ?? config_path();
66
    }
67
68
    /**
69
     * Run the scan
70
     *
71
     * @return mixed
72
     * @throws \Exception
73
     */
74
    public function scan()
75
    {
76
        $files = $this->recursiveDirSearch($this->dir, '/.*?.php/');
77
78
        foreach ($files as $file) {
79
            preg_match_all(
80
                '# env\((.*?)\)| getenv\((.*?)\)#',
81
                str_replace(["\n", "\r"], '', file_get_contents($file)),
82
                $matches
83
            );
84
85
            if (empty(array_filter($matches))) {
86
                continue;
87
            }
88
89
            $this->currentFile = $file;
90
            $invocations = $matches[0];
91
92
            foreach ($invocations as $index => $invocation) {
93
                $result = $this->getResult($invocation, [
94
                    $matches[1][$index],
95
                    $matches[2][$index]
96
                ]);
97
98
                if (!$result) {
99
                    continue;
100
                }
101
102
                $this->storeResult($result);
103
            }
104
        }
105
106
        return $this;
107
    }
108
109
    /**
110
     * Get result based on comma separated parsed env() or getenv() parameters
111
     * Validates by alphanumeric and underscore and skips already processed
112
     *
113
     * @param string $invocation
114
     * @param array $matches
115
     * @return object|bool
116
     */
117
    private function getResult(string $invocation, array $matches)
118
    {
119
        $params = explode(',', str_replace(
120
            ["'", '"', ' '], '', empty($matches[0]) ? $matches[1] : $matches[0]
121
        ));
122
123
        $envVar = $params[0];
124
125
        if (in_array($envVar, $this->processed['vars'])) {
126
            return false;
127
        }
128
129
        $this->processed['vars'][] = $envVar;
130
131
        if (!preg_match('/^[A-Za-z0-9_]+$/', $envVar)) {
132
            $invocation = str_replace(' ', '', $invocation);
133
134
            $this->warnings[] = (object)[
135
                'filename' => $this->currentFile,
136
                'invocation' => $invocation,
137
            ];
138
139
            return false;
140
        }
141
142
        return (object)[
143
            'envVar' => $envVar,
144
            'hasValue' => env($envVar) !== null,
145
            'hasDefault' => isset($params[1]),
146
        ];
147
    }
148
149
    /**
150
     * Store result and optional runtime output
151
     *
152
     * @param $result
153
     */
154
    private function storeResult($result)
155
    {
156
        $resultData = [
157
            'filename' => $this->getColumnFilename(),
158
            'defined' => '-',
159
            'depending_on_default' => '-',
160
            'undefined' => '-',
161
        ];
162
163
        if ($result->hasValue) {
164
            $resultData['defined'] = $result->envVar;
165
            $this->results['defined']++;
166
        } else if ($result->hasDefault) {
167
            $resultData['depending_on_default'] = $result->envVar;
168
            $this->results['depending_on_default']++;
169
        } else {
170
            $resultData['undefined'] = $this->undefined[] = $result->envVar;
171
            $this->results['undefined']++;
172
        }
173
174
        $this->results['columns'][] = $resultData;
175
176
        if (!in_array($this->currentFile, $this->processed['files'])) {
177
            $this->results['files']++;
178
            $this->processed['files'][] = $this->currentFile;
179
        }
180
    }
181
182
    /**
183
     * Return filename or '-' for table
184
     *
185
     * @return string
186
     */
187
    private function getColumnFilename(): string
188
    {
189
        if (in_array($this->currentFile, $this->processed['files'])) {
190
            return '-';
191
        }
192
193
        return basename($this->currentFile);
194
    }
195
196
    private function recursiveDirSearch(string $folder, string $pattern): array
197
    {
198
        if (!file_exists($folder)) {
199
            return [];
200
        }
201
202
        $files = new RegexIterator(
203
            new RecursiveIteratorIterator(
204
                new RecursiveDirectoryIterator($folder)
205
            ),
206
            $pattern, RegexIterator::GET_MATCH
207
        );
208
209
        $list = [];
210
211
        foreach ($files as $file) {
212
            $list = array_merge($list, $file);
213
        }
214
215
        return $list;
216
    }
217
}
218