Kotchasan Framework Documentation
Kotchasan\ArrayTool
Kotchasan\ArrayTool
Overview
ArrayTool is a utility class providing efficient tools for manipulating arrays and objects. This class offers functions that are NOT available in native PHP or provide significant convenience over native functions.
When to use:
- Extract data from specific columns in array of arrays/objects
- Search or filter data based on complex conditions
- Insert data at specific positions in arrays
- Handle arrays and objects uniformly
Key Features:
- Works with both arrays and objects
- Helper functions that add value beyond native PHP
- Case-insensitive search and filter support
- Handles nested arrays/objects
[!NOTE]
This documentation reflects the currentArrayToolAPI for PHP 7.4+. The class focuses on helpers that provide value beyond native PHP functions.
API Reference
columns()
Extract values from a specific column from array or object (similar to array_column() but works with both arrays and objects)
Signature:
public static function columns(iterable $source, string $column_key, ?string $index_key = null): arrayParameters:
$source(iterable) - Array, object, or array of objects to extract from$column_key(string) - Name of the column to retrieve$index_key(string|null) - Column to use as index (null = numeric index)
Returns: Array of extracted values
Usage Examples:
use Kotchasan\ArrayTool;
$users = [
['id' => 1, 'name' => 'Alice', 'email' => 'alice@example.com'],
['id' => 2, 'name' => 'Bob', 'email' => 'bob@example.com'],
['id' => 3, 'name' => 'Charlie', 'email' => 'charlie@example.com']
];
// Extract all names (numeric index)
$names = ArrayTool::columns($users, 'name');
// Result: ['Alice', 'Bob', 'Charlie']
// Extract emails with id as index
$emailsById = ArrayTool::columns($users, 'email', 'id');
// Result: [1 => 'alice@example.com', 2 => 'bob@example.com', 3 => 'charlie@example.com']Works with objects:
$users = [
(object)['id' => 1, 'name' => 'Alice'],
(object)['id' => 2, 'name' => 'Bob']
];
$names = ArrayTool::columns($users, 'name', 'id');
// Result: [1 => 'Alice', 2 => 'Bob']extract()
Extract keys and values from nested array recursively
Signature:
public static function extract(array $array, array &$keys, array &$values): voidParameters:
$array(array) - Source array (can be nested)$keys(array) - Reference array to store extracted keys$values(array) - Reference array to store extracted values
Returns: void (modifies $keys and $values by reference)
Usage Examples:
$data = [
'name' => 'John',
'address' => [
'city' => 'Bangkok',
'country' => 'Thailand'
],
'age' => 30
];
$keys = [];
$values = [];
ArrayTool::extract($data, $keys, $values);
// $keys = ['name', 'city', 'country', 'age']
// $values = ['John', 'Bangkok', 'Thailand', 30]filter()
Filter array elements containing search string (case-insensitive)
Signature:
public static function filter(array $array, string $search): arrayParameters:
$array(array) - Array to filter$search(string) - Search string
Returns: Filtered array with matching items (original keys preserved)
Usage Examples:
$items = [
'apple',
'banana',
'orange',
'grape',
'pineapple'
];
$filtered = ArrayTool::filter($items, 'app');
// Result: [0 => 'apple', 4 => 'pineapple'] (contains 'app')
// Empty string returns original array
$all = ArrayTool::filter($items, '');
// Result: ['apple', 'banana', 'orange', 'grape', 'pineapple']Works with nested arrays:
$products = [
['name' => 'Laptop', 'brand' => 'Dell'],
['name' => 'Mouse', 'brand' => 'Logitech'],
['name' => 'Keyboard', 'brand' => 'Dell']
];
// Search for Dell products
$dellProducts = ArrayTool::filter($products, 'Dell');
// Result: [0 => ['name' => 'Laptop', 'brand' => 'Dell'],
// 2 => ['name' => 'Keyboard', 'brand' => 'Dell']]getFirstKey()
Get the first key of an array or object
Signature:
public static function getFirstKey($source)Parameters:
$source(array|object) - Array or object
Returns: string|int|null - First key, or null if no keys found
Usage Examples:
$array = ['name' => 'John', 'age' => 30, 'city' => 'Bangkok'];
$firstKey = ArrayTool::getFirstKey($array);
// Result: 'name'
$object = (object)['id' => 1, 'title' => 'Test'];
$firstKey = ArrayTool::getFirstKey($object);
// Result: 'id'
$empty = [];
$firstKey = ArrayTool::getFirstKey($empty);
// Result: null[!NOTE]
For arrays in PHP 7.3+, you can usearray_key_first()instead. This method is kept for object support.
insertAfter()
Insert data into an array after the specified key
Signature:
public static function insertAfter(array $source, $find, $key, $value): arrayParameters:
$source(array) - Array to insert into$find(string|int) - Key to search for$key(string|int) - Key for the new data$value(mixed) - Data to insert
Returns: Modified array (if key not found, inserts at the end)
Usage Examples:
$menu = [
'home' => 'Home',
'about' => 'About',
'contact' => 'Contact'
];
// Insert after 'home'
$menu = ArrayTool::insertAfter($menu, 'home', 'services', 'Services');
// Result: [
// 'home' => 'Home',
// 'services' => 'Services',
// 'about' => 'About',
// 'contact' => 'Contact'
// ]
// If key not found, inserts at end
$menu = ArrayTool::insertAfter($menu, 'notfound', 'blog', 'Blog');
// 'blog' will be added at the endinsertBefore()
Insert data into an array before the specified key
Signature:
public static function insertBefore(array $source, $find, $key, $value): arrayParameters:
$source(array) - Array to insert into$find(string|int) - Key to search for$key(string|int) - Key for the new data$value(mixed) - Data to insert
Returns: Modified array (if key not found, inserts at the end)
Usage Examples:
$menu = [
'home' => 'Home',
'contact' => 'Contact'
];
// Insert before 'contact'
$menu = ArrayTool::insertBefore($menu, 'contact', 'about', 'About');
// Result: [
// 'home' => 'Home',
// 'about' => 'About',
// 'contact' => 'Contact'
// ]replace()
Replace/merge values from $replace into $source (works with arrays and objects)
Signature:
public static function replace($source, $replace)Parameters:
$source(array|object) - Base array or object$replace(array|object) - Values to merge/replace
Returns: array|object - Modified source
Usage Examples:
// With arrays
$data = ['a' => 1, 'b' => 2, 'c' => 3];
$replacements = ['b' => 5, 'd' => 4];
$result = ArrayTool::replace($data, $replacements);
// Result: ['a' => 1, 'b' => 5, 'c' => 3, 'd' => 4]
// With objects
$config = (object)['host' => 'localhost', 'port' => 3306];
$updates = ['port' => 3307, 'database' => 'test'];
$config = ArrayTool::replace($config, $updates);
// Result: object with host='localhost', port=3307, database='test'search()
Search array of arrays/objects for items with matching key-value pair
Signature:
public static function search(array $array, string $key, $search): arrayParameters:
$array(array) - Array of arrays or objects to search$key(string) - Key/property to match$search(mixed) - Value to match (strict comparison)
Returns: Array of matching items with original keys preserved
Usage Examples:
$users = [
['id' => 1, 'name' => 'Alice', 'status' => 'active'],
['id' => 2, 'name' => 'Bob', 'status' => 'inactive'],
['id' => 3, 'name' => 'Charlie', 'status' => 'active'],
['id' => 4, 'name' => 'Diana', 'status' => 'pending']
];
// Find active users
$activeUsers = ArrayTool::search($users, 'status', 'active');
// Result: [
// 0 => ['id' => 1, 'name' => 'Alice', 'status' => 'active'],
// 2 => ['id' => 3, 'name' => 'Charlie', 'status' => 'active']
// ]
// Search with boolean
$items = [
['name' => 'Item 1', 'featured' => true],
['name' => 'Item 2', 'featured' => false],
['name' => 'Item 3', 'featured' => true]
];
$featured = ArrayTool::search($items, 'featured', true);
// Result: [0 => ..., 2 => ...]Works with objects:
$users = [
(object)['id' => 1, 'role' => 'admin'],
(object)['id' => 2, 'role' => 'user']
];
$admins = ArrayTool::search($users, 'role', 'admin');
// Result: [0 => object(['id' => 1, 'role' => 'admin'])]shift()
Remove elements from an array before the specified key (keeps elements from the specified key onwards)
Signature:
public static function shift(array $source, $key): arrayParameters:
$source(array) - The source array$key(string|int) - The key to start keeping elements from
Returns: Array of elements from the specified key onwards
Usage Examples:
$data = [
'step1' => 'Start',
'step2' => 'Process',
'step3' => 'Complete',
'step4' => 'Close'
];
$remaining = ArrayTool::shift($data, 'step2');
// Result: [
// 'step2' => 'Process',
// 'step3' => 'Complete',
// 'step4' => 'Close'
// ]
// With numeric array
$numbers = [10, 20, 30, 40, 50];
$fromIndex2 = ArrayTool::shift($numbers, 2);
// Result: [2 => 30, 3 => 40, 4 => 50]sort()
Sort array of associative arrays/objects by a specified key
Signature:
public static function sort(array $array, string $sort_key = 'id', bool $descending = false): arrayParameters:
$array(array) - Array to sort$sort_key(string) - Key to sort by (default: 'id')$descending(bool) - Sort descending (default: false = ascending)
Returns: Sorted array (re-indexed numerically)
Usage Examples:
$products = [
['id' => 3, 'name' => 'Product C', 'price' => 100],
['id' => 1, 'name' => 'Product A', 'price' => 300],
['id' => 2, 'name' => 'Product B', 'price' => 200]
];
// Sort by id (ascending)
$sortedById = ArrayTool::sort($products, 'id');
// Result: [
// 0 => ['id' => 1, 'name' => 'Product A', 'price' => 300],
// 1 => ['id' => 2, 'name' => 'Product B', 'price' => 200],
// 2 => ['id' => 3, 'name' => 'Product C', 'price' => 100]
// ]
// Sort by price (descending)
$sortedByPrice = ArrayTool::sort($products, 'price', true);
// Result: [
// 0 => ['id' => 1, 'name' => 'Product A', 'price' => 300],
// 1 => ['id' => 2, 'name' => 'Product B', 'price' => 200],
// 2 => ['id' => 3, 'name' => 'Product C', 'price' => 100]
// ]
// Sort by name (alphabetically)
$sortedByName = ArrayTool::sort($products, 'name');
// Result: sorted alphabetically (case-insensitive)[!NOTE]
Sorting uses case-insensitive comparison and the returned array will be re-indexed numerically (0, 1, 2...)
toString()
Convert nested array/object to string by joining values recursively
Signature:
public static function toString(string $glue, $source): stringParameters:
$glue(string) - Separator between values$source(mixed) - Array, object, or scalar value
Returns: string - Concatenated string
Usage Examples:
// Simple array
$data = ['apple', 'banana', 'orange'];
$result = ArrayTool::toString(', ', $data);
// Result: "apple, banana, orange"
// Nested array
$nested = [
'user' => [
'name' => 'John',
'details' => ['age' => 30, 'city' => 'Bangkok']
]
];
$result = ArrayTool::toString(' | ', $nested);
// Result: "John | 30 | Bangkok"
// With scalar value
$value = ArrayTool::toString(',', 'test');
// Result: "test"unserialize()
Unserialize a string and update the source array with the unserialized data (supports both JSON and PHP serialize)
Signature:
public static function unserialize($str, $source = [], $replace = true)Parameters:
$str(string) - The serialized string$source(array) - Array to update with unserialized data (optional, default: [])$replace(bool) - Whether to replace existing values in the source array (optional, default: true)
Returns: array - The updated source array
Usage Examples:
// JSON string
$json = '{"name":"John","age":30,"city":"Bangkok"}';
$data = ArrayTool::unserialize($json);
// Result: ['name' => 'John', 'age' => 30, 'city' => 'Bangkok']
// PHP serialized string
$serialized = serialize(['id' => 1, 'status' => 'active']);
$data = ArrayTool::unserialize($serialized);
// Result: ['id' => 1, 'status' => 'active']
// Merge with existing array
$existing = ['host' => 'localhost', 'port' => 3306];
$updates = '{"port":3307,"database":"mydb"}';
$config = ArrayTool::unserialize($updates, $existing);
// Result: ['host' => 'localhost', 'port' => 3307, 'database' => 'mydb']
// Don't replace existing values (replace = false)
$existing = ['name' => 'Alice', 'age' => 25];
$updates = '{"name":"Bob","city":"Bangkok"}';
$data = ArrayTool::unserialize($updates, $existing, false);
// Result: ['name' => 'Alice', 'age' => 25, 'city' => 'Bangkok']
// (name not replaced because it already exists)[!NOTE]
This function tries to decode as JSON first, if that fails it usesunserialize()instead.
inArrayAny()
Check if any value in the needle array exists in the haystack array
Signature:
public static function inArrayAny(array $needles, array $haystack, bool $strict = false): boolParameters:
$needles(array) - Array of values to search for$haystack(array) - Array to search in$strict(bool) - Perform strict type comparison (default: false)
Returns: bool - True if any value is found
Usage Examples:
$permissions = ['read', 'write', 'delete'];
$userPermissions = ['read', 'execute'];
// Check if user has any required permission
$hasAny = ArrayTool::inArrayAny($permissions, $userPermissions);
// Result: true (has 'read')
$adminPermissions = ['create', 'delete'];
$hasAny = ArrayTool::inArrayAny($adminPermissions, $userPermissions);
// Result: false (has none)
// Use strict comparison
$numbers = ['1', '2', '3'];
$check = [1, 4];
$found = ArrayTool::inArrayAny($check, $numbers, false);
// Result: true ('1' == 1 is true)
$found = ArrayTool::inArrayAny($check, $numbers, true);
// Result: false ('1' === 1 is false)[!NOTE]
Unlike nativein_array()which accepts a single value, this accepts an array of values and returns true if ANY of them is found.
getNextKey()
Retrieve the key of the element following a specified key in the array
Signature:
public static function getNextKey(array $array, $key)Parameters:
$array(array) - The input array$key(string|int) - The key to find the next key after
Returns: string|int|null - The next key, or null if not found or at end
Usage Examples:
$menu = [
'home' => 'Home',
'about' => 'About',
'services' => 'Services',
'contact' => 'Contact'
];
$nextKey = ArrayTool::getNextKey($menu, 'about');
// Result: 'services'
$nextKey = ArrayTool::getNextKey($menu, 'contact');
// Result: null (at the end of array)
$nextKey = ArrayTool::getNextKey($menu, 'notfound');
// Result: null (key doesn't exist)
// With numeric array
$numbers = [10, 20, 30, 40];
$nextKey = ArrayTool::getNextKey($numbers, 1);
// Result: 2Real-World Examples
1. User Data Management
use Kotchasan\ArrayTool;
class UserManager
{
private $users = [
['id' => 1, 'name' => 'Alice', 'role' => 'admin', 'active' => true],
['id' => 2, 'name' => 'Bob', 'role' => 'user', 'active' => true],
['id' => 3, 'name' => 'Charlie', 'role' => 'user', 'active' => false],
['id' => 4, 'name' => 'Diana', 'role' => 'moderator', 'active' => true]
];
/**
* Get active users
*/
public function getActiveUsers()
{
return ArrayTool::search($this->users, 'active', true);
}
/**
* Get all administrators
*/
public function getAdmins()
{
return ArrayTool::search($this->users, 'role', 'admin');
}
/**
* Get user names (indexed by ID)
*/
public function getUserNamesById()
{
return ArrayTool::columns($this->users, 'name', 'id');
// Result: [1 => 'Alice', 2 => 'Bob', 3 => 'Charlie', 4 => 'Diana']
}
/**
* Search users by term
*/
public function searchUsers($term)
{
return ArrayTool::filter($this->users, $term);
}
/**
* Sort users by name
*/
public function sortByName($descending = false)
{
return ArrayTool::sort($this->users, 'name', $descending);
}
}
// Usage
$manager = new UserManager();
$active = $manager->getActiveUsers();
$admins = $manager->getAdmins();
$nameMap = $manager->getUserNamesById();
$searchResults = $manager->searchUsers('ali'); // case-insensitive search2. Menu Building and Management
class MenuBuilder
{
private $menu = [];
public function __construct()
{
$this->menu = [
'home' => ['title' => 'Home', 'url' => '/', 'icon' => 'home'],
'contact' => ['title' => 'Contact', 'url' => '/contact', 'icon' => 'mail']
];
}
/**
* Add menu after home
*/
public function addMenuAfterHome($key, $title, $url, $icon)
{
$item = ['title' => $title, 'url' => $url, 'icon' => $icon];
$this->menu = ArrayTool::insertAfter($this->menu, 'home', $key, $item);
return $this;
}
/**
* Add menu before contact
*/
public function addMenuBeforeContact($key, $title, $url, $icon)
{
$item = ['title' => $title, 'url' => $url, 'icon' => $icon];
$this->menu = ArrayTool::insertBefore($this->menu, 'contact', $key, $item);
return $this;
}
/**
* Update menus
*/
public function updateMenu($updates)
{
foreach ($updates as $key => $data) {
if (isset($this->menu[$key])) {
$this->menu[$key] = ArrayTool::replace($this->menu[$key], $data);
}
}
return $this;
}
/**
* Get all menu titles
*/
public function getMenuTitles()
{
return ArrayTool::columns($this->menu, 'title');
}
public function getMenu()
{
return $this->menu;
}
}
// Usage
$builder = new MenuBuilder();
$builder
->addMenuAfterHome('about', 'About', '/about', 'info')
->addMenuBeforeContact('services', 'Services', '/services', 'briefcase')
->updateMenu([
'home' => ['icon' => 'house'] // change icon
]);
$menu = $builder->getMenu();
// Result: [
// 'home' => ['title' => 'Home', 'url' => '/', 'icon' => 'house'],
// 'about' => ['title' => 'About', 'url' => '/about', 'icon' => 'info'],
// 'services' => ['title' => 'Services', 'url' => '/services', 'icon' => 'briefcase'],
// 'contact' => ['title' => 'Contact', 'url' => '/contact', 'icon' => 'mail']
// ]3. API Data Processing
class ApiDataProcessor
{
/**
* Process API response
*/
public function processUserResponse($apiResponse)
{
$users = $apiResponse['data']['users'] ?? [];
// Extract needed data
$emails = ArrayTool::columns($users, 'email');
$userMap = ArrayTool::columns($users, 'name', 'id');
// Filter verified users
$verified = ArrayTool::search($users, 'verified', true);
// Sort by creation date
$sorted = ArrayTool::sort($verified, 'created_at', true);
return [
'emails' => $emails,
'user_map' => $userMap,
'verified_users' => $verified,
'latest_users' => $sorted
];
}
/**
* Merge data from multiple sources
*/
public function mergeUserData($users, $profiles)
{
foreach ($users as &$user) {
$userId = $user['id'];
$profileData = ArrayTool::search($profiles, 'user_id', $userId);
if (!empty($profileData)) {
$user = ArrayTool::replace($user, $profileData[0]);
}
}
return $users;
}
/**
* Sanitize data (remove sensitive fields)
*/
public function sanitize($data)
{
$sensitiveFields = ['password', 'token', 'secret_key'];
foreach ($data as &$item) {
$item = array_diff_key($item, array_flip($sensitiveFields));
}
return $data;
}
}4. Permission Management
class PermissionChecker
{
/**
* Check if user has any required permission
*/
public function hasAnyPermission($userPermissions, $requiredPermissions)
{
return ArrayTool::inArrayAny($requiredPermissions, $userPermissions);
}
/**
* Check if user has allowed role
*/
public function hasRole($userRoles, $allowedRoles)
{
return ArrayTool::inArrayAny($allowedRoles, $userRoles);
}
}
// Usage
$checker = new PermissionChecker();
$userPerms = ['post:read', 'post:create', 'comment:read'];
$requiredPerms = ['post:delete', 'post:update'];
$canEdit = $checker->hasAnyPermission($userPerms, $requiredPerms);
// Result: false (no required permissions)
$userRoles = ['editor', 'author'];
$adminRoles = ['admin', 'super-admin'];
$isAdmin = $checker->hasRole($userRoles, $adminRoles);
// Result: falseBest Practices
1. Validate Data Before Usage
// ✅ Good
public function safeColumnExtraction($data, $column)
{
if (!is_array($data) || empty($data)) {
return [];
}
// Check if column exists
$firstItem = reset($data);
if (!isset($firstItem[$column])) {
throw new InvalidArgumentException("Column '{$column}' not found");
}
return ArrayTool::columns($data, $column);
}
// ❌ Bad - no validation
public function unsafeExtraction($data, $column)
{
return ArrayTool::columns($data, $column); // might error
}2. Use Native PHP Functions When Appropriate
// For safe array access
// ✅ Use null coalescing operator
$value = $config['key'] ?? 'default';
// For removing keys
// ✅ Use native functions
$result = array_diff_key($array, array_flip(['key1', 'key2']));
// For getting first key (PHP 7.3+)
// ✅ Use native function for arrays
$firstKey = array_key_first($array);
// But if you need to support objects, use ArrayTool::getFirstKey()
$firstKey = ArrayTool::getFirstKey($object);3. Performance for Large Datasets
// For large datasets (> 1000 items)
// Consider using array_filter or foreach instead
public function efficientSearch($data, $key, $value)
{
if (count($data) > 1000) {
return array_filter($data, function($item) use ($key, $value) {
return isset($item[$key]) && $item[$key] === $value;
});
}
return ArrayTool::search($data, $key, $value);
}4. Type Safety
// ✅ Specify types clearly
public function typeSafeReplace($source, array $replacements)
{
if (!is_array($source) && !is_object($source)) {
throw new InvalidArgumentException('Source must be array or object');
}
return ArrayTool::replace($source, $replacements);
}Important Considerations
[!WARNING]
sort()re-indexes the array
Thesort()function returns a numerically re-indexed array (0, 1, 2...) instead of preserving original keys
// Original keys will be lost
$data = [
'user1' => ['name' => 'Bob'],
'user2' => ['name' => 'Alice']
];
$sorted = ArrayTool::sort($data, 'name');
// Result: [
// 0 => ['name' => 'Alice'],
// 1 => ['name' => 'Bob']
// ]
// NOT ['user2' => ..., 'user1' => ...][!WARNING]
search()uses strict comparison
Thesearch()function uses===for comparison, be aware of types
$users = [['id' => 1], ['id' => '1']];
ArrayTool::search($users, 'id', 1); // only matches ['id' => 1]
ArrayTool::search($users, 'id', '1'); // only matches ['id' => '1'][!CAUTION]
Memory usage with nested arrays
UsingtoString()with large nested arrays may consume significant memory
Related Classes
- Collection - For collection management
- Database - For query results
- Text - For string manipulation