1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Imanghafoori\LaravelMicroscope\Checks; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Illuminate\Support\Facades\View; |
7
|
|
|
use Illuminate\Support\Str; |
8
|
|
|
use Imanghafoori\LaravelMicroscope\Analyzers\FunctionCall; |
9
|
|
|
use InvalidArgumentException; |
10
|
|
|
|
11
|
|
|
class ExtractBladePartial |
12
|
|
|
{ |
13
|
|
|
public static function check($tokens, $absPath) |
14
|
|
|
{ |
15
|
|
|
// we skip the very first tokens: '<?php ' |
16
|
|
|
$i = 4; |
17
|
|
|
// we skip the very end of the file. |
18
|
|
|
$total = \count($tokens) - 3; |
19
|
|
|
$calls = []; |
20
|
|
|
$callsOrder = []; |
21
|
|
|
$partialName = ''; |
22
|
|
|
while ($i < $total) { |
23
|
|
|
$index = FunctionCall::isGlobalCall('extractBlade', $tokens, $i); |
24
|
|
|
|
25
|
|
|
if (! $index) { |
26
|
|
|
$i++; |
27
|
|
|
continue; |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
$params = FunctionCall::readParameters($tokens, $i); |
31
|
|
|
|
32
|
|
|
$partialName = $params[0][0][1] ?? $partialName; |
33
|
|
|
! \in_array($partialName, $callsOrder) && $callsOrder[] = $partialName; |
34
|
|
|
$calls[$partialName][] = ($params[0][0]) ?? ($tokens[$i - 1]); |
35
|
|
|
|
36
|
|
|
$i++; |
37
|
|
|
} |
38
|
|
|
if (! $calls) { |
|
|
|
|
39
|
|
|
return; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
$file = file($absPath); |
43
|
|
|
|
44
|
|
|
$callsOrder = array_reverse($callsOrder); |
45
|
|
|
foreach ($callsOrder as $paramName) { |
46
|
|
|
$call = $calls[$paramName]; |
47
|
|
|
if (\count($call) < 2) { |
48
|
|
|
continue; |
49
|
|
|
} |
50
|
|
|
$replacement = ['@include('.$call[0][1].')'."\n"]; |
51
|
|
|
|
52
|
|
|
$start = $call[0][2] - (1); |
53
|
|
|
$removedLinesNumber = ($call[1][2] - $call[0][2]) + 1; |
54
|
|
|
$extracted = array_splice($file, $start, $removedLinesNumber, $replacement); |
55
|
|
|
$partialPath = self::find(\trim($call[0][1], '\'\"')); |
56
|
|
|
array_shift($extracted); |
57
|
|
|
array_pop($extracted); |
58
|
|
|
|
59
|
|
|
$partialPath = \str_replace(['/', '\\'], '/', $partialPath); |
60
|
|
|
|
61
|
|
|
$spaces = Str::before($extracted[0], \trim($extracted[0])); |
62
|
|
|
// add space before the @include to have proper indentation. |
63
|
|
|
$file[$start] = $spaces.$file[$start]; |
64
|
|
|
foreach ($extracted as $i => $line) { |
65
|
|
|
// remove spaces so that the created file |
66
|
|
|
// does not have irrelevant indentation. |
67
|
|
|
$extracted[$i] = Str::after($extracted[$i], $spaces); |
68
|
|
|
} |
69
|
|
|
self::forceFilePutContents($partialPath, \implode('', $extracted)); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
self::forceFilePutContents($absPath, \implode('', $file)); |
73
|
|
|
|
74
|
|
|
return $tokens; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
public static function find($name) |
78
|
|
|
{ |
79
|
|
|
if (self::hasHintInformation($name = \trim($name))) { |
80
|
|
|
return self::findNamespacedView($name); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
return self::findInPaths($name, View::getFinder()->getPaths()); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
protected static function getPossibleViewFiles($name) |
87
|
|
|
{ |
88
|
|
|
return array_map(function ($extension) use ($name) { |
89
|
|
|
return \str_replace('.', DIRECTORY_SEPARATOR, $name).'.'.$extension; |
90
|
|
|
}, ['blade.php']); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
protected static function findNamespacedView($name) |
94
|
|
|
{ |
95
|
|
|
[$namespace, $view] = self::parseNamespaceSegments($name); |
|
|
|
|
96
|
|
|
|
97
|
|
|
$hints = View::getFinder()->getHints(); |
98
|
|
|
|
99
|
|
|
return self::findInPaths($view, $hints[$namespace]); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
protected static function parseNamespaceSegments($name) |
103
|
|
|
{ |
104
|
|
|
$segments = \explode('::', $name); |
105
|
|
|
|
106
|
|
|
if (\count($segments) !== 2) { |
107
|
|
|
throw new InvalidArgumentException("View [{$name}] has an invalid name."); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
$hints = View::getFinder()->getHints(); |
111
|
|
|
if (! isset($hints[$segments[0]])) { |
112
|
|
|
throw new InvalidArgumentException("No hint path defined for [{$segments[0]}]."); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
return $segments; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
protected static function findInPaths($name, $paths) |
119
|
|
|
{ |
120
|
|
|
foreach ((array) $paths as $path) { |
121
|
|
|
foreach (self::getPossibleViewFiles($name) as $file) { |
122
|
|
|
return $viewPath = $path.DIRECTORY_SEPARATOR.$file; |
|
|
|
|
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
public static function hasHintInformation($name) |
128
|
|
|
{ |
129
|
|
|
return strpos($name, '::') > 0; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
public static function forceFilePutContents($filepath, $message) |
133
|
|
|
{ |
134
|
|
|
try { |
135
|
|
|
$isInFolder = preg_match("/^(.*)\/([^\/]+)$/", $filepath, $filepathMatches); |
136
|
|
|
if ($isInFolder) { |
137
|
|
|
$folderName = $filepathMatches[1]; |
138
|
|
|
if (! is_dir($folderName)) { |
139
|
|
|
mkdir($folderName, 0777, true); |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
file_put_contents($filepath, $message); |
143
|
|
|
} catch (Exception $e) { |
144
|
|
|
echo "ERR: error writing '$message' to '$filepath', ".$e->getMessage(); |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.