PHPackages                             sajjadhossainshohag/laravel-doctor - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. sajjadhossainshohag/laravel-doctor

ActiveLibrary

sajjadhossainshohag/laravel-doctor
==================================

Code Health Checker for Laravel — catch broken routes, missing views, schema mismatches, and runtime errors before deployment.

v0.3.1(1mo ago)7173MITPHPPHP ^8.1CI passing

Since Jul 14Pushed 1mo agoCompare

[ Source](https://github.com/sajjadhossainshohag/laravel-doctor)[ Packagist](https://packagist.org/packages/sajjadhossainshohag/laravel-doctor)[ RSS](/packages/sajjadhossainshohag-laravel-doctor/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (5)Dependencies (22)Versions (9)Used By (0)

 [![Laravel Doctor](art/laravel-doctor-banner.png)](art/laravel-doctor-banner.png)

Laravel Doctor
==============

[](#laravel-doctor)

**Code Health Checker for Laravel** — catch broken routes, missing views, schema mismatches, and runtime errors before deployment.

> **Beta** — under active development. Things may change.

Run a single artisan command to scan your entire Laravel codebase for 50+ common issues, grouped by category with severity levels.

---

Installation
------------

[](#installation)

```
composer require sajjadhossainshohag/laravel-doctor --dev
```

Requirements
------------

[](#requirements)

- PHP ^8.1
- Laravel ^10.0 | ^11.0 | ^12.0 | ^13.0

Publish the config (optional):

```
php artisan vendor:publish --tag=doctor-config
```

Usage
-----

[](#usage)

```
php artisan doctor:scan
```

### Options

[](#options)

OptionDescription`--only=routes,views,env`Comma-separated categories to scan`--json`Output as JSON`--html`Output as HTML`--fail-on=error,warning`Exit code 1 if issues at these severities exist`--no-cache`Skip cached results`--parallel`Distribute checks across parallel subprocesses`--workers=N`Number of parallel workers (auto-detected from CPU by default)`--format=agent`Machine-readable JSON for AI agents (auto-detected in OpenCode/Claude Code when `laravel/agent-detector` is installed)`--help`Display help### Examples

[](#examples)

```
# Scan everything
php artisan doctor:scan

# Only check routes and views
php artisan doctor:scan --only=routes,views

# Fail CI pipeline on any error or warning
php artisan doctor:scan --fail-on=error,warning

# JSON output for tooling
php artisan doctor:scan --json

# Skip cached results (force re-scan)
php artisan doctor:scan --no-cache

# Run checks in parallel (4 workers by default)
php artisan doctor:scan --parallel

# Run checks in parallel with custom worker count
php artisan doctor:scan --parallel --workers=8

# Agent-readable output (auto-detected when laravel/agent-detector is installed)
php artisan doctor:scan --format=agent
```

### Cache

[](#cache)

Results are cached (default 3600s). Clear with:

```
php artisan doctor:cache:clear
```

---

What It Catches
---------------

[](#what-it-catches)

### Routes

[](#routes)

- **MissingControllerCheck** — route references a controller class that doesn't exist
- **MissingControllerMethodCheck** — route references a method that doesn't exist on the controller
- **DuplicateRouteNamesCheck** — two or more routes share the same `->name()`
- **DuplicateUrisCheck** — two or more routes share the same URI + HTTP method
- **InvalidMiddlewareCheck** — route references middleware that is not registered
- **RouteClosureBreaksCacheCheck** — route uses closures instead of controller strings, preventing `php artisan route:cache`

### Views

[](#views)

- **MissingIncludeCheck** — `@include('view')` references a view that doesn't exist
- **MissingExtendsCheck** — `@extends('layout')` references a layout that doesn't exist
- **MissingComponentCheck** — `@component('name')` references a component that doesn't exist
- **StackPushMismatchCheck** — `@push('name')` exists but no corresponding `@stack('name')`

### Blade

[](#blade)

- **MissingNamedRoutesCheck** — Blade templates calling `route('name')` where the route is undefined, or using `url('name')` where `route()` should be used

### Components

[](#components)

- **ComponentClassCheck** — Blade component alias references a class that doesn't exist
- **ComponentNamespaceCheck** — view namespace maps to a non-existent directory
- **AnonymousComponentCheck** — anonymous component namespace maps to a non-existent directory

### Eloquent / Models

[](#eloquent--models)

- **WithCountOnUndefinedRelationshipCheck** — `->withCount('rel')` where the relationship method doesn't exist
- **ValueVsFirstOnNullCheck** — `->first()->property` without a null guard (crashes on empty result)
- **MissingGuardedOrFillableCheck** — model has neither `$fillable` nor `$guarded` (mass-assignment unprotected)
- **AccessorMutatorStyleConflictCheck** — model mixes old-style accessors with new `Attribute::make()` pattern
- **GetThenCountCheck** — `->get()` or `->all()` followed by `->count()` instead of a single `->count()` query

### Schema / Database

[](#schema--database)

- **ColumnMismatchCheck** — `$fillable` or `$casts` columns don't exist in the actual database table
- **InvalidCastsCheck** — model `$casts` references invalid cast types or classes

### Cache

[](#cache-1)

- **SessionDriverMismatchCheck** — session driver is `database` but the sessions table doesn't exist

### Config

[](#config)

- **EarlyConfigAccessCheck** — `config()` called inside a service provider's `register()` method
- **AbortIfWrongHttpCodeCheck** — `abort_if()` / `abort_unless()` called with a code below 400
- **NonExistentConfigFileCheck** — `config('file.key')` references a config file that doesn't exist
- **NonExistentConfigKeyCheck** — `config('file.key')` references a config key that doesn't exist

### Security

[](#security)

- **RequestAllInCreateCheck** — raw `request()->all()` passed to mass-assignment methods (`create()`, `update()`), bypassing `$fillable` protection

### Debug

[](#debug)

- **DebugStatementLeftInCheck** — `dd()`, `dump()`, `var_dump()`, `ray()`, etc. left in PHP or Blade files

### Jobs / Queue

[](#jobs--queue)

- **MissingJobClassCheck** — `Job::dispatch()` references a class that doesn't exist
- **BusChainCheck** — `Bus::chain()` references a job class that doesn't exist
- **JobHasHandleMethodCheck** — `ShouldQueue` class has no `handle()` method
- **JobDependencyResolutionCheck** — job constructor has unresolvable type-hinted parameter
- **JobTriesZeroCheck** — job has `public $tries = 0` (never retries)
- **FailedJobTableMissingCheck** — `failed_jobs` table doesn't exist

### Events

[](#events)

- **MissingListenerClassCheck** — event listener class doesn't exist
- **ListenerMissingHandleMethodCheck** — listener has no `handle()` method

### Middleware

[](#middleware)

- **UnregisteredMiddlewareCheck** — `->middleware('alias')` used but alias not registered
- **TerminateMethodThrowsCheck** — `terminate()` makes external calls without try/catch

### Validation

[](#validation)

- **NonExistentRuleClassCheck** — custom rule class instantiated but doesn't exist
- **AuthorizeAlwaysFalseCheck** — FormRequest `authorize()` hardcoded to `return false`

### Storage

[](#storage)

- **UndefinedDiskCheck** — `Storage::disk('name')` references an undefined disk
- **StoreAsPathTraversalCheck** — `->storeAs()` path contains `..` (path traversal risk)
- **S3UrlWithoutConfigCheck** — S3 `url()` called without full configuration
- **MissingStorageSymlinkCheck** — `public/storage` symlink doesn't exist

### Container

[](#container)

- **SingletonAfterFirstResolveCheck** — singleton registered in `boot()` instead of `register()`
- **InterfaceBoundToDeletedConcreteCheck** — container binding references a deleted concrete class

### Schedule

[](#schedule)

- **ScheduledCommandNotExistsCheck** — `$schedule->command(Class::class)` references a non-existent class
- **DeletedScheduledCommandCheck** — `$schedule->command('name')` references an unregistered command
- **OverlappingJobsWithoutLockCheck** — frequent task doesn't use `->withoutOverlapping()`

### Gates

[](#gates)

- **MissingPolicyClassCheck** — `Gate::policy()` references a policy class that doesn't exist

### Livewire

[](#livewire)

- **MissingLivewireComponentCheck** — `` used but component class doesn't exist

### Mail

[](#mail)

- **MailableMissingViewCheck** — mailable's `->view('name')` references a view that doesn't exist
- **MailableVariableMismatchCheck** — mailable passes variables to a template that doesn't use them

---

Configuration
-------------

[](#configuration)

Publish the config to customize:

```
php artisan vendor:publish --tag=doctor-config
```

### `enabled`

[](#enabled)

```
'enabled' => env('DOCTOR_ENABLED', true),
```

Set to `false` to disable all checks globally.

### `scan_paths`

[](#scan_paths)

```
'scan_paths' => [
    app_path(),
    resource_path('views'),
],
```

Directories scanned for PHP/Blade files. Add paths for custom namespaces or package directories.

### `ignore`

[](#ignore)

```
'ignore' => [
    'routes'     => ['telescope.*', 'debugbar.*', 'horizon.*'],
    'views'      => ['vendor/*'],
    'components' => ['vendor/*'],
    'eloquent'   => ['vendor/*', 'migrations/*'],
    'container'  => ['vendor/*'],
    'events'     => ['vendor/*'],
    'mail'       => ['vendor/*'],
    'middleware' => ['vendor/*'],
    'validation' => ['vendor/*'],
    'storage'    => ['vendor/*'],
    'cache'      => ['vendor/*'],
    'schedule'   => ['vendor/*'],
    'gates'      => ['vendor/*'],
    'livewire'   => ['vendor/*'],
    'config'     => ['vendor/*'],
    'security'   => ['vendor/*'],
    'debug'      => ['vendor/*'],
],
```

Glob patterns per category to skip noisy files (e.g. Telescope, Debugbar, Horizon routes, vendor views).

### `cache`

[](#cache-2)

```
'cache' => [
    'enabled' => true,
    'ttl'     => 3600,           // seconds
    'store'   => env('DOCTOR_CACHE_STORE', 'file'),
],
```

Caches scan results per check. Set `enabled` to `false` or use `--no-cache` to always re-scan.

### `health_score`

[](#health_score)

```
'health_score' => [
    'weights' => [
        'schema'     => 12,
        'eloquent'   => 12,
        'routes'     => 10,
        'views'      => 8,
        'components' => 5,
        'jobs'       => 5,
        'cache'      => 5,
        'storage'    => 5,
        'validation' => 5,
        'container'  => 5,
        'events'     => 4,
        'mail'       => 4,
        'middleware' => 4,
        'schedule'   => 4,
        'gates'      => 3,
        'livewire'   => 3,
        'config'     => 2,
        'debug'      => 3,
        'security'   => 10,
    ],
],
```

Relative weight of each category for calculating an overall code health score.

---

License
-------

[](#license)

MIT

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance93

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

7

Last Release

35d ago

PHP version history (2 changes)v0.1.0PHP ^8.2

v0.2.0PHP ^8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/63788037?v=4)[Sajjad Hossain Shohag](/maintainers/sajjadhossainshohag)[@sajjadhossainshohag](https://github.com/sajjadhossainshohag)

---

Top Contributors

[![sajjadhossainshohag](https://avatars.githubusercontent.com/u/63788037?v=4)](https://github.com/sajjadhossainshohag "sajjadhossainshohag (69 commits)")

---

Tags

laravellaravel-doctorlaravelstatic analysiscode qualityhealth check

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/sajjadhossainshohag-laravel-doctor/health.svg)

```
[![Health](https://phpackages.com/badges/sajjadhossainshohag-laravel-doctor/health.svg)](https://phpackages.com/packages/sajjadhossainshohag-laravel-doctor)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3365.5M359](/packages/psalm-plugin-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k17.6M165](/packages/laravel-pulse)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k31.8M166](/packages/laravel-cashier)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M270](/packages/laravel-mcp)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

1.0k2.5M152](/packages/roots-acorn)[api-platform/laravel

API Platform support for Laravel

58190.1k22](/packages/api-platform-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
