<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Idrees Saifuddin</title>
    <description>The latest articles on DEV Community by Idrees Saifuddin (@idrees_saifuddin).</description>
    <link>https://dev.to/idrees_saifuddin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4230500%2F76dfb972-68e5-4a40-ada2-3ceb5f284fca.png</url>
      <title>DEV Community: Idrees Saifuddin</title>
      <link>https://dev.to/idrees_saifuddin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/idrees_saifuddin"/>
    <language>en</language>
    <item>
      <title>How I built a request tracer for Laravel without a PHP extension</title>
      <dc:creator>Idrees Saifuddin</dc:creator>
      <pubDate>Fri, 06 Nov 2026 13:22:40 +0000</pubDate>
      <link>https://dev.to/idrees_saifuddin/how-i-built-a-request-tracer-for-laravel-without-a-php-extension-40m5</link>
      <guid>https://dev.to/idrees_saifuddin/how-i-built-a-request-tracer-for-laravel-without-a-php-extension-40m5</guid>
      <description>&lt;p&gt;When a page in a Laravel app is slow, the usual tools tell you &lt;em&gt;that&lt;/em&gt; it was slow. I wanted to know &lt;em&gt;which function&lt;/em&gt; and &lt;em&gt;which query&lt;/em&gt; made it slow, and &lt;em&gt;which line of my code&lt;/em&gt; ran that query. So I built a tool for it: &lt;strong&gt;BlastRadius&lt;/strong&gt;, my first open-source package.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4zi97rtphahs3htv6dhd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4zi97rtphahs3htv6dhd.gif" alt="BlastRadius overview" width="799" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;You install it, open &lt;code&gt;/blast-radius&lt;/code&gt; in your browser, and use your app normally. For each page you open, it shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the API calls the page made&lt;/li&gt;
&lt;li&gt;a timeline of your own functions and database queries, with time and rows returned&lt;/li&gt;
&lt;li&gt;which query failed, and the line of your code that ran it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff997oq2p6kqwqdvggz04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff997oq2p6kqwqdvggz04.png" alt="Live requests" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When something fails, the page turns red and shows the exception, the failing query and where it was called from:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh50v5aup9mcl3toee8il.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh50v5aup9mcl3toee8il.png" alt="A failed request" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also has a &lt;strong&gt;Code flow&lt;/strong&gt; tab that draws how your routes, jobs and events reach your functions, and a command that looks at a git change and lists the routes it could affect and the tests to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interesting part: tracing without a PHP extension
&lt;/h2&gt;

&lt;p&gt;Profilers like Xdebug or Blackfire hook into PHP itself, which means installing an extension. I wanted &lt;code&gt;composer require --dev&lt;/code&gt; and nothing else, so BlastRadius does three things in plain PHP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. It rewrites your methods as they load.&lt;/strong&gt; A small autoloader runs before Composer's. For classes in your &lt;code&gt;app&lt;/code&gt; folder it loads a rewritten copy where every method reports to a tracer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// before&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* your code */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// after (all on the same line, so line numbers don't move)&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$__br&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tracer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Payments::pay'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* your code */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Throwable&lt;/span&gt; &lt;span class="nv"&gt;$__be&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Tracer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$__br&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$__be&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nv"&gt;$__be&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Tracer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$__br&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rewriting is done on the token level and keeps every line where it was, so stack traces and error messages still point at the right place. The copies are saved by a hash of the file's contents, so editing a file simply creates a new copy. If a rewrite ever produces invalid PHP, the loader falls back to your original file, so the worst case is "not timed", not "broken page".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. It wraps the database connection.&lt;/strong&gt; Laravel lets you register your own connection class per driver. BlastRadius registers a thin subclass that counts the rows each query returned or changed, and records failures, so you see "this query returned 27,313 rows in 293 ms" next to the function that called it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It records each request in a middleware.&lt;/strong&gt; When the request ends, the function timeline, the queries and any exception are saved in a small SQLite file (the last 300 requests). The page then groups them by the page that made them, using the &lt;code&gt;Referer&lt;/code&gt; header.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "what can this break" half
&lt;/h2&gt;

&lt;p&gt;For a git diff, BlastRadius parses your code with &lt;a href="https://github.com/nikic/PHP-Parser" rel="noopener noreferrer"&gt;php-parser&lt;/a&gt;, builds a call graph, and follows callers back to routes, jobs and commands. Every link has a confidence: &lt;strong&gt;CONFIRMED&lt;/strong&gt; (the code says so), &lt;strong&gt;POSSIBLE&lt;/strong&gt; (a guess from names) or &lt;strong&gt;UNRESOLVED&lt;/strong&gt; (dynamic calls, reported instead of silently dropped). A path is only as certain as its weakest link.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can't do (yet)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's a &lt;strong&gt;development tool&lt;/strong&gt;. It stays off in production unless you turn it on on purpose.&lt;/li&gt;
&lt;li&gt;It times your own code, not vendor packages.&lt;/li&gt;
&lt;li&gt;It records how many values a query bound, not the values themselves.&lt;/li&gt;
&lt;li&gt;Dynamic calls (&lt;code&gt;$class::$method()&lt;/code&gt;) show up as unresolved, not guessed.&lt;/li&gt;
&lt;li&gt;It's an early release (0.1.x), tested on Laravel 8 to 12 and PHP 8.1 to 8.4. I've used it on a few apps, so expect rough edges.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; idrees-saifuddin/blast-radius
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;http://your-app.test/blast-radius&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Source and docs: &lt;a href="https://github.com/Idrees-Saifuddin/blast-radius" rel="noopener noreferrer"&gt;https://github.com/Idrees-Saifuddin/blast-radius&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, I'd really like to know what breaks and what's missing. Open an issue or leave a comment here.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
