laravel-artican maintained by sajjadhossainshohag
Laravel Artican
Code Health Checker for Laravel — catch broken routes, missing views, schema mismatches, and runtime errors before deployment.
Run a single artisan command to scan your entire Laravel codebase for 50+ common issues, grouped by category with severity levels.
Installation
composer require sajjadhossainshohag/laravel-artican --dev
Requirements
- PHP ^8.1
- Laravel ^10.0 | ^11.0 | ^12.0 | ^13.0
Publish the config (optional):
php artisan vendor:publish --tag=artican-config
Usage
php artisan artican:scan
Options
| Option | Description |
|---|---|
--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
# Scan everything
php artisan artican:scan
# Only check routes and views
php artisan artican:scan --only=routes,views
# Fail CI pipeline on any error or warning
php artisan artican:scan --fail-on=error,warning
# JSON output for tooling
php artisan artican:scan --json
# Skip cached results (force re-scan)
php artisan artican:scan --no-cache
# Run checks in parallel (4 workers by default)
php artisan artican:scan --parallel
# Run checks in parallel with custom worker count
php artisan artican:scan --parallel --workers=8
# Agent-readable output (auto-detected when laravel/agent-detector is installed)
php artisan artican:scan --format=agent
Cache
Results are cached (default 3600s). Clear with:
php artisan artican:cache:clear
What It Catches
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
- 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
- MissingNamedRoutesCheck — Blade templates calling
route('name')where the route is undefined, or usingurl('name')whereroute()should be used
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
- WithCountOnUndefinedRelationshipCheck —
->withCount('rel')where the relationship method doesn't exist - ValueVsFirstOnNullCheck —
->first()->propertywithout a null guard (crashes on empty result) - MissingGuardedOrFillableCheck — model has neither
$fillablenor$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
- ColumnMismatchCheck —
$fillableor$castscolumns don't exist in the actual database table - InvalidCastsCheck — model
$castsreferences invalid cast types or classes
Cache
- SessionDriverMismatchCheck — session driver is
databasebut the sessions table doesn't exist
Config
- EarlyConfigAccessCheck —
config()called inside a service provider'sregister()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
- RequestAllInCreateCheck — raw
request()->all()passed to mass-assignment methods (create(),update()), bypassing$fillableprotection
Debug
- DebugStatementLeftInCheck —
dd(),dump(),var_dump(),ray(), etc. left in PHP or Blade files
Jobs / Queue
- MissingJobClassCheck —
Job::dispatch()references a class that doesn't exist - BusChainCheck —
Bus::chain()references a job class that doesn't exist - JobHasHandleMethodCheck —
ShouldQueueclass has nohandle()method - JobDependencyResolutionCheck — job constructor has unresolvable type-hinted parameter
- JobTriesZeroCheck — job has
public $tries = 0(never retries) - FailedJobTableMissingCheck —
failed_jobstable doesn't exist
Events
- MissingListenerClassCheck — event listener class doesn't exist
- ListenerMissingHandleMethodCheck — listener has no
handle()method
Middleware
- UnregisteredMiddlewareCheck —
->middleware('alias')used but alias not registered - TerminateMethodThrowsCheck —
terminate()makes external calls without try/catch
Validation
- NonExistentRuleClassCheck — custom rule class instantiated but doesn't exist
- AuthorizeAlwaysFalseCheck — FormRequest
authorize()hardcoded toreturn false
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/storagesymlink doesn't exist
Container
- SingletonAfterFirstResolveCheck — singleton registered in
boot()instead ofregister() - InterfaceBoundToDeletedConcreteCheck — container binding references a deleted concrete class
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
- MissingPolicyClassCheck —
Gate::policy()references a policy class that doesn't exist
Livewire
- MissingLivewireComponentCheck —
<livewire:name>used but component class doesn't exist
- 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
Publish the config to customize:
php artisan vendor:publish --tag=artican-config
enabled
'enabled' => env('ARTICAN_ENABLED', true),
Set to false to disable all checks globally.
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' => [
'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' => [
'enabled' => true,
'ttl' => 3600, // seconds
'store' => env('ARTICAN_CACHE_STORE', 'file'),
],
Caches scan results per check. Set enabled to false or use --no-cache to always re-scan.
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
MIT