PHPackages                             glhd/laralint - 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. [Framework](/categories/framework)
4. /
5. glhd/laralint

ActiveLibrary[Framework](/categories/framework)

glhd/laralint
=============

Laravel linting framework

0.2.0(2mo ago)830.0k↓50.9%2[1 PRs](https://github.com/glhd/laralint/pulls)MITPHPPHP &gt;=8.3.0CI passing

Since Jan 5Pushed 3w ago1 watchersCompare

[ Source](https://github.com/glhd/laralint)[ Packagist](https://packagist.org/packages/glhd/laralint)[ RSS](/packages/glhd-laralint/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (10)Dependencies (12)Versions (33)Used By (0)

LaraLint
========

[](#laralint)

[![GitHub Workflow Status](https://github.com/glhd/laralint/workflows/Tests/badge.svg)](https://github.com/glhd/laralint/actions?query=workflow%3ATests)

This is a **very early** work-in-progress linter for Laravel projects. It’s different from other PHP linters in that it focuses on building custom rules for your specific needs.

The goal is to make **writing custom linters** easy and fluent, just like writing Laravel-backed code. While the project uses an AST/tokenizers/etc under-the-hood, most common use-cases shouldn’t need a deep understanding of how that works.

Getting Started
---------------

[](#getting-started)

Install this into your project with:

```
composer require glhd/laralint --dev
```

Once installed, you can lint your project using LaraLint’s default rules by running:

```
php artisan laralint:lint
```

If you only want to lint uncommitted Git changes, you can pass the `--diff`flat to the command

```
php artisan laralint:lint --diff
```

Or, ff you only want to lint specific files, pass their filenames as the first argument:

```
php artisan laralint:lint app/Http/Controllers/HomeController.php
```

Configuration and Presets
-------------------------

[](#configuration-and-presets)

LaraLint comes with a **very opinionated** preset installed. If this works for you, great, but you’ll probably want to customize things to match your team’s code standards.

To start, publish the LaraLint config file:

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

This will install a `laralint.php` file into your project’s `config/`directory with the default LaraLint config.

In some cases, you may be able to simply tweak the config to meet your needs. But it’s more likely that you’ll want to create your own preset.

Take a look at the [Preset](src/Contracts/Preset.php) contract, or the default [LaraLint Preset](src/Presets/LaraLint.php) to get started. From there you can create a custom preset for your project that uses linters that make sense for your team. Mix and match the pre-built linters with your own [custom linters](#custom-linters) to your heart’s content!

Custom Linters
--------------

[](#custom-linters)

If you’re interested in LaraLint, you’re probably interested in custom linters. Each team’s needs are different, and LaraLint tries to make it as easy as possible to quickly add logic that enforces your agreed upon conventions.

That said, LaraLint *does* rely on concepts like Abstract Syntax Trees, which can be intimidating at first. But with a few key concepts under your belt, you should be traversing that tree like a champ in no time!

### Real Quick: How LaraLint Works

[](#real-quick-how-laralint-works)

In as few words as possible:

LaraLint uses [Microsoft’s Tolerant PHP Parser](https://github.com/microsoft/tolerant-php-parser)to parse your PHP code into an Abstract Syntax Tree, or AST. We use Microsoft’s parser because it’s fast and works well with partially-written code (like code you’re typing in your IDE). It’s also the basis for VS Code’s IntelliSense, so it’s got a good track record.

Think of this tree as a structured view of your code. Given the following PHP code:

```
class Foo
{
  public function bar()
  {
    return 'baz';
  }
}
```

The AST will look something like this (simplified for clarity’s sake):

- ClassDeclaration (`class Foo`)
    - MethodDeclaration (`public function bar()`)
        - CompoundStatementNode (`return 'baz';`)
            - ReturnStatement (`return`)
                - StringLiteral (`'baz'`)

LaraLint walks that tree from top to bottom, and passes each node to each applicable Linter for inspection. Linters are simply objects that receive AST nodes and optionally return linting results once the tree is fully walked.

This means you can write incredibly complex and custom linters, but hopefully you won't have to.

### How to Write a LaraLint Linter

[](#how-to-write-a-laralint-linter)

The core of most LaraLint Linters is a `Matcher` object. These objects are designed to easily match a general AST “shape” and flag part of the code if the entire “shape” is found. for example, if you wanted to flag any method named `bar` that returns the string `'baz'`, you could use the following matcher:

```
(new TreeMatcher())
    // (1) Find a method declaration where the method name is "bar"
    ->withChild(function(MethodDeclaration $node) {
        return 'bar' === $node->getName();
    })
    // (2) Find any return statement
    ->withChild(ReturnStatement::class)
    // (3) Find a string literal that matches the string "baz"
    ->withChild(function(StringLiteral $node) {
        return 'baz' === $node->getStringContentsText();
    })
    ->onMatch(function(Collection $all_matched_nodes) {
        // Create a linting result using the matched nodes.
        // LaraLint will automatically map the AST nodes to line
        // numbers when printing the results.
    });
```

As soon as LaraLint finds a node that matches the first rule, it will begin looking for a child that matches the second rule. If it finds a child that matches the second rule, it will move on to the third rule. When it's matched all the rules, the matcher will trigger the `onMatch`callback, where you can perform any additional logic you choose.

LaraLint comes with several “strategies” that apply to common use-cases. These strategies abstract away even more of the logic, and are the best place to start. Look at some of the existing linters to understand how best to use each strategy.

### But Wait… How Do I Write a LaraLint Linter?

[](#but-wait-how-do-i-write-a-laralint-linter)

OK, you’re probably looking at `ReturnStatement` and `StringLiteral` and thinking, “I don’t speak Abstract Syntax Tree.”

Neither does anyone else.

That's where the `laralint:dump` command comes into play. Say you’re trying to write the silly bar/baz linter from the example above. Simply create a PHP file that *should* fail, and dump its tree:

```
php artisan laralint:dump barbaz_source.php
```

Which will output something like:

```
┏━━ ClassDeclaration ━━━━━━┓
┃                          ┃
┃ class Foo                ┃
┃ {                        ┃
┃   public function bar()  ┃
┃   {                      ┃
┃     return 'baz';        ┃
┃   }                      ┃
┃ }                        ┃
┃                          ┃
┃   ┏━━ ClassMembersNode ━━━━━━┓
┃   ┃                          ┃
┃   ┃ {                        ┃
┃   ┃   public function bar()  ┃
┃   ┃   {                      ┃
┃   ┃     return 'baz';        ┃
┃   ┃   }                      ┃
┃   ┃ }                        ┃
┃   ┃                          ┃
┃   ┃   ┏━━ MethodDeclaration ━━━┓
┃   ┃   ┃                        ┃
┃   ┃   ┃ public function bar()  ┃
┃   ┃   ┃   {                    ┃
┃   ┃   ┃     return 'baz';      ┃
┃   ┃   ┃   }                    ┃
┃   ┃   ┃                        ┃
┃   ┃   ┃   ┏━━ CompoundStatementNode ━━┓
┃   ┃   ┃   ┃                           ┃
┃   ┃   ┃   ┃ {                         ┃
┃   ┃   ┃   ┃     return 'baz';         ┃
┃   ┃   ┃   ┃   }                       ┃
┃   ┃   ┃   ┃                           ┃
┃   ┃   ┃   ┃   ┏━━ ReturnStatement ━━┓
┃   ┃   ┃   ┃   ┃                     ┃
┃   ┃   ┃   ┃   ┃ return 'baz';       ┃
┃   ┃   ┃   ┃   ┃                     ┃
┃   ┃   ┃   ┃   ┃   ┏━━ StringLiteral ━━┓
┃   ┃   ┃   ┃   ┃   ┃                   ┃
┃   ┃   ┃   ┃   ┃   ┃ 'baz'             ┃
┃   ┃   ┃   ┃   ┃   ┃                   ┃
┃   ┃   ┃   ┃   ┃   ┗━━━━━━━━━━━━━━━━━━━┛
┃   ┃   ┃   ┃   ┗━━━━━━━━━━━━━━━━━━━━━┛
┃   ┃   ┃   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┃   ┃   ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛
┃   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

```

Between the output of the `dump` command, and the example of existing Linters, you’d be surprised how easy it is to start writing your own rules.

IDE Integration
---------------

[](#ide-integration)

The best place for linting results is right in your IDE. Rather than publishing our own IDE plugins, LaraLint can simply pretend to be `PHP_CodeSniffer`:

```
php artisan laralint:lint --printer=phpcs
```

This will give you XML output that is compatible with any plugin that can parse `PHP_CodeSniffer`’s XML output (such as PhpStorm).

Running `php artisan laralint:install` will install a nice helper file that makes this a little easier. Just configure your IDE to point to that file, and your LaraLint lints should get flagged in your IDE.

###  Health Score

57

—

FairBetter than 98% of packages

Maintenance91

Actively maintained with recent releases

Popularity35

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity73

Established project with proven stability

 Bus Factor1

Top contributor holds 88.1% 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 ~96 days

Recently: every ~72 days

Total

25

Last Release

64d ago

PHP version history (2 changes)0.0.6PHP &gt;=7.3.0

0.1.0PHP &gt;=8.3.0

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/21592?v=4)[Chris Morrell](/maintainers/inxilpro)[@inxilpro](https://github.com/inxilpro)

---

Top Contributors

[![inxilpro](https://avatars.githubusercontent.com/u/21592?v=4)](https://github.com/inxilpro "inxilpro (140 commits)")[![skylerkatz](https://avatars.githubusercontent.com/u/7297992?v=4)](https://github.com/skylerkatz "skylerkatz (15 commits)")[![bogdankharchenko](https://avatars.githubusercontent.com/u/32746389?v=4)](https://github.com/bogdankharchenko "bogdankharchenko (4 commits)")

---

Tags

laravellintlinting

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/glhd-laralint/health.svg)

```
[![Health](https://phpackages.com/badges/glhd-laralint/health.svg)](https://phpackages.com/packages/glhd-laralint)
```

###  Alternatives

[laravel/sail

Docker files for running a basic Laravel application.

1.9k205.7M1.3k](/packages/laravel-sail)[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M194](/packages/laravel-ai)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M151](/packages/laravel-mcp)[laravel/boost

Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.

3.5k21.5M594](/packages/laravel-boost)[laravel/cashier

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

2.6k29.9M146](/packages/laravel-cashier)[laravel/pulse

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

1.7k15.1M132](/packages/laravel-pulse)

PHPackages © 2026

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