Laravel Pint is an opinionated code style fixer for Laravel projects. While Pint works out of the box with a sensible default configuration, many teams benefit from customizing it to better match their coding principles and project architecture.
In this post, we’ll walk through our tailored pint.json
configuration — explain what we changed, why we changed it, and how it helps us write cleaner, safer, and more maintainable PHP code.
Why Customize Pint?
The default Laravel preset is a great start. But as your team grows or your codebase scales, you may want to:
- Enforce stricter type safety
- Eliminate legacy or verbose syntax
- Modernize casting and string functions
- Promote immutability and consistent code structure
To do this, we introduced a custom pint.json
file.
Our Pint Configuration
Here’s the high-level structure of our pint.json
file:
{
"preset": "laravel",
"rules": {
"array_push": true,
"backtick_to_shell_exec": true,
"date_time_immutable": true,
"declare_strict_types": true,
"lowercase_keywords": true,
"lowercase_static_reference": true,
"final_class": false,
"final_internal_class": false,
"final_public_method_for_abstract_class": false,
"fully_qualified_strict_types": true,
"global_namespace_import": {
"import_classes": true,
"import_constants": true,
"import_functions": true
},
"mb_str_functions": true,
"modernize_types_casting": true,
"new_with_parentheses": false,
"no_superfluous_elseif": true,
"no_useless_else": true,
"no_multiple_statements_per_line": true,
"ordered_class_elements": {
"order": [
"use_trait",
"case",
"constant",
"constant_public",
"constant_protected",
"constant_private",
"property_public",
"property_protected",
"property_private",
"construct",
"destruct",
"magic",
"phpunit",
"method_abstract",
"method_public_static",
"method_public",
"method_protected_static",
"method_protected",
"method_private_static",
"method_private"
],
"sort_algorithm": "none"
},
"ordered_interfaces": true,
"ordered_traits": true,
"protected_to_private": true,
"self_accessor": true,
"self_static_accessor": true,
"strict_comparison": true,
"visibility_required": true
}
}
We extend the Laravel preset and layer on our own rules. Let’s explore what we changed and why.
Enabled Rules
declare_strict_types and fully_qualified_strict_types
We enforce strict_types in every PHP file and require fully qualified declare(strict_types=1);
declarations. This ensures our code behaves consistently and avoids silent type coercions.
date_time_immutable
We prefer DateTimeImmutable over DateTime to avoid unintended side effects caused by mutable date objects.
array_push
We discourage the use of array_push($array, $value)
in favor of the cleaner $array[] = $value
.
mb_str_functions
All string manipulation uses multibyte-safe functions like mb_strtolower
instead of strtolower
, making our code safer for UTF-8
content.
modernize_types_casting
This replaces outdated casting syntax like (int)
with modern functions like intval()
, which is clearer and more predictable in edge cases.
backtick_to_shell_exec
Backtick syntax for executing shell commands is replaced with shell_exec()
— it’s more readable, consistent, and avoids subtle syntax issues.
lowercase_keywords and lowercase_static_reference
All PHP keywords and static calls are enforced to be lowercase, ensuring visual consistency and aligning with modern best practices.
Customized or Disabled Rules
Disabled:
final_class,
final_internal_class,
final_public_method_for_abstract_class
We allow more flexibility in class inheritance, especially useful in a modular or package-driven architecture where extensibility is key.
Disabled: new_with_parentheses
We’re okay with omitting parentheses for object instantiation in expressive contexts, like $foo = new Foo;
.
Structural Rules
ordered_class_elements,
ordered_interfaces,
ordered_traits
These rules define the exact order in which class members should appear (traits, constants, properties, constructors, public methods, etc.), making it easier for developers to read and understand our code.
Example structure:
"ordered_class_elements": {
"order": [
"use_trait",
"constant",
"property_public",
"construct",
"method_public"
]
}
global_namespace_import
We automatically import classes, constants, and functions from the global namespace, reducing noise and promoting clarity.
protected_to_private
When a class member doesn’t need to be protected, we automatically convert it to private, enforcing encapsulation and avoiding accidental coupling.
Code Safety Enhancements
strict_comparison
We use ===
and !==
for all comparisons, ensuring type-safe logic and preventing bugs due to loose equality (==, !=).
no_useless_else,
no_superfluous_elseif,
no_multiple_statements_per_line
These rules clean up our control flow and make conditionals more readable and maintainable.