php-standard-library/math
Strictly typed math utilities for PHP with predictable, consistent error handling. Part of the PHP Standard Library project, providing reliable mathematical functions and a stable developer experience for safer numeric operations.
Installation:
composer require php-standard-library/math
No configuration required—package is dependency-free.
First Use Case: Replace loose arithmetic with strict, typed operations. Example:
use Math\Division;
// Instead of:
$result = 10 / 0; // Silent NaN in PHP
// Use:
$result = Division::safeDivide(10, 0); // Returns null with no exception
// Or throw on error:
$result = Division::divide(10, 0); // Throws DivisionByZeroException
Key Classes to Explore:
Math\Addition, Math\Subtraction: Strict addition/subtraction with overflow checks.Math\Multiplication, Math\Division: Safe operations with zero-division handling.Math\SquareRoot, Math\Logarithm: Scientific functions with domain validation.Math\Range: Validate numeric ranges (e.g., Range::validate(10, 0, 100)).Where to Look First:
tests/ for usage examples).tests/ directory for edge-case examples (e.g., Math\FactorialTest).// Before (loose)
$discount = $price * $percentage; // Silent type coercion
// After (strict)
$discount = Multiplication::multiply($price, $percentage);
AppServiceProvider:
$this->app->bind(Multiplication::class, function () {
return new Multiplication();
});
Use in controllers/services:
use Illuminate\Support\Facades\App;
$total = App::make(Multiplication::class)->multiply($subtotal, 1.08);
Math\Range or custom rules.use Math\Range;
use Illuminate\Validation\Rule;
// Custom validation rule
$validator->addRules([
'age' => ['required', function ($attribute, $value, $fail) {
if (!Range::validate($value, 0, 120)) {
$fail('Age must be between 0 and 120.');
}
}],
]);
use Illuminate\Foundation\Http\FormRequest;
use Math\Range;
class StoreUserRequest extends FormRequest
{
public function rules()
{
return [
'height' => ['required', 'numeric', function ($attribute, $value, $fail) {
if (!Range::validate($value, 0, 300)) {
$fail('Height must be between 0 and 300 cm.');
}
}],
];
}
}
use Math\Division;
class Product extends Model
{
public function getDiscountedPriceAttribute()
{
return Division::safeDivide(
$this->price,
$this->discount_factor ?? 1
);
}
}
use Math\Round;
$roundedPrices = Product::query()
->get()
->map(fn ($product) => [
'price' => Round::round($product->price, 2),
]);
use Math\Addition;
use Illuminate\Console\Command;
class NormalizePricesCommand extends Command
{
protected $signature = 'math:normalize-prices';
protected $description = 'Adjust prices by a fixed percentage';
public function handle()
{
$products = Product::all();
foreach ($products as $product) {
$newPrice = Addition::add(
$product->price,
Addition::multiply($product->price, 0.1) // 10% increase
);
$product->update(['price' => $newPrice]);
}
}
}
use Math\Round;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
public function toArray($request)
{
return [
'price' => Round::round($this->price, 2),
'tax' => Round::round($this->price * 0.08, 2),
];
}
}
Facade Wrapper:
Create a Math facade for consistency with Laravel’s ecosystem:
// app/Facades/Math.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
use Math\Addition;
class Math extends Facade
{
protected static function getFacadeAccessor()
{
return Addition::class;
}
}
Register in AppServiceProvider:
Facade::alias('Math', \App\Facades\Math::class);
Usage:
$total = \Math::add(10, 20); // Alias for Addition::add()
Exception Handling:
Centralize math exceptions in a global handler (e.g., app/Exceptions/Handler.php):
public function render($request, Throwable $exception)
{
if ($exception instanceof \Math\MathException) {
return response()->json([
'error' => 'Invalid math operation',
'details' => $exception->getMessage(),
], 400);
}
return parent::render($request, $exception);
}
Testing: Use Pest/PHPUnit to test edge cases:
// tests/Feature/MathOperationsTest.php
use Math\Division;
use Tests\TestCase;
class MathOperationsTest extends TestCase
{
public function testDivisionByZeroThrowsException()
{
$this->expectException(\Math\DivisionByZeroException::class);
Division::divide(10, 0);
}
public function testSafeDivisionReturnsNull()
{
$result = Division::safeDivide(10, 0);
$this->assertNull($result);
}
}
Strict Typing Breaks Legacy Code:
null or non-numeric values to math functions throws InvalidInputException.safe* variants or validate inputs:
if (!is_numeric($input)) {
throw new \InvalidArgumentException('Input must be numeric');
}
Overflow/Underflow Silently Fails:
Math\Factorial) may not throw on overflow in PHP.$result = Math\Factorial::compute(min($input, 1000)); // Cap at 1000!
Performance Overhead:
// Avoid for hot paths:
$sum = Math\Addition::add($a, $b);
// Prefer for hot paths:
$sum = $a + $b;
Floating-Point Precision:
Math\Division may still suffer from IEEE 754 quirks.bcmath or gmp for arbitrary precision:
$result = gmp_div_q($a, $b); // Alternative for high precision
Laravel Caching Quirks:
$key = "math:discount:{$productId}:{$percentage}";
$discount = Cache::remember($key, now()->addHours(1), function () use
How can I help you explore Laravel packages today?