If you’ve ever opened a PR review and gotten dinged for using \Illuminate\Support\Collection inline instead of importing it at the top, you already know why this feature matters.
Why Laravel Pint Now Replaces Fully Qualified Class Names with Imports
Laravel Pint — Laravel’s zero-config opinionated code style fixer built on top of PHP-CS-Fixer — shipped a behavior change that a lot of PHP developers have been quietly wanting for years. As of this update covered by Laravel News, Pint now replaces fully qualified class names with imports automatically, transforming verbose inline class references into clean use statements at the top of your file.
This isn’t just cosmetic. It directly improves readability, enforces consistency across a codebase, and eliminates a whole category of code style arguments in PRs. If you’ve worked on a team where half the devs write new \Carbon\Carbon() and the other half write use Carbon\Carbon at the top, you know exactly what I mean.
The underlying rule driving this is the fully_qualified_strict_types fixer from PHP-CS-Fixer, which Pint now applies as part of its default ruleset behavior. Let’s dig into what exactly changes, how to configure it, and when you might want to tweak the defaults.
What Changes in Your Code: Before and After
To make this concrete, here’s what Pint will now transform automatically.
Before: Fully Qualified Class Names Inline
<?php
namespace App\Services;
class OrderProcessor
{
public function process(): \Illuminate\Support\Collection
{
$items = new \Illuminate\Support\Collection();
return \Illuminate\Support\Facades\DB::transaction(function () use ($items) {
return $items->map(function ($item) {
return new \App\Models\Order($item);
});
});
}
}
After: Clean Imports at the Top
<?php
namespace App\Services;
use App\Models\Order;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class OrderProcessor
{
public function process(): Collection
{
$items = new Collection();
return DB::transaction(function () use ($items) {
return $items->map(function ($item) {
return new Order($item);
});
});
}
}
The difference is immediately obvious. The second version is what every senior Laravel developer expects to see — and now Pint enforces it without you having to think about it.
This transformation handles multiple use cases at once: type hints in method signatures, new instantiation expressions, static method calls, and catch blocks. That’s comprehensive coverage of every place a fully qualified class name tends to sneak in.
How Pint Replaces Fully Qualified Class Names Under the Hood
Pint’s a wrapper, not a standalone tool. It delegates the actual fixing to PHP-CS-Fixer and manages opinionated rule presets on top. The specific fixers involved here include:
fully_qualified_strict_types— Converts FQCNs in strict type declarations and return types to short names with importsno_leading_import_slash— Strips the leading backslash fromusestatementsordered_imports— Keeps your import block sorted alphabetically (grouped by type if configured)no_unused_imports— Removesusestatements that aren’t referenced anywhere in the file
These fixers work together. When Pint converts an inline \Illuminate\Support\Collection to Collection, it also adds the use Illuminate\Support\Collection; statement and runs the import sorter in the same pass. It’s genuinely atomic — one command, everything cleaned up together.
Running Pint
If you’re not already running Pint in your project, setup is a single Composer command:
composer require laravel/pint --dev
Then run it:
./vendor/bin/pint
To preview what it would change without actually writing files, use the --test flag:
./vendor/bin/pint --test
For CI pipelines — and you should absolutely be running this in CI — --test exits with a non-zero status code if any files need fixing, which fails the build and blocks the merge. That’s exactly the behavior you want.
Configuring the Rules in pint.json
Pint reads from a pint.json file in your project root. If you’re using a custom preset and want to explicitly enable the import-replacing behavior, here’s how to configure it:
{
"preset": "laravel",
"rules": {
"fully_qualified_strict_types": true,
"global_namespace_import": {
"import_classes": true,
"import_constants": false,
"import_functions": false
},
"no_unused_imports": true,
"ordered_imports": {
"sort_algorithm": "alpha"
}
}
}
The global_namespace_import rule deserves a specific callout. Setting import_classes to true ensures that even globally namespaced classes — think \DateTime, \Exception, \Throwable — get imported rather than referenced with a leading backslash. This is a team preference thing. Some codebases keep the leading backslash for global PHP classes to distinguish them visually, and honestly that’s a reasonable stance. Configure it to match your team’s standard.
Practical Implications for Teams and CI Pipelines
Running Pint in GitHub Actions
Here’s a minimal GitHub Actions workflow that enforces this on every PR:
name: Code Style
on: [push, pull_request]
jobs:
pint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Install Dependencies
run: composer install --no-interaction --prefer-dist
- name: Run Pint
run: ./vendor/bin/pint --test
This blocks any PR that introduces inline FQCNs. Once you’ve got this check enabled, it’s self-enforcing — the pipeline catches it before a human has to, which is the whole point.
Migrating an Existing Codebase
If you’re enabling this on an existing project with hundreds of files, do not run Pint across the entire codebase in a single commit. That creates a massive diff that’s nearly impossible to review, and it absolutely destroys your git blame history for those files.
The better approach:
- Run
./vendor/bin/pint --dirtyto only fix files that have uncommitted changes - Fix files incrementally as you touch them in feature work
- Or, if you want to do it all at once, do it in an isolated “style cleanup” commit with a clear message so
git blamestays useful
The --dirty flag is particularly handy in a pre-commit hook setup with tools like Husky or the PHP equivalent GrumPHP.
When You Might Want to Disable This Behavior
Most of the time you want this on. But there are edge cases worth knowing about.
Conditional class existence checks using class_exists('Some\Class\Name') don’t get touched — those are strings, not actual references, so Pint won’t break anything there.
Generated files like migrations or compiled views might have inline FQCNs for specific reasons. If you have directories you want Pint to skip entirely, configure exclusions in pint.json:
{
"exclude": [
"database/migrations",
"bootstrap/cache"
]
}
Packages you’re developing might deliberately use FQCNs in specific places for clarity in documentation-facing code. In those cases you can configure rule exclusions per file using PHP-CS-Fixer’s @ annotations in the file itself — though I’d treat that as a last resort. If you’re reaching for per-file overrides constantly, something’s off with your config.
Conclusion: Let Pint Do the Work
Pint replacing fully qualified class names with imports automatically is a net positive for virtually every Laravel project. It removes a tedious category of review comments, enforces a pattern that experienced PHP developers already follow manually, and requires zero ongoing effort once it’s wired into your CI pipeline. Why spend mental energy on import formatting when a tool can handle it for you?
The opinionated defaults in the Laravel preset are sensible for most projects, and pint.json gives you enough control for the edge cases where you need to deviate. Add it to your CI pipeline today, run it once across your codebase in a dedicated commit, and move on.
Your reviewers will notice the difference — mostly because they’ll stop having anything to comment on.




Leave a Reply