Kotchasan Framework Documentation
Utility Classes
Utility Classes
Utility classes in Kotchasan Framework act as helper functions for managing various data types, including ArrayTool, Text, Number, and Date, facilitating efficient data handling.
Table of Contents
- ArrayTool - Array Management
- Text - Text Management
- Number - Number Management
- Date - Date and Time Management
- Best Practices
ArrayTool - Array Management
Basic Usage
use Kotchasan\ArrayTool;
// Sample data
$users = [
['id' => 1, 'name' => 'John', 'email' => 'john@email.com', 'status' => 'active'],
['id' => 2, 'name' => 'Jane', 'email' => 'jane@email.com', 'status' => 'inactive'],
['id' => 3, 'name' => 'Bob', 'email' => 'bob@email.com', 'status' => 'active'],
];
$products = [
(object)['id' => 1, 'name' => 'Laptop', 'price' => 25000, 'category' => 'electronics'],
(object)['id' => 2, 'name' => 'Mouse', 'price' => 500, 'category' => 'electronics'],
(object)['id' => 3, 'name' => 'Book', 'price' => 250, 'category' => 'education'],
];Retrieving Columns
// Get all names
$names = ArrayTool::columns($users, 'name');
// Result: [0 => 'John', 1 => 'Jane', 2 => 'Bob']
// Get names indexed by ID
$namesByIds = ArrayTool::columns($users, 'name', 'id');
// Result: [1 => 'John', 2 => 'Jane', 3 => 'Bob']
// Works with Object arrays
$productNames = ArrayTool::columns($products, 'name', 'id');
// Result: [1 => 'Laptop', 2 => 'Mouse', 3 => 'Book']
// Create dropdown options
$statusOptions = ArrayTool::columns($users, 'status', 'id');
$selectOptions = '';
foreach ($statusOptions as $id => $status) {
$selectOptions .= "<option value=\"{$id}\">{$status}</option>";
}Searching and Filtering
// Search users with status = 'active'
$activeUsers = ArrayTool::search($users, 'status', 'active');
// Result: [0 => ['id' => 1, 'name' => 'John', ...], 2 => ['id' => 3, 'name' => 'Bob', ...]]
// Filter by keyword
$filteredUsers = ArrayTool::filter($users, 'john');
// Result: Users with name containing 'John'
// Product search system
$searchTerm = 'laptop';
$searchResults = ArrayTool::filter($products, $searchTerm);
// Combine with transformation
$productList = [];
foreach ($searchResults as $product) {
$productList[] = [
'id' => $product->id,
'name' => $product->name,
'price_formatted' => number_format($product->price, 2)
];
}Sorting
// Sort users by name
$sortedUsers = ArrayTool::sort($users, 'name');
// Sort products by price descending
$sortedProducts = ArrayTool::sort($products, 'price', true);
// Complex sorting
$orders = [
['order_id' => 101, 'total' => 1500, 'date' => '2024-01-15'],
['order_id' => 102, 'total' => 2500, 'date' => '2024-01-12'],
['order_id' => 103, 'total' => 800, 'date' => '2024-01-18'],
];
$ordersByTotal = ArrayTool::sort($orders, 'total', true); // High to Low
$ordersByDate = ArrayTool::sort($orders, 'date'); // Old to NewData Manipulation
// Delete by key
$remainingUsers = array_diff_key($users, array_flip([0, 2])); // Remove index 0 and 2
$remainingUsers = array_diff_key($users, array_flip(explode(',', '1,2'))); // Remove index 1 and 2
// Safe retrieval
$userEmail = $users[0]['email'] ?? 'No email';
$userPhone = $users[0]['phone'] ?? 'No phone'; // Key doesn't exist
// Replace data
$updatedUser = ArrayTool::replace($users[0], [
'status' => 'premium',
'last_login' => date('Y-m-d H:i:s')
]);
// Insert before or after
$menu = ['home' => 'Home', 'contact' => 'Contact'];
$menuWithAbout = ArrayTool::insertAfter($menu, 'home', 'about', 'About Us');
// Result: ['home' => 'Home', 'about' => 'About Us', 'contact' => 'Contact']Complex Transformation
// Convert nested array to string
$nestedData = [
'user' => ['name' => 'John', 'age' => 30],
'preferences' => ['theme' => 'dark', 'language' => 'th'],
'tags' => ['admin', 'moderator']
];
$dataString = ArrayTool::toString(' | ', $nestedData);
// Result: "John|30|dark|th|admin|moderator"
// Unserialize data
$serializedData = serialize(['setting1' => 'value1', 'setting2' => 'value2']);
$defaultSettings = ['setting2' => 'default2', 'setting3' => 'default3'];
$finalSettings = ArrayTool::unserialize($serializedData, $defaultSettings, false);
// Result: ['setting2' => 'default2', 'setting3' => 'default3', 'setting1' => 'value1']
// Flexible existence check
$needles = ['admin', 'user'];
$userRoles = ['admin', 'moderator', 'editor'];
$hasAccess = ArrayTool::inArrayAny($needles, $userRoles); // true
// Permission system
$requiredPermissions = ['read', 'write'];
$userPermissions = ['read', 'write', 'delete'];
$canAccess = ArrayTool::inArrayAny($requiredPermissions, $userPermissions);Real-world Usage
class UserManager
{
public function getUserDropdown($users, $selectedId = null)
{
$options = ArrayTool::columns($users, 'name', 'id');
$html = '<select name="user_id">';
foreach ($options as $id => $name) {
$selected = ($id == $selectedId) ? 'selected' : '';
$html .= "<option value=\"{$id}\" {$selected}>{$name}</option>";
}
$html .= '</select>';
return $html;
}
public function filterUsersByRole($users, $role)
{
return ArrayTool::search($users, 'role', $role);
}
public function sortUsersByLastActivity($users)
{
return ArrayTool::sort($users, 'last_activity', true);
}
public function buildUserSearchResults($users, $searchTerm)
{
$filtered = ArrayTool::filter($users, $searchTerm);
return ArrayTool::sort($filtered, 'name');
}
}Text - Text Management
Text Formatting
use Kotchasan\Text;
// Truncate text
$longText = "This is a very long text that needs to be truncated for display purposes";
$shortText = Text::cut($longText, 30);
// Result: "This is a very long text th.."
// Clean multiline text
$multilineText = " This is line 1 \n\n This is line 2 \t\n This is line 3 ";
$cleanText = Text::oneLine($multilineText);
// Result: "This is line 1 This is line 2 This is line 3"
$cleanTextWithLimit = Text::oneLine($multilineText, 25);
// Result: "This is line 1 This is .."
// Safe text
$unsafeText = "<script>alert('XSS')</script>Hello & Welcome";
$safeText = Text::htmlspecialchars($unsafeText);
// Result: "<script>alert('XSS')</script>Hello & Welcome"
// Revert
$originalText = Text::unhtmlspecialchars($safeText);
// Result: "<script>alert('XSS')</script>Hello & Welcome"Password and Username Management
// Clean password
$rawPassword = "P@ssw0rd!@#$%^&()_+{}[]|\"':;?><,./`~abc";
$cleanPassword = Text::password($rawPassword);
// Result: "P@ssw0rd!#$&{}[]_-.abc"
// Clean username
$rawUsername = "user@domain.com!@#$%^&(){}[]|\"':;?><,./`~";
$cleanUsername = Text::username($rawUsername);
// Result: "user@domain.com_-."
// Generate random password
$randomPassword = Text::generateRandomString(8, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
$randomCode = Text::generateRandomString(6); // Numbers only
$randomPin = Text::generateRandomString(4, '0123456789');Conversion and Highlighting
// BBCode and URL conversion
$bbcodeText = "Hello [b]World[/b]! Check out https://example.com for more info.";
$htmlText = Text::highlighter($bbcodeText);
// Result: "Hello <b>World</b>! Check out <a href=\"https://example.com\" target=\"_blank\">https://example.com</a> for more info."
// Lists
$listText = "[ul]Item 1<br>Item 2<br>Item 3[/ul]";
$htmlList = Text::highlighter($listText);
// Result: "<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>"
// YouTube embed
$youtubeText = "Watch this video: [youtube]dQw4w9WgXcQ[/youtube]";
$embedCode = Text::highlighter($youtubeText);File Size Formatting
// Format file size
$fileSize1 = Text::formatFileSize(1024); // "1 KB"
$fileSize2 = Text::formatFileSize(1048576); // "1 MB"
$fileSize3 = Text::formatFileSize(1073741824); // "1 GB"
$fileSize4 = Text::formatFileSize(1536, 1); // "1.5 KB"
// List files
$files = [
['name' => 'document.pdf', 'size' => 2097152],
['name' => 'image.jpg', 'size' => 524288],
['name' => 'video.mp4', 'size' => 52428800],
];
foreach ($files as $file) {
echo $file['name'] . ' (' . Text::formatFileSize($file['size']) . ')' . "\n";
}
// Output:
// document.pdf (2 MB)
// image.jpg (512 KB)
// video.mp4 (50 MB)Advanced Usage
// Replace text with template
$template = "Hello {name}, your order #{order_id} total is {total} baht.";
$replacements = [
'{name}' => 'John Doe',
'{order_id}' => '12345',
'{total}' => '1,500.00'
];
$message = Text::replace($template, $replacements);
// Result: "Hello John Doe, your order #12345 total is 1,500.00 baht."
// Clean URL
$unsafeUrl = "javascript:alert('XSS')";
$safeUrl = Text::url($unsafeUrl); // Result: "alert('XSS')"
$normalUrl = "https://example.com/page?param=value";
$cleanUrl = Text::url($normalUrl); // Result: "https://example.com/page?param=value"
// Text for editor
$editorText = "Content with <tags> & special chars {variables}";
$editorSafe = Text::toEditor($editorText);
// Result: "Content with <tags> & special chars {variables}"
// Clean Topic
$rawTopic = " Breaking News:\n\n Important Update! ";
$cleanTopic = Text::topic($rawTopic);
// Result: "Breaking News: Important Update!"Real-world Usage
class ContentManager
{
public function processUserComment($comment)
{
// Basic cleaning
$clean = Text::htmlspecialchars($comment);
// Truncate
$clean = Text::cut($clean, 500);
// Convert URL and BBCode
$clean = Text::highlighter($clean);
return $clean;
}
public function generateSlug($title)
{
// Clean topic
$clean = Text::topic($title, false);
// Convert to slug
$slug = strtolower($clean);
$slug = preg_replace('/[^a-z0-9\-]/', '-', $slug);
$slug = preg_replace('/-+/', '-', $slug);
$slug = trim($slug, '-');
return $slug;
}
public function formatFileUploadInfo($filename, $size)
{
$cleanName = Text::htmlspecialchars($filename);
$formattedSize = Text::formatFileSize($size);
return "{$cleanName} ({$formattedSize})";
}
public function generateEmailTemplate($template, $userData)
{
$replacements = [];
foreach ($userData as $key => $value) {
$replacements["{{$key}}"] = Text::htmlspecialchars($value);
}
return Text::replace($template, $replacements);
}
}Number - Number Management
Number Formatting
use Kotchasan\Number;
// Format with comma
$price = 1234567; // .89;
$formattedPrice = Number::format($price);
// Result: "1,234,567.89"
// Custom separator
$formattedEuropean = Number::format($price, '.');
// Result: "1.234.567.89"
// Integer formatting
$quantity = 1500;
$formattedQuantity = Number::format($quantity);
// Result: "1,500"
// Financial data
$amounts = [1234.56, 987654, 12.345, 0.99];
foreach ($amounts as $amount) {
echo Number::format($amount, ',') . " Baht\n";
}
// Output:
// 1,234.56 Baht
// 987,654 Baht
// 12.345 Baht
// 0.99 BahtSafe Division
// Safe division (prevents division by zero)
$dividend = 10;
$divisor = 0;
$result = Number::division($dividend, $divisor);
// Result: 0 (instead of error)
// Daily average
$totalSales = 5000;
$totalDays = 3;
$averageDaily = Number::division($totalSales, $totalDays);
// Result: 1666.67
// Percentage
$passed = 8;
$total = 10;
$percentage = Number::division($passed * 100, $total);
// Result: 85
// Prevent division by zero
$scores = [95, 87, 92, 88, 76];
$totalScore = array_sum($scores);
$count = count($scores);
$average = Number::division($totalScore, $count);
// Result: 87.6Date Template Formatting
// Format with placeholders
$documentFormat = "DOC-%Y%m%d-%04d"; // Year AD
$documentNumber = 1;
$docId = Number::printf($documentFormat, $documentNumber);
// Result: "DOC-20240115-0001"
$invoiceFormat = "INV-%YY%M-%05d"; // Short Year BE
$invoiceNumber = 12;
$invoiceId = Number::printf($invoiceFormat, $invoiceNumber, 'TH');
// Result: "INV-67-01-00123" (If year 2567)
// Other formats
$formats = [
"ORD-%y%m%d-%d" => 1, // ORD-240115-1
"REF-%Y%M-%d" => 15, // REF-2567-1-15
"TXN-%yy%mm%dd-%05d" => 999 // TXN-24-01-15-00999
];
foreach ($formats as $format => $number) {
echo Number::printf($format, $number) . "\n";
}Real-world Usage
class FinancialCalculator
{
public function formatCurrency($amount, $currency = 'THB')
{
$formatted = Number::format($amount, ',');
$symbols = [
'THB' => '฿',
'USD' => '$',
'EUR' => '€'
];
$symbol = $symbols[$currency] ?? $currency;
return $symbol . ' ' . $formatted;
}
public function calculatePercentage($value, $total)
{
$percentage = Number::division($value * 100, $total);
return Number::format($percentage, ',') . '%';
}
public function calculateAverage($values)
{
$sum = array_sum($values);
$count = count($values);
return Number::division($sum, $count);
}
public function generateInvoiceNumber($sequence)
{
return Number::printf("INV-%Y%M-%05d", $sequence);
}
}
class SalesReport
{
public function formatSalesData($salesData)
{
$formatted = [];
foreach ($salesData as $item) {
$formatted[] = [
'product' => $item['product'],
'quantity' => Number::format($item['quantity']),
'price' => Number::format($item['price'], ','),
'total' => Number::format($item['quantity'] * $item['price'], ','),
'percentage' => $this->calculatePercentage($item['total'], $item['grand_total'])
];
}
return $formatted;
}
private function calculatePercentage($value, $total)
{
return Number::format(Number::division($value * 100, $total), ',') . '%';
}
}Date - Date and Time Management
Comparing Dates
use Kotchasan\Date;
// Calculate age
$birthDate = '1990-05-15';
$today = date('Y-m-d');
$ageData = Date::compare($birthDate, $today);
// Result: ['days' => 12345, 'year' => 33, 'month' => 8, 'day' => 1]
$ageText = Date::age($birthDate);
// Result: "33 {LNG_year} 8 {LNG_month} 1 {LNG_days}"
// Compare two dates
$startDate = '2024-01-01';
$endDate = '2024-12-31';
$diff = Date::compare($startDate, $endDate);
// Result: ['days' => 365, 'year' => 0, 'month' => 11, 'day' => 30]
// Service period
$hireDate = '2020-03-15';
$currentDate = date('Y-m-d');
$workPeriod = Date::compare($hireDate, $currentDate);
echo "Worked for {$workPeriod['year']} years {$workPeriod['month']} months";Date Formatting
// Format today
$formattedToday = Date::format();
// Uses format from DATE_FORMAT language file
// Format specific timestamp
$timestamp = strtotime('2024-01-15 14:30:00');
$formatted = Date::format($timestamp, 'd/m/Y H:i');
// Result: "15/01/2567 14:30" (If using BE year)
// Format from string
$dateString = '2024-01-15 14:30:00';
$formatted = Date::format($dateString, 'l, F d, Y');
// Result: "Mon, January 15, 2567" (Depends on language settings)
// Various formats
$formats = [
'd/m/Y' => '15/01/2567',
'Y-m-d' => '2567-01-15',
'l, F d' => 'Monday, January 15',
'H:i:s' => '14:30:00'
];
foreach ($formats as $format => $expected) {
echo Date::format($dateString, $format) . "\n";
}Date Names
// Day names (0=Sunday, 6=Saturday)
$dayShort = Date::dateName(1); // "Mon"
$dayFull = Date::dateName(1, false); // "Monday"
// Month names (1=January, 12=December)
$monthShort = Date::monthName(1); // "Jan"
$monthFull = Date::monthName(1, false); // "January"
// Create calendar
$calendar = [];
for ($month = 1; $month <= 12; $month++) {
$calendar[$month] = [
'short' => Date::monthName($month, true),
'full' => Date::monthName($month, false)
];
}
// Day options
$dayOptions = '';
for ($day = 0; $day < 7; $day++) {
$dayName = Date::dateName($day, false);
$dayOptions .= "<option value=\"{$day}\">{$dayName}</option>";
}Parsing and Time Difference
// Parse date
$dateComponents = Date::parse('2024-01-15 14:30:45');
// Result: ['y' => '2024', 'm' => '01', 'd' => '15', 'h' => '14', 'i' => '30', 's' => '45']
$dateOnly = Date::parse('2024-01-15');
// Result: ['y' => '2024', 'm' => '01', 'd' => '15']
// Reconstruct
if ($dateComponents) {
$customDate = "{$dateComponents['d']}/{$dateComponents['m']}/{$dateComponents['y']}";
// Result: "15/01/2024"
}
// Time difference
$time1 = '2024-01-15 10:00:00';
$time2 = '2024-01-15 14:30:00';
$timeDiff = Date::timeDiff($time1, $time2);
// Result: 16200 (seconds) = 4.5 hoursTime Ago
// Friendly time ago
$postDate = '2024-01-14 10:00:00';
$timeAgo = Date::timeAgo($postDate);
// Result: "1 {LNG_days} {LNG_ago}" or "2 {LNG_hours} {LNG_ago}"
// Example
$posts = [
['title' => 'Post 1', 'created_at' => '2024-01-15 10:00:00'],
['title' => 'Post 2', 'created_at' => '2024-01-14 15:30:00'],
['title' => 'Post 3', 'created_at' => '2024-01-10 08:00:00'],
];
foreach ($posts as $post) {
$timeAgo = Date::timeAgo($post['created_at']);
echo "{$post['title']} - {$timeAgo}\n";
}
// Output:
// Post 1 - 2 {LNG_hours} {LNG_ago}
// Post 2 - 1 {LNG_days} {LNG_ago}
// Post 3 - 1 {LNG_week} {LNG_ago}Real-world Usage
class DateTimeManager
{
public function formatUserBirthday($birthDate)
{
$age = Date::age($birthDate);
$formatted = Date::format($birthDate, 'd F Y');
return [
'formatted_date' => $formatted,
'age_text' => $age,
'age_data' => Date::compare($birthDate, date('Y-m-d'))
];
}
public function generateMonthlyReport($year)
{
$report = [];
for ($month = 1; $month <= 12; $month++) {
$report[] = [
'month_number' => $month,
'month_short' => Date::monthName($month, true),
'month_full' => Date::monthName($month, false),
'days_in_month' => date('t', mktime(0, 0, 0, $month, 1, $year))
];
}
return $report;
}
public function formatPostTimestamps($posts)
{
return array_map(function($post) {
$post['formatted_date'] = Date::format($post['created_at'], 'd/m/Y H:i');
$post['time_ago'] = Date::timeAgo($post['created_at']);
$post['date_components'] = Date::parse($post['created_at']);
return $post;
}, $posts);
}
public function calculateWorkingDays($startDate, $endDate)
{
$diff = Date::compare($startDate, $endDate);
// Calculate working days (exclude weekends)
$totalDays = $diff['days'];
$weeks = floor($totalDays / 7);
$remainingDays = $totalDays % 7;
$workingDays = $weeks * 5; // 5 working days per week
// Check remaining days
$start = new DateTime($startDate);
for ($i = 0; $i < $remainingDays; $i++) {
$dayOfWeek = $start->format('w');
if ($dayOfWeek != 0 && $dayOfWeek != 6) { // Not weekend
$workingDays++;
}
$start->add(new DateInterval('P1D'));
}
return $workingDays;
}
}Best Practices
1. Using ArrayTool
// ✅ Good - Use null coalescing to prevent undefined index
$email = $_POST['email'] ?? '';
// ✅ Good - Use columns() for creating options
$categoryOptions = ArrayTool::columns($categories, 'name', 'id');
// ✅ Good - Use search() instead of complex loops
$activeUsers = ArrayTool::search($users, 'status', 'active');
// ✅ Good - Use sort() for sorting
$sortedProducts = ArrayTool::sort($products, 'price', true);2. Using Text
// ✅ Good - Clean data before display
$title = Text::htmlspecialchars($_POST['title']);
$summary = Text::cut(Text::oneLine($_POST['content']), 100);
// ✅ Good - Use template for messages
$template = "Hello {name}, your balance is {balance} baht";
$message = Text::replace($template, [
'{name}' => $userName,
'{balance}' => Number::format($balance)
]);
// ✅ Good - Validate URL before use
$cleanUrl = Text::url($_POST['website']);
if (!empty($cleanUrl)) {
// Use URL
}3. Using Number
// ✅ Good - Use division() preventing division by zero
$average = Number::division($total, $count);
// ✅ Good - Format numbers before display
$formattedPrice = Number::format($price, ',') . ' Baht';
// ✅ Good - Use printf() for document numbers
$invoiceNo = Number::printf("INV-%Y%m-%05d", $sequence);4. Using Date
// ✅ Good - Use format() for display
$displayDate = Date::format($timestamp, 'd/m/Y');
// ✅ Good - Use timeAgo() for social style
$postTime = Date::timeAgo($post['created_at']);
// ✅ Good - Use compare() for calculation
$workPeriod = Date::compare($hireDate, date('Y-m-d'));
// ✅ Good - Check result from parse()
$dateInfo = Date::parse($dateString);
if ($dateInfo) {
// Use date info
}5. Performance Tips
// ✅ Good - Cache frequent results
class UtilityCache
{
private static $monthNames = [];
public static function getMonthName($month, $short = true)
{
$key = $month . '_' . ($short ? 'short' : 'full');
if (!isset(self::$monthNames[$key])) {
self::$monthNames[$key] = Date::monthName($month, $short);
}
return self::$monthNames[$key];
}
}
// ✅ Good - Use bulk operations
$userNames = ArrayTool::columns($users, 'name', 'id');
$formattedPrices = array_map(function($price) {
return Number::format($price, ',');
}, $prices);
// ✅ Good - Combine filter and sort
$filteredAndSorted = ArrayTool::sort(
ArrayTool::filter($data, $searchTerm),
'name'
);These Utility classes help manage data efficiently and securely. Correct usage reduces code duplication and prevents common errors.