19
19
use Illuminate \Contracts \Container \Container ;
20
20
use Illuminate \Contracts \Http \Kernel ;
21
21
use Illuminate \Database \Events \QueryExecuted ;
22
- use Illuminate \Events \Dispatcher ;
23
22
use Illuminate \Support \Collection ;
23
+ use Illuminate \Support \Facades \Event ;
24
24
use Illuminate \Support \Str ;
25
25
use function Guanguans \LaravelSoar \Support \humanly_milliseconds ;
26
26
use function Guanguans \LaravelSoar \Support \star_for ;
@@ -37,11 +37,6 @@ public function __construct(private Container $container)
37
37
self ::$ scores = collect ();
38
38
}
39
39
40
- public function isBooted (): bool
41
- {
42
- return $ this ->booted ;
43
- }
44
-
45
40
/**
46
41
* @throws BindingResolutionException
47
42
*/
@@ -52,58 +47,58 @@ public function boot(): void
52
47
}
53
48
54
49
$ this ->booted = true ;
55
- $ this ->logQuery ( $ this -> container -> make (\ Illuminate \ Contracts \ Events \Dispatcher::class) );
56
- $ this ->registerOutputMonitor ($ this -> container );
50
+ $ this ->logQueries ( );
51
+ $ this ->registerOutputMonitor ();
57
52
}
58
53
59
54
/**
60
55
* @throws \JsonException
61
56
*/
62
57
public function getScores (): Collection
63
58
{
64
- if ( self ::$ scores-> isEmpty ()) {
65
- self :: $ scores = $ this ->toScores (self :: $ queries )
59
+ return self ::$ scores = self :: $ scores -> whenEmpty (
60
+ fn () => $ this ->toScores ()
66
61
->sortBy ('Score ' )
67
- ->map (function (array $ score ): array {
68
- $ query = $ this ->matchQuery (self ::$ queries , $ score );
69
-
70
- return [
71
- 'Summary ' => \sprintf (
72
- '[%s|%d分|%s|%s] ' ,
73
- $ star = star_for ($ score ['Score ' ]),
74
- $ score ['Score ' ],
75
- $ query ['time ' ],
76
- $ query ['sql ' ]
77
- ),
78
- 'Basic ' => [
79
- 'Sample ' => $ query ['sql ' ],
80
- 'Score ' => $ score ['Score ' ],
81
- 'Star ' => $ star ,
82
- 'Time ' => $ query ['time ' ],
83
- 'Connection ' => $ query ['connection ' ],
84
- 'Driver ' => $ query ['driver ' ],
85
- 'Tables ' => (array ) $ score ['Tables ' ],
86
- ],
87
- 'HeuristicRules ' => (array ) $ score ['HeuristicRules ' ],
88
- 'IndexRules ' => (array ) $ score ['IndexRules ' ],
89
- 'Explain ' => $ this ->sanitizeExplain ((array ) $ score ['Explain ' ]),
90
- 'Backtraces ' => $ query ['backtraces ' ],
91
- ];
92
- })
93
- ->values ();
94
- }
62
+ ->map (fn (array $ score ): array => $ this ->hydrateScore ($ score ))
63
+ ->values ()
64
+ );
65
+ }
95
66
96
- return self ::$ scores ;
67
+ private function hydrateScore (array $ score ): array
68
+ {
69
+ $ query = $ this ->matchQuery ($ score );
70
+
71
+ return [
72
+ 'Summary ' => \sprintf (
73
+ '[%s|%d分|%s|%s] ' ,
74
+ $ star = star_for ($ score ['Score ' ]),
75
+ $ score ['Score ' ],
76
+ $ query ['time ' ],
77
+ $ query ['sql ' ]
78
+ ),
79
+ 'Basic ' => [
80
+ 'Sample ' => $ query ['sql ' ],
81
+ 'Score ' => $ score ['Score ' ],
82
+ 'Star ' => $ star ,
83
+ 'Time ' => $ query ['time ' ],
84
+ 'Connection ' => $ query ['connection ' ],
85
+ 'Driver ' => $ query ['driver ' ],
86
+ 'Tables ' => (array ) $ score ['Tables ' ],
87
+ ],
88
+ 'HeuristicRules ' => (array ) $ score ['HeuristicRules ' ],
89
+ 'IndexRules ' => (array ) $ score ['IndexRules ' ],
90
+ 'Explain ' => $ this ->sanitizeExplain ((array ) $ score ['Explain ' ]),
91
+ 'Backtraces ' => $ query ['backtraces ' ],
92
+ ];
97
93
}
98
94
99
- private function logQuery ( Dispatcher $ dispatcher ): void
95
+ private function logQueries ( ): void
100
96
{
101
- // 记录 SQL
102
- $ dispatcher ->listen (QueryExecuted::class, function (QueryExecuted $ queryExecuted ): void {
97
+ Event::listen (QueryExecuted::class, function (QueryExecuted $ queryExecuted ): void {
103
98
if (
104
99
self ::$ queries ->has ($ queryExecuted ->sql )
105
100
|| $ this ->isExceptSql ($ queryExecuted ->sql )
106
- || $ this ->isExceptSql ($ sql = $ this ->toSql ($ queryExecuted ))
101
+ || $ this ->isExceptSql ($ sql = $ this ->toRawSql ($ queryExecuted ))
107
102
) {
108
103
return ;
109
104
}
@@ -126,20 +121,25 @@ private function isExceptSql(string $sql): bool
126
121
/**
127
122
* @noinspection DebugFunctionUsageInspection
128
123
*/
129
- private function toSql (QueryExecuted $ queryExecuted ): string
124
+ private function toRawSql (QueryExecuted $ queryExecuted ): string
130
125
{
126
+ if (method_exists ($ queryExecuted , 'toRawSql ' )) {
127
+ return $ queryExecuted ->toRawSql ();
128
+ }
129
+
131
130
if ([] === $ queryExecuted ->bindings ) {
132
131
return $ queryExecuted ->sql ;
133
132
}
134
133
135
- $ sqlWithPlaceholders = str_replace (['% ' , '? ' , '%s%s ' ], ['%% ' , '%s ' , '? ' ], $ queryExecuted ->sql );
136
- $ bindings = $ queryExecuted ->connection ->prepareBindings ($ queryExecuted ->bindings );
137
- $ pdo = $ queryExecuted ->connection ->getPdo ();
138
-
139
- return vsprintf ($ sqlWithPlaceholders , array_map (
140
- static fn (mixed $ binding ): string => \is_string ($ binding ) ? $ pdo ->quote ($ binding ) : var_export ($ binding , true ),
141
- $ bindings
142
- ));
134
+ return vsprintf (
135
+ str_replace (['% ' , '? ' , '%s%s ' ], ['%% ' , '%s ' , '? ' ], $ queryExecuted ->sql ),
136
+ array_map (
137
+ static fn (mixed $ binding ): string => \is_string ($ binding )
138
+ ? $ queryExecuted ->connection ->getPdo ()->quote ($ binding )
139
+ : var_export ($ binding , true ),
140
+ $ queryExecuted ->connection ->prepareBindings ($ queryExecuted ->bindings )
141
+ )
142
+ );
143
143
}
144
144
145
145
/**
@@ -166,28 +166,26 @@ private function getBacktraces(int $limit = 0, int $forgetLines = 0): array
166
166
/**
167
167
* @throws \Illuminate\Contracts\Container\BindingResolutionException
168
168
*/
169
- private function registerOutputMonitor (Container $ container ): void
169
+ private function registerOutputMonitor (): void
170
170
{
171
- // 注册输出监听
172
- $ container ->make (\Illuminate \Contracts \Events \Dispatcher::class)->listen (
171
+ Event::listen (
173
172
CommandFinished::class,
174
- fn (CommandFinished $ commandFinished ) => $ container ->make (OutputManager::class)->output (
173
+ fn (CommandFinished $ commandFinished ) => $ this -> container ->make (OutputManager::class)->output (
175
174
$ this ->getScores (),
176
175
$ commandFinished
177
176
)
178
177
);
179
178
180
- // 注册输出中间件
181
- $ container ->make (Kernel::class)->pushMiddleware (OutputScoresMiddleware::class);
179
+ $ this ->container ->make (Kernel::class)->prependMiddleware (OutputScoresMiddleware::class);
182
180
}
183
181
184
182
/**
185
183
* @throws \JsonException
186
184
*/
187
- private function toScores (Collection $ queries ): Collection
185
+ private function toScores (): Collection
188
186
{
189
- return $ queries
190
- ->map ( static fn ( array $ query ): string => $ query [ 'sql ' ] )
187
+ return self :: $ queries
188
+ ->pluck ( 'sql ' )
191
189
->pipe (static fn (Collection $ sqls ): Collection => collect (resolve (Soar::class)->arrayScores ($ sqls ->all ())));
192
190
}
193
191
@@ -200,32 +198,27 @@ private function toScores(Collection $queries): Collection
200
198
* backtraces: array<string>
201
199
* }
202
200
*/
203
- private function matchQuery (Collection $ queries , array $ score ): array
201
+ private function matchQuery (array $ score ): array
204
202
{
205
- $ query = $ queries ->first (static fn ( array $ query ): bool => $ score [ ' Sample ' ] === $ query [ ' sql ' ]);
206
-
207
- if ( $ query ) {
208
- return $ query;
209
- }
203
+ return self :: $ queries ->first (
204
+ static fn ( array $ query ): bool => $ score [ ' Sample ' ] === $ query [ ' sql ' ],
205
+ static fn (): array => self :: $ queries
206
+ -> map ( static function ( array $ query) use ( $ score ): array {
207
+ $ query [ ' similarity ' ] = similar_text ( $ score [ ' Sample ' ], $ query [ ' sql ' ]);
210
208
211
- // @codeCoverageIgnoreStart
212
- return $ queries
213
- ->map (static function (array $ query ) use ($ score ): array {
214
- $ query ['similarity ' ] = similar_text ($ score ['Sample ' ], $ query ['sql ' ]);
215
-
216
- return $ query ;
217
- })
218
- ->sortByDesc ('similarity ' )
219
- ->first ();
220
- // @codeCoverageIgnoreEnd
209
+ return $ query ;
210
+ })
211
+ ->sortByDesc ('similarity ' )
212
+ ->first ()
213
+ );
221
214
}
222
215
223
216
private function sanitizeExplain (array $ explain ): array
224
217
{
225
218
return collect ($ explain )
226
219
->map (static function (array $ explain ): array {
227
- $ explain ['Content ' ] = collect ( explode (\ PHP_EOL , $ explain ['Content ' ]))->filter ()->values ()->all ();
228
- $ explain ['Case ' ] = collect ( explode (\ PHP_EOL , $ explain ['Case ' ]))->filter ()->values ()->all ();
220
+ $ explain ['Content ' ] = str ( $ explain ['Content ' ])-> explode (\ PHP_EOL )->filter ()->values ()->all ();
221
+ $ explain ['Case ' ] = str ( $ explain ['Case ' ])-> explode (\ PHP_EOL )->filter ()->values ()->all ();
229
222
230
223
return $ explain ;
231
224
})
0 commit comments