roave/no-floaters
PHPStan plugin that bans floats: no float properties, parameters, return types, or assigning float values. Optional strict mode disallows any float-producing expression. Helps prevent rounding errors in domains like money, grading, or scientific calculations.
Installation Add the package via Composer:
composer require --dev roave/no-floaters
Note: This release (1.16.0) drops PHP 8.3 support entirely. Ensure your project uses PHP 8.1 or 8.2 (no PHP 8.3 compatibility).
First Use Case Run the static analyzer on your codebase:
vendor/bin/no-floaters
This includes conditional return type handling (e.g., detecting floating-point expressions in ternary or conditional return types like function foo(): float { return $floatVar ? 1.0 : 2.0; }).
Where to Look First
config/no-floaters.php for custom rules (if auto-generated).tests/ for examples of flagged operations, including conditional return types.Integrate with CI/CD
Add to composer.json under scripts:
"scripts": {
"test": [
"@phpstan",
"no-floaters"
]
}
Run via composer test in your pipeline.
Note: Ensure your CI environment uses PHP 8.1–8.2 (no PHP 8.3).
Custom Rules
Extend rules in config/no-floaters.php:
return [
'rules' => [
'Roave\NoFloaters\Rules\DivisionByFloat',
'Roave\NoFloaters\Rules\ConditionalReturnType',
'App\Custom\Rules\NoFloatMultiplication',
],
];
The ConditionalReturnType rule detects floating-point expressions in conditional return types (e.g., ternary operators or ?: syntax).
Excluding Files/Directories
Use .no-floaters-exclude file or CLI args:
vendor/bin/no-floaters --exclude=tests/unit/
Fixing Violations
1 / 3 instead of 1.0 / 3.0).bcmul(), bcdiv() for arbitrary precision (Laravel’s bcmath helper):
$result = bcdiv('1', '3', 10); // 10 decimal places
// Before (flagged)
function getDiscount(): float { return $isPremium ? 1.0 : 0.0; }
// After
function getDiscount(): float { return $isPremium ? 1 : 0; }
Laravel-Specific Patterns
DB::raw('SELECT * FROM users WHERE score > 1.0'). Use integers or CAST in SQL.Integer or Numeric rules instead of Float:
$request->validate(['price' => 'integer']); // or 'numeric'
integer columns for monetary values (use cents, e.g., 999 for $9.99).// Before (flagged by ConditionalReturnType)
public function calculateTax(): float { return $isVatApplicable ? 1.2 : 1.0; }
// After
public function calculateTax(): float { return $isVatApplicable ? 120 : 100; }
PHP Version Compatibility (BREAKING CHANGE)
composer.json:
"require": {
"php": ">=8.1.0,<8.3.0"
}
False Positives
ConditionalReturnType rule may flag legitimate cases like:
function getRatio(): float { return $isValid ? 1.0 / 3.0 : 0.0; }
Solution: Exclude specific files or adjust rules:
'exceptions' => [
'App\Services\RatioCalculator', // Exclude class
],
M_PI or E may still trigger rules. Exclude them in config:
'exceptions' => [
'M_PI',
'E',
],
Performance Overhead
phpstan:
vendor/bin/no-floaters --cache-results
Precision Assumptions
== for floats. Use abs($a - $b) < 0.0001 or bccomp():
if (bccomp($floatA, $floatB, 6) === 0) { ... }
Legacy Code
--fix mode (if available) to auto-correct simple cases.?: syntax returning floats.-v to see file-by-file violations:
vendor/bin/no-floaters -v
ConditionalReturnType:
vendor/bin/no-floaters --rules=ConditionalReturnType
-vv for detailed analysis of conditional return types:
vendor/bin/no-floaters -vv
Custom Rules
Extend Roave\NoFloaters\Rules\AbstractRule to create domain-specific checks (e.g., "no floats in tax calculations").
Example: Override ConditionalReturnType for custom logic:
use Roave\NoFloaters\Rules\AbstractRule;
class NoFloatInDiscounts extends AbstractRule
{
public function __invoke(Node $node): ?string
{
if ($node instanceof ConditionalReturnType && $node->getReturnType() === 'float') {
return 'Floating-point discounts are not allowed.';
}
return null;
}
}
Integration with PHPStan
Combine with phpstan for deeper analysis:
vendor/bin/phpstan analyse --level=max --memory-limit=2G
Add to phpstan.neon:
includes:
- vendor/roave/no-floaters/phpstan/extension.neon
IDE Support
phpstan/extension-installer to auto-load rules in PHPStorm:
composer require --dev phpstan/extension-installer
no-floaters on save via Settings > Tools > External Tools.Laravel Artisan Command
Create a custom Artisan command to wrap no-floaters:
// app/Console/Commands/CheckFloaters.php
public function handle()
{
$this->call('vendor:publish', ['--provider' => 'Roave\NoFloaters\NoFloatersServiceProvider']);
$this->info('Running NoFloaters (including ConditionalReturnType checks)...');
$exitCode = Artisan::call('no-floaters');
if ($exitCode !== 0) exit($exitCode);
}
Testing Conditional Logic Add tests for conditional return types in your project:
// tests/Unit/NoFloatersTest.php
public function testConditionalReturnTypeDetection()
{
$this->assertViolation(
'ConditionalReturnType',
'function foo(): float { return $bool ? 1.0 : 2.0; }'
);
}
How can I help you explore Laravel packages today?