Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Stopwatch Laravel Package

symfony/stopwatch

Symfony Stopwatch provides lightweight code profiling: start/stop named events, record laps, and group timings into sections/phases. Useful for measuring execution time and memory across parts of an application, with simple API and Composer install.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require symfony/stopwatch
    

    No additional configuration is required—Stopwatch is dependency-free.

  2. First Use Case: Profile a controller method or service to identify bottlenecks:

    use Symfony\Component\Stopwatch\Stopwatch;
    
    public function index(Stopwatch $stopwatch)
    {
        $event = $stopwatch->start('process_order');
    
        // Simulate work
        $this->orderService->process();
    
        $event->stop();
    
        // Log or display results
        $duration = $event->getDuration();
        logger()->info("Order processing took {$duration}ms", ['event' => $event->getName()]);
    }
    
  3. Where to Look First:

    • API Reference: Focus on Stopwatch and StopwatchEvent classes.
    • Laravel Integration: Use Laravel’s Service Container to auto-resolve Stopwatch in controllers/services.
    • Debugging: Leverage dd($event) to inspect timing data (duration, memory, laps).

Implementation Patterns

Core Workflows

  1. Granular Profiling in Controllers:

    public function show(Stopwatch $stopwatch)
    {
        $stopwatch->start('fetch_user_data');
        $user = $this->userRepository->find($id);
        $stopwatch->stop('fetch_user_data');
    
        $stopwatch->start('render_view');
        return view('user.show', compact('user'));
        $stopwatch->stop('render_view');
    }
    
  2. Middleware Profiling:

    public function handle($request, Closure $next, Stopwatch $stopwatch)
    {
        $event = $stopwatch->start('auth_middleware');
        $response = $next($request);
        $event->stop();
        return $response;
    }
    
  3. CLI Command Benchmarking:

    protected function execute(InputInterface $input, OutputInterface $output, Stopwatch $stopwatch)
    {
        $event = $stopwatch->start('import_users');
        $this->importUsers();
        $event->stop();
        $output->writeln(sprintf("Import took %dms", $event->getDuration()));
    }
    
  4. Nested Sections for Phased Workflows:

    $stopwatch->openSection('checkout_flow');
    $stopwatch->start('validate_cart');
    // ... validate
    $stopwatch->stop('validate_cart');
    
    $stopwatch->start('process_payment');
    // ... process
    $stopwatch->stop('process_payment');
    $stopwatch->stopSection('checkout_flow');
    

Integration Tips

  • Laravel Debugbar: Extend Stopwatch data to Debugbar for visual profiling:
    Debugbar::addMeasure('stopwatch', $event->getDuration(), 'ms');
    
  • Logging: Store critical events in Laravel’s log:
    if ($event->getDuration() > 500) {
        logger()->warning("Slow event: {$event->getName()}", $event->toArray());
    }
    
  • Testing: Use Stopwatch in PHPUnit to assert performance:
    public function testOrderProcessing()
    {
        $stopwatch = new Stopwatch();
        $event = $stopwatch->start('process_order');
        $this->app->make(OrderService::class)->process();
        $event->stop();
        $this->assertLessThan(100, $event->getDuration());
    }
    
  • Dependency Injection: Bind Stopwatch to a custom alias for clarity:
    $this->app->bind('profiler', Stopwatch::class);
    

Gotchas and Tips

Pitfalls

  1. Memory Leaks:

    • Stopwatch events are not garbage-collected automatically if referenced. Always call stop() or let events go out of scope:
      // Bad: Holds reference indefinitely
      $event = $stopwatch->start('event');
      // ... long-running code
      // Forgot to stop!
      
    • Fix: Use try-finally or Laravel’s finally in middleware/controllers.
  2. Lap Timing Quirks:

    • Laps (lap()) do not reset the event timer. They record elapsed time since the event started:
      $stopwatch->start('event');
      $stopwatch->lap('first_step'); // Records time from start()
      sleep(1);
      $stopwatch->lap('second_step'); // Records cumulative time
      $stopwatch->stop('event');
      
    • Tip: Use laps for checkpoints, not independent timers.
  3. Section Nesting:

    • Sections must be closed in reverse order (LIFO). Nesting mismatches throw exceptions:
      $stopwatch->openSection('outer');
      $stopwatch->openSection('inner');
      // ... code
      $stopwatch->stopSection('outer'); // Throws if 'inner' is still open!
      
    • Fix: Use a stack or ensure proper nesting in IDEs (e.g., PHPStorm).
  4. PHP 8.1+ Requirement:

    • Stopwatch v8.x requires PHP 8.1+. For older Laravel versions, use ^7.0:
      composer require symfony/stopwatch:^7.0
      
    • Tip: Check composer.json constraints to avoid accidental upgrades.
  5. Event Naming Collisions:

    • Stopwatch does not validate event names. Duplicate names overwrite previous events:
      $stopwatch->start('event');
      $stopwatch->stop('event'); // First event
      $stopwatch->start('event'); // Overwrites!
      $stopwatch->stop('event'); // Second event (first lost)
      
    • Fix: Use unique names (e.g., include timestamps or class names):
      $eventName = "OrderService::process_{$order->id}";
      

Debugging Tips

  • Inspect Events:
    dd($event->toArray());
    // Outputs:
    // [
    //     'name' => 'eventName',
    //     'duration' => 123.45, // ms
    //     'memory' => 1024,     // bytes
    //     'startTime' => 1234567890,
    //     'laps' => [...],
    // ]
    
  • Memory Profiling: Enable memory tracking in the Stopwatch constructor:
    $stopwatch = new Stopwatch(true); // Enable memory
    
  • Log All Events: Create a global listener in a service provider:
    public function boot()
    {
        $this->app->resolving(Stopwatch::class, function ($stopwatch) {
            $stopwatch->getClient()->listen(function (StopwatchEvent $event) {
                logger()->debug("Stopwatch: {$event->getName()}", $event->toArray());
            });
        });
    }
    

Extension Points

  1. Custom Event Classes: Extend StopwatchEvent to add metadata:

    class CustomEvent extends StopwatchEvent
    {
        public function __construct(string $name, float $startTime, bool $memory = false)
        {
            parent::__construct($name, $startTime, $memory);
            $this->setData(['custom_field' => 'value']);
        }
    }
    

    Register with Stopwatch:

    $stopwatch = new Stopwatch();
    $stopwatch->getClient()->setEventClass(CustomEvent::class);
    
  2. Storage Backends: Persist events to a database or cache:

    $stopwatch->getClient()->listen(function (StopwatchEvent $event) {
        cache()->put("stopwatch:{$event->getName()}", $event->toArray(), now()->addHours(1));
    });
    
  3. Laravel Telescope Integration: Publish Stopwatch data to Telescope:

    Telescope::addData([$event->getName() => $event->toArray()]);
    
  4. Conditional Profiling: Disable Stopwatch in production:

    $stopwatch = app()->environment('local') ? new Stopwatch() : new class {
        public function start($name) { return $this; }
        public function stop($name) { return $this; }
    };
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport