ViewParser::getViewFromLine()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
namespace TypeHints\Unused\Parser;
4
5
use Illuminate\Filesystem\Filesystem;
6
use TypeHints\Unused\Parser\Action\ParserActionInterface;
7
8
class ViewParser implements ParserInterface
9
{
10
    /**
11
     * @var ParserActionInterface
12
     */
13
    protected $action;
14
15
    /**
16
     * @var array
17
     */
18
    protected $parent = [];
19
20
    /**
21
     * @var array
22
     */
23
    protected $children = [];
24
25
    /**
26
     * @var array
27
     */
28
    protected $childrenViews = [];
29
30
    /**
31
     * @var array
32
     */
33
    protected $viewAliases = [
34
        'View::make(',
35
        'view(',
36
        'view->make(',
37
    ];
38
39
    /**
40
     * @var array
41
     */
42
    protected $ignoredStrings = [
43
        '(',
44
        ')',
45
        ';',
46
        "'",
47
    ];
48
49
    /**
50
     * @var array
51
     */
52
    protected $bladeDirectives = [
53
        '@include(',
54
        '@includeIf(',
55
        '@extends(',
56
        'Blade::include(',
57
    ];
58
59
    /**
60
     * @var array
61
     */
62
    protected $statementBladeDirectives = [
63
        '@includeWhen(',
64
        '@includeUnless(',
65
        '@includeFirst(',
66
    ];
67
68
    /**
69
     * @param ParserActionInterface
70
     */
71
    public function __construct(ParserActionInterface $action)
72
    {
73
        $this->action = $action;
74
    }
75
76
    /**
77
     * @return ParserInterface
78
     */
79
    public function parse(): ParserInterface
80
    {
81
        $this->parent = $this->retrieveViewsFromMethod();
82
83
        if ($this->parent) {
84
            $this->retrieveChildrenFromNestedViews();
85
        }
86
87
        return $this;
88
    }
89
90
    public function retrieveChildrenFromNestedViews(): void
91
    {
92
        $this->children = $this->loopForNestedViews($this->parent);
93
94
        $this->resolveChildrenHierarchy($this->children);
95
    }
96
97
    /**
98
     * @param array $children
99
     */
100
    public function resolveChildrenHierarchy(array $children): void
101
    {
102
        collect($children)->each(function ($value, $key) {
103
            if (is_string($key)) {
104
                $this->childrenViews[] = $key;
105
            }
106
107
            return $this->resolveChildrenHierarchy($value);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->resolveChildrenHierarchy($value) targeting TypeHints\Unused\Parser\...olveChildrenHierarchy() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
108
        });
109
    }
110
111
    public function loopForNestedViews($views): array
112
    {
113
        $generated = [];
114
115
        if (!is_array($views)) {
116
            return $this->loopForNestedViews($this->retrieveNestedViews($views));
117
        }
118
119
        foreach ($views as $view) {
120
            if (!empty($this->loopForNestedViews($view))) {
121
                $generated[$view][] = $this->loopForNestedViews($view);
122
            } else {
123
                $generated[$view] = $this->loopForNestedViews($view);
124
            }
125
        }
126
127
        return $generated;
128
    }
129
130
    /**
131
     * @return array
132
     */
133
    protected function retrieveViewsFromMethod(): array
134
    {
135
        $views = [];
136
137
        $content = $this->action->getContent();
0 ignored issues
show
Bug introduced by
The method getContent() does not exist on TypeHints\Unused\Parser\...n\ParserActionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to TypeHints\Unused\Parser\...n\ParserActionInterface. ( Ignorable by Annotation )

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

137
        /** @scrutinizer ignore-call */ 
138
        $content = $this->action->getContent();
Loading history...
138
139
        if (!$content) {
140
            return [];
141
        }
142
143
        if (array_key_exists('view', ($content))) {
144
            $views[] = $content['view'];
145
146
            return $views;
147
        }
148
149
        foreach ($content as $key => $line) {
150
            foreach ($this->viewAliases as $viewAlias) {
151
                if (strpos($line, $viewAlias) !== false) {
152
                    $view = $this->getViewFromLine($line, $viewAlias);
153
154
                    if (empty($view)) {
155
                        $view = $this->getViewFromLine($content[$key + 1], $viewAlias);
156
                    }
157
158
                    $views[] = $this->retrieveViewFromLine($view, $viewAlias);
159
                }
160
            }
161
        }
162
163
        return $views;
164
    }
165
166
    protected function getViewFromLine($line, $viewAlias)
167
    {
168
        $line = trim($line);
169
170
        if (strpos($line, $viewAlias) === false) {
171
            return $line;
172
        }
173
174
        return substr($line, strpos($line, $viewAlias) + strlen($viewAlias));
175
    }
176
177
    /**
178
     * @param string $line
179
     * @param string $viewAlias
180
     *
181
     * @return string
182
     */
183
    protected function retrieveViewFromLine(string $view, string $viewAlias): string
0 ignored issues
show
Unused Code introduced by
The parameter $viewAlias is not used and could be removed. ( Ignorable by Annotation )

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

183
    protected function retrieveViewFromLine(string $view, /** @scrutinizer ignore-unused */ string $viewAlias): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
184
    {
185
        if (strpos($view, ')') !== false) {
186
            $view = substr($view, 0, strpos($view, ')'));
187
        }
188
189
        if (($position = strpos($view, ',')) !== false) {
190
            $view = substr($view, 0, $position);
191
        }
192
193
        foreach ($this->ignoredStrings as $string) {
194
            $view = str_replace($string, '', $view);
195
        }
196
197
        return trim($view);
198
    }
199
200
    /**
201
     * @param string $view
202
     *
203
     * @return array
204
     */
205
    protected function retrieveNestedViews(string $view): array
206
    {
207
        $views = [];
208
209
        $content = $this->getViewContent($view);
210
211
        foreach ($this->bladeDirectives as $key => $bladeDirective) {
212
            $positions = $this->getPositionOfBladeDirectives($bladeDirective, $content);
213
214
            foreach ($positions as $position) {
215
                $view = $this->getViewFromLine($content, $bladeDirective);
216
217
                $views[] = $this->retrieveViewFromLine($view, $bladeDirective);
218
            }
219
        }
220
221
        return $views;
222
    }
223
224
    /**
225
     * @param string $bladeDirective
226
     * @param string $content
227
     *
228
     * @return array
229
     */
230
    protected function getPositionOfBladeDirectives(string $bladeDirective, ?string $content): array
231
    {
232
        $positions = [];
233
234
        $lastPos = 0;
235
236
        while (($lastPos = strpos($content, $bladeDirective, $lastPos)) !== false) {
237
            $positions[] = $lastPos;
238
            $lastPos = $lastPos + strlen($bladeDirective);
239
        }
240
241
        return $positions;
242
    }
243
244
    /**
245
     * @param string $view
246
     *
247
     * @return string
248
     */
249
    public function getViewContent(string $view): ?string
250
    {
251
        $filesystem = new Filesystem();
252
253
        foreach (config('view.paths') as $viewPath) {
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

253
        foreach (/** @scrutinizer ignore-call */ config('view.paths') as $viewPath) {
Loading history...
254
            $path = $viewPath.'/'.str_replace('.', '/', $view).'.blade.php';
255
256
            if (!$filesystem->exists($path)) {
257
                // View Is Missing
258
                break;
259
            }
260
261
            return $filesystem->get($path);
262
        }
263
264
        return null;
265
    }
266
267
    /**
268
     * @return array
269
     */
270
    public function getViews(): array
271
    {
272
        return collect(array_merge($this->childrenViews, $this->parent))
273
            ->unique()
274
            ->flatMap(function ($view) {
275
                return [
276
                    $view => 0,
277
                ];
278
            })->toArray();
279
    }
280
281
    /**
282
     * @return ParserActionInterface
283
     */
284
    public function getAction(): ParserActionInterface
285
    {
286
        return $this->action;
287
    }
288
289
    /**
290
     * @return array
291
     */
292
    public function getParent(): array
293
    {
294
        return $this->parent;
295
    }
296
297
    /**
298
     * @return array
299
     */
300
    public function getChildren(): array
301
    {
302
        return $this->children;
303
    }
304
}
305