Kotchasan Framework Documentation
Number Class - Number Manipulation Utilities
Number Class - Number Manipulation Utilities
The Number class provides utilities for number formatting, safe division, and document number generation with date placeholders.
Namespace
Kotchasan\NumberOverview
Number class provides methods for:
- Formatting numbers with thousands separators
- Safe division (prevents division by zero)
- Generating document numbers with date patterns
API Reference
format()
Format a number with thousands separator (preserves decimal places without rounding)
public static function format($value, string $thousands_sep = ','): stringParameters:
$value- Number to format$thousands_sep- Thousands separator (default:,)
Returns: Formatted number as string
Features:
- Supports decimals (preserves original decimal places)
- No rounding
- Only formats the integer part
Example:
use Kotchasan\Number;
// Basic formatting
echo Number::format(1234567); // "1,234,567"
echo Number::format(1234.56); // "1,234.56"
echo Number::format(1000000.789); // "1,000,000.789"
// No rounding of decimals
echo Number::format(123.456789); // "123.456789"
echo Number::format(9999.99); // "9,999.99"
// Integer (no decimals)
echo Number::format(123); // "123"
echo Number::format(123.0); // "123"
// Custom separators
echo Number::format(1234567, ' '); // "1 234 567"
echo Number::format(1234567, '.'); // "1.234.567"
echo Number::format(1234567, '-'); // "1-234-567"
// With currency
$price = 2500.50;
echo "Price: " . Number::format($price) . " USD";
// "Price: 2,500.50 USD"
// With database data
$total = $db->first("SELECT SUM(amount) AS total FROM orders")->total;
echo "Total: $" . Number::format($total);division()
Perform safe division, returns 0 if divisor is 0 (prevents division by zero error)
public static function division($dividend, $divisor)Parameters:
$dividend- The dividend$divisor- The divisor
Returns: Result of division, or 0 (if divisor is 0)
Example:
use Kotchasan\Number;
// Normal division
echo Number::division(10, 2); // 5
echo Number::division(15, 3); // 5
echo Number::division(7, 2); // 3.5
echo Number::division(100, 7); // 14.285714285714
// Prevents division by zero
echo Number::division(10, 0); // 0 (no error)
echo Number::division(100, 0); // 0
// Calculate percentage
$correct = 85;
$total = 100;
$percentage = Number::division($correct * 100, $total);
echo "Score: " . $percentage . "%"; // "Score: 85%"
// Calculate average (safe if no data)
$totalAmount = 1500;
$itemCount = 0; // could be 0
$average = Number::division($totalAmount, $itemCount);
echo "Average: " . Number::format($average); // "Average: 0"
// With actual data
$itemCount = 5;
$average = Number::division($totalAmount, $itemCount);
echo "Average: " . Number::format($average); // "Average: 300"
// Calculate rate
$success = $db->count('orders', ['status' => 'completed']);
$totalOrders = $db->count('orders');
$successRate = Number::division($success * 100, $totalOrders);
echo "Success Rate: " . Number::format($successRate) . "%";printf()
Format a number with date placeholders for generating document numbers
public static function printf(string $format, $value, string $prefix = ''): stringParameters:
$format- Format string with placeholders$value- Number value to substitute (for%dor other format specifiers)$prefix- Prefix (optional, used with%s)
Returns: Formatted string
Supported Placeholders:
%YY- Buddhist year (full) - 2567%yy- Gregorian year (full) - 2024%Y- Buddhist year (2 digits) - 67%y- Gregorian year (2 digits) - 24%M- Month with leading zero (01-12)%m- Month without leading zero (1-12)%D- Day with leading zero (01-31)%d- Day without leading zero (1-31)%s- Prefix
Example:
use Kotchasan\Number;
// Assuming current date is December 1, 2024
// Basic document number
echo Number::printf("DOC%YY%M%D-%04d", 123);
// "DOC25671201-0123" (Buddhist year 2567, month 12, day 01)
// Gregorian year
echo Number::printf("INV%yy%M%D-%05d", 456);
// "INV20241201-00456"
// 2-digit year
echo Number::printf("REC%Y%M-%04d", 789);
// "REC6712-0789"
// Month and day without leading zeros
echo Number::printf("ORD%yy%m%d-%04d", 101);
// "ORD2024121-0101" (if date is 1/12/2024)
// With prefix
echo Number::printf("%sBILL%yy%m%d-%03d", 999, "COMP");
// "COMPBILL2024121-999"
// Tax invoice number
echo Number::printf("TAX%YY%M-%06d", 1);
// "TAX256712-000001"
// Member ID
echo Number::printf("MEM%YY-%08d", 12345);
// "MEM2567-00012345"
// Standard format specifiers
echo Number::printf("ORDER-%05d", 42); // "ORDER-00042"
echo Number::printf("ID-%010d", 123); // "ID-0000000123"Real-World Examples
1. Receipt System
use Kotchasan\Number;
class ReceiptSystem
{
private $db;
public function createReceipt($customerId, $items)
{
// Calculate total
$total = 0;
foreach ($items as $item) {
$total += $item['price'] * $item['quantity'];
}
// Generate receipt number
$lastReceipt = $this->db->first("SELECT MAX(id) as last_id FROM receipts");
$nextId = ($lastReceipt->last_id ?? 0) + 1;
$receiptNumber = Number::printf("REC%YY%M%D-%06d", $nextId);
// Save receipt
$receiptId = $this->db->insert('receipts', [
'receipt_number' => $receiptNumber,
'customer_id' => $customerId,
'total_amount' => $total,
'created_date' => date('Y-m-d H:i:s')
]);
return [
'receipt_id' => $receiptId,
'receipt_number' => $receiptNumber,
'total_formatted' => '$' . Number::format($total)
];
}
public function calculateDiscount($amount, $discountPercent)
{
$discountAmount = Number::division($amount * $discountPercent, 100);
$finalAmount = $amount - $discountAmount;
return [
'original' => Number::format($amount),
'discount' => $discountPercent . '%',
'discount_amount' => Number::format($discountAmount),
'final' => Number::format($finalAmount)
];
}
}
// Usage
$receipt = new ReceiptSystem($db);
$result = $receipt->createReceipt(123, [
['price' => 100, 'quantity' => 2],
['price' => 50.50, 'quantity' => 3]
]);
echo $result['receipt_number']; // "REC25671201-000001"
echo $result['total_formatted']; // "$351.50"2. Financial Report
class FinancialReport
{
public static function formatCurrency($amount)
{
return '$' . Number::format($amount);
}
public static function calculatePercentage($part, $total)
{
$percentage = Number::division($part * 100, $total);
return Number::format($percentage) . '%';
}
public static function generateSummary($sales, $target)
{
$achievementRate = Number::division($sales * 100, $target);
$remaining = $target - $sales;
return [
'sales' => self::formatCurrency($sales),
'target' => self::formatCurrency($target),
'achievement' => Number::format($achievementRate) . '%',
'remaining' => self::formatCurrency($remaining)
];
}
}
// Generate report
$report = FinancialReport::generateSummary(250000, 300000);
print_r($report);
/*
Array (
[sales] => $250,000
[target] => $300,000
[achievement] => 83.333333333333%
[remaining] => $50,000
)
*/3. Document Number Generator
class DocumentNumberGenerator
{
private static $formats = [
'invoice' => "INV%YY%M%D-%05d",
'receipt' => "REC%YY%M%D-%05d",
'quotation' => "QUO%YY%M%D-%05d",
'purchase_order' => "PO%YY%M%D-%05d"
];
public static function generate($type, $sequence, $branch = '')
{
if (!isset(self::$formats[$type])) {
throw new \Exception("Invalid document type: $type");
}
return Number::printf(self::$formats[$type], $sequence, $branch);
}
public static function generateWithBranch($type, $sequence, $branchCode)
{
$format = "%s" . self::$formats[$type];
return Number::printf($format, $sequence, $branchCode);
}
}
// Usage
echo DocumentNumberGenerator::generate('invoice', 123);
// "INV25671201-00123"
echo DocumentNumberGenerator::generateWithBranch('receipt', 456, "NYC");
// "NYCREC25671201-00456"4. Statistics Calculator
class Statistics
{
public static function calculateAverage($numbers)
{
$sum = array_sum($numbers);
$count = count($numbers);
return Number::division($sum, $count);
}
public static function calculateGrowthRate($oldValue, $newValue)
{
$diff = $newValue - $oldValue;
$growthRate = Number::division($diff * 100, $oldValue);
return Number::format($growthRate) . '%';
}
public static function formatReport($data)
{
$total = array_sum($data);
$average = self::calculateAverage($data);
$count = count($data);
return [
'count' => Number::format($count),
'total' => Number::format($total),
'average' => Number::format($average),
'max' => Number::format(max($data)),
'min' => Number::format(min($data))
];
}
}
// Usage
$salesData = [1500, 2300, 1800, 2100, 1900];
$stats = Statistics::formatReport($salesData);
print_r($stats);
echo Statistics::calculateGrowthRate(1000, 1500); // "50%"Best Practices
1. Use format() for Display
// ✅ Good - display as readable string
echo "Total: $" . Number::format($total);
// ❌ Bad - display raw number
echo "Total: $$total"; // $1234567 (hard to read)2. Use division() When Zero is Possible
// ✅ Good - prevents division by zero
$average = Number::division($total, $count);
// ❌ Dangerous - may cause error
$average = $count > 0 ? $total / $count : 0; // verbose and complex3. Generate Document Numbers with printf()
// ✅ Good - consistent format with printf()
$docNo = Number::printf("INV%YY%M%D-%05d", $id);
// ❌ Bad - manual generation (complex and error-prone)
$y = date('Y') + 543;
$docNo = "INV{$y}" . date('md') . "-" . str_pad($id, 5, '0', STR_PAD_LEFT);4. Format Before or After Storage?
// ✅ Good - store as number, format only for display
$db->insert('orders', ['amount' => 1234.56]);
echo Number::format($order->amount); // "1,234.56"
// ❌ Bad - store formatted string
$db->insert('orders', ['amount' => Number::format(1234.56)]); // "1,234.56"
// Problem: cannot calculate with stringImportant Considerations
[!WARNING]
format() does NOT round: This method only adds separators, it doesn't round decimals. Useround()first if needed[!NOTE]
division() returns 0: When dividing by zero, returns 0 (not null). Be careful when distinguishing "no data" from "result is zero"[!IMPORTANT]
printf() uses current date: Date placeholders usedate(), so they reflect the time when the method is called, not a custom date[!TIP]
Performance: All methods are static and can be called directly without creating an instance
Related Classes
Summary
The Number class has 3 main methods:
- format() - Format numbers with thousands separators (preserves decimals)
- division() - Safe division (returns 0 if divisor is 0)
- printf() - Generate document numbers with date patterns (supports Buddhist/Gregorian years)
Perfect for:
- Displaying numbers in readable format
- Calculations that need to prevent division by zero
- Generating automatic document numbers with date patterns