22
33namespace Livewire \Blaze ;
44
5+ use Illuminate \Support \Facades \File ;
6+
57class Debugger
68{
79 protected ?int $ renderStart = null ;
@@ -22,13 +24,20 @@ class Debugger
2224
2325 protected bool $ isColdRender = false ;
2426
27+ protected bool $ timerInjected = false ;
28+
2529 // ── Profiler trace ───────────────────────────
2630 protected array $ traceStack = [];
2731 protected array $ traceEntries = [];
2832 protected ?float $ traceOrigin = null ;
2933 protected int $ memoHits = 0 ;
3034 protected array $ memoHitNames = [];
3135
36+ public function __construct (
37+ protected BladeService $ blade ,
38+ ) {
39+ }
40+
3241 /**
3342 * Extract a human-readable component name from its file path.
3443 *
@@ -188,6 +197,80 @@ public function setTimerView(string $name): void
188197 $ this ->timerView = $ name ;
189198 }
190199
200+ /**
201+ * Inject start/stop timer calls into the compiled file of the first
202+ * view being rendered. This ensures we measure only view rendering
203+ * time, not the full request lifecycle.
204+ *
205+ * Called from the view composer on each composing view; only the
206+ * first successful injection per request takes effect.
207+ */
208+ public function injectRenderTimer (\Illuminate \View \View $ view ): void
209+ {
210+ if ($ this ->timerInjected ) {
211+ return ;
212+ }
213+
214+ $ path = $ view ->getPath ();
215+
216+ // Some views (e.g. Livewire virtual views) may not have a real path.
217+ if (! $ path || ! file_exists ($ path )) {
218+ return ;
219+ }
220+
221+ // Ensure the view is compiled.
222+ if ($ this ->blade ->compiler ->isExpired ($ path )) {
223+ $ this ->blade ->compiler ->compile ($ path );
224+ }
225+
226+ $ compiledPath = $ this ->blade ->compiler ->getCompiledPath ($ path );
227+
228+ if (! file_exists ($ compiledPath )) {
229+ return ;
230+ }
231+
232+ $ compiled = file_get_contents ($ compiledPath );
233+
234+ // Record which view was wrapped with the render timer.
235+ $ this ->setTimerView ($ this ->resolveTimerViewName ($ view ));
236+
237+ $ this ->timerInjected = true ;
238+
239+ // Already injected (persisted from a previous request).
240+ if (str_contains ($ compiled , '__blaze_timer ' )) {
241+ return ;
242+ }
243+
244+ $ start = '<?php $__blaze->debugger->startRenderTimer(); /* __blaze_timer */ ?> ' ;
245+ $ stop = '<?php $__blaze->debugger->stopRenderTimer(); ?> ' ;
246+
247+ File::replace ($ compiledPath , $ start . $ compiled . $ stop );
248+ }
249+
250+ /**
251+ * Resolve a human-readable name for the view being timed.
252+ *
253+ * For Livewire SFCs the view path points to an extracted blade file
254+ * (e.g. storage/.../livewire/views/6ea59dbe.blade.php) which isn't
255+ * meaningful. In that case we pull the component name from Livewire's
256+ * shared view data instead.
257+ */
258+ protected function resolveTimerViewName (\Illuminate \View \View $ view ): string
259+ {
260+ $ path = $ view ->getPath ();
261+
262+ // Livewire SFC extracted views live inside a "livewire/views" cache directory.
263+ if ($ path && str_contains ($ path , '/livewire/views/ ' )) {
264+ $ livewire = app ('view ' )->shared ('__livewire ' );
265+
266+ if ($ livewire && method_exists ($ livewire , 'getName ' )) {
267+ return $ livewire ->getName ();
268+ }
269+ }
270+
271+ return $ view ->name ();
272+ }
273+
191274 public function startRenderTimer (): void
192275 {
193276 $ this ->renderStart = hrtime (true );
@@ -296,6 +379,25 @@ protected function getData(): array
296379 'timerView ' => $ this ->timerView ,
297380 ];
298381 }
382+
383+ public function flushState (): void
384+ {
385+ $ this ->renderStart = null ;
386+ $ this ->renderTime = 0.0 ;
387+ $ this ->timerView = null ;
388+ $ this ->components = [];
389+ $ this ->bladeComponentCount = 0 ;
390+ $ this ->bladeComponents = [];
391+ $ this ->blazeEnabled = false ;
392+ $ this ->comparison = null ;
393+ $ this ->isColdRender = false ;
394+ $ this ->timerInjected = false ;
395+ $ this ->traceStack = [];
396+ $ this ->traceEntries = [];
397+ $ this ->traceOrigin = null ;
398+ $ this ->memoHits = 0 ;
399+ $ this ->memoHitNames = [];
400+ }
299401
300402 protected function formatMs (float $ value ): string
301403 {
0 commit comments